Ищите G C дружественный способ частой замены подстроки - PullRequest
1 голос
/ 02 февраля 2020

Моя цель - просто заменить подстроку, но очень часто. Программа работает в Android.

Например, у меня есть строка = {a} is a good {b}. с картой = {{a}=Bob, {b}=boy}, и результат должен быть Bob is a good boy. Мне нужно иметь дело с такой заменой для другой строки до 400 раз в секунду, поскольку значение карты будет обновляться в реальном времени.

Однако я использую дерево tr ie и автомат Aho-Corasick для высокой производительности, вот фрагмент ядра:

    val builder: StringBuilder

    private fun replace(str: String): String {
        if (!getFail) {
            getFail()
        }
        var p = 1
        builder.setLength(0)
        for (c in str) {
            builder.append(c)
            if (c.toInt() !in 0..126) {
                continue // ignore non-ascii char
            }
            var k = trie[p][c.toInt()]
            while (k > 1) {
                // find a tag
                if (end[k] != 0) {
                    val last = builder.length - end[k]
                    // replace the tag
                    values[builder.sub(last, end[k])]?.let {
                        builder.replace1(last, end[k], it)
                    }
                    p = 0
                    break
                }
                k = fail[k] // not find
            }
            p = trie[p][c.toInt()]
        }
        return builder.toString()
    }

Как видите, я использовал StringBuilder для повторного использования памяти, но в конце концов мне нужно вызвать StringBuilder.toString(), чтобы вернуть результат, и эта операция создала новый строковый объект. Между тем жизненный цикл результата очень короткий, и функция замены вызывается очень часто. В результате JVM будет часто G C.

Любой способ повторно использовать память, занятую строкой результата с коротким сроком службы? Или просто другое решение.

1 Ответ

5 голосов
/ 02 февраля 2020

Есть ли способ повторно использовать память, занятую строкой результата с коротким сроком службы?

Нет.

Или просто другое решение.

Если вы могли бы изменить код, который использует String объекты, сгенерированные этим методом, принимают вместо него CharSequence. Тогда вы могли бы передать ему экземпляр StringBuilder в builder и избежать вызова toString().

Проблема в том, что вы не смогли бы помешать чему-либо привести CharSequence к StringBuilder и мутируя. (Но если код не критичен для безопасности, вы можете это проигнорировать. Это будет трудно сделать случайно, особенно если вы используете тип интерфейса CharSequence при передаче StringBuilder.)

Другая проблема заключается в том, что вызывающая сторона будет каждый раз получать один и тот же объект с различным состоянием. Он не сможет сохранить состояние ... если только он не вызовет toString().


Но вы можете излишне беспокоиться о производительности. G C относительно хорош в работе с недолговечными объектами. Предполагая, что объект недоступен в первом цикле G C после его создания, он никогда не будет помечен или скопирован, а стоимость его удаления будет равна нулю. В первом приближении, это достижимые объекты в «от» пространстве, которые будут вам стоить.

Я бы сначала провел некоторое профилирование и мониторинг G C. Только go по пути изменения кода, как указано выше, если есть явное свидетельство того, что короткоживущие строки вызывают проблемы с производительностью.

(Моя интуиция заключается в том, что 400 краткосрочных строк в секунду не должно быть проблем, если предположить, что 1) они невелики и 2) вы выбрали G C, который подходит для вашего варианта использования.)

...