Как я могу исправить эту проблему с памятью в моем алгоритме максимального соответствия с RealmSwift?

Я написал свою собственную функцию максимального соответствия в Swift, чтобы разделить китайские предложения на слова. Он работает нормально, за исключением того, что при аномально длинных предложениях использование памяти превышает 1 ГБ. Мне нужна помощь, чтобы выяснить, как изменить мой код, чтобы не было этой проблемы с памятью. Я не уверен, связано ли это с тем, как я использую RealmSwift, или с моим алгоритмом в целом.

Вот мой код:

    func splitSentenceIntoWordsWithDictionaryMaximumMatching(string: String) -> [String] {
    var string = string
    var foundWordsArray: [String] = []
    var position = count(string)

    while position > 0
    {
        var index = advance(string.startIndex, position)
        let partialString = string.substringToIndex(index)
        if let found = Realm().objects(Word).filter("simplified == '\(partialString)'").first
        {
            foundWordsArray.append(partialString)
            position = position - 1
            var partialStringCount = count(partialString)
            while partialStringCount > 0
            {
                string = dropFirst(string)
                partialStringCount -= 1
            }
            position = count(string)
            index = advance(string.startIndex, position)
        }
        else if count(partialString) == 1
        {
            addNewEntryToDictionaryInTransaction(partialString, "", [partialString], partialString)
            foundWordsArray.append(partialString)
            var partialStringCount = count(partialString)
            while partialStringCount > 0
            {
                string = dropFirst(string)
                partialStringCount -= 1
            }
            position = count(string)
            index = advance(string.startIndex, position)
        }
        else
        {
            position = position - 1
            index = advance(string.startIndex, position)
        }
    }

    return foundWordsArray
}

person webmagnets    schedule 24.05.2015    source источник


Ответы (1)


В этом случае следует использовать autoreleasepool (см.: Использовать блоки локального пула автоматического освобождения для уменьшения пикового объема памяти) в цикле:

    while position > 0 {
        autoreleasepool {
            var index = advance(string.startIndex, position)
            ...
        }
    }

Кстати, в вашем коде слишком много операций копирования памяти и O(N) операций над String.Index (count() и advance()), что может привести к серьезным проблемам с производительностью. Вместо этого вы должны эффективно использовать String.Index примерно так:

import Foundation

var  words:Set = ["今日", "献立", "魚", "味噌汁", "定食", "焼き魚", "です"]
func splitSentenceIntoWordsWithDictionaryMaximumMatching(string: String) -> [String] {
    var foundWordsArray: [String] = []
    var start = string.startIndex
    var end = string.endIndex

    while start != end {
        autoreleasepool { // In this case(using builtin `Set`), I think we don't need `autoreleasepool` here. But this is just a demo :)
            let partialString = string[start ..< end]
            if !words.contains(partialString) {
                if end.predecessor() != start { // faster way of `count(partialString) == 1`
                    end = end.predecessor()
                    return // we cannot use `continue` here because we are in `autoreleasepool` closure
                }
                words.insert(partialString)
            }
            foundWordsArray.append(partialString)
            start = end
            end = string.endIndex
        }
    }

    return foundWordsArray
}

var str = "今日の献立は焼き魚定食と味噌汁です"
let result = splitSentenceIntoWordsWithDictionaryMaximumMatching(str)

debugPrintln(result) // ["今日", "の", "献立", "は", "焼き魚", "定食", "と", "味噌汁", "です"]
debugPrintln(words) // Set(["献立", "焼き魚", "今日", "魚", "味噌汁", "定食", "は", "と", "です", "の"])

Я использовал встроенный Set, но я думаю, что вы можете легко адаптировать свои коды Realm здесь :)


ДОБАВЛЕНО: в ответ на комментарии:

обратная версия:

var  words:Set = ["研究", "研究生", "生命", "起源"]
func splitSentenceIntoWordsWithDictionaryMaximumMatchingReversed(string: String) -> [String] {
    var foundWordsArray: [String] = []
    var start = string.startIndex
    var end = string.endIndex

    while start != end {
        autoreleasepool {
            let partialString = string[start ..< end]
            if !words.contains(partialString) {
                if start.successor() != end {
                    start = start.successor()
                    return
                }
                words.insert(partialString)
            }
            foundWordsArray.append(partialString)
            end = start
            start = string.startIndex
        }
    }

    return foundWordsArray.reverse()
}

var str = "研究生命起源"
let result = splitSentenceIntoWordsWithDictionaryMaximumMatchingReversed(str)

debugPrintln(result) // ["研究", "生命", "起源"]
person rintaro    schedule 25.05.2015
comment
Спасибо, это сработало. С кодом Realm вам все еще нужен autoreleasepool. - person webmagnets; 25.05.2015
comment
Не могли бы вы подсказать, как это сделать в обратном порядке? В дополнение к тому, как это делает эта функция, мне нужна еще одна функция, которая соответствует задней части строки, а не передней. Часть, с которой у меня больше всего проблем, это let partialString = string[start ..< end]. Должно быть что-то вроде let partialString = string[end ..< start]? (конечно, это не работает)` fatal error: Can't form Range with end < start - person webmagnets; 25.05.2015
comment
Я могу наградить вас наградой, если вы поможете мне сделать то же самое в обратном порядке. - person webmagnets; 26.05.2015
comment
Я не уверен, что реверс вы имеете в виду. Когда у вас есть слова "ABC", "CDE", "EDC", "CBA" и строка "ABCDE", что вы ожидаете в результате? Кстати, мне не нужна награда или представители для этого :) - person rintaro; 27.05.2015
comment
Я имею в виду, что если текст 研究生命起源, то вперед будет возвращено [研究生,命,起源], потому что 研究生 — самое длинное слово в начале. Обратное вернет [研究,生命,起源]. потому что он начнет с конца и найдет 生命, прежде чем, наконец, найдет 研究. - person webmagnets; 27.05.2015
comment
Обе функции будут возвращать корректные результаты, потому что в словаре есть как 研究生, так и 研究, а также 生命 и 命. - person webmagnets; 27.05.2015
comment
Спасибо. Баунти за 23 часа. - person webmagnets; 27.05.2015