Почему это строка андроида.формат собаки медленный?


Я реализовывал действие ListView с пользовательскими рисованными представлениями и имел следующий код:

@Override
public void onDraw(Canvas canvas)
{
    super.onDraw(canvas);
    ....
    canvas.drawText(String.format("%02d: %dx%d", position, w, h),
        10, 15, cached_paint);
}

Почти ничего другого в методе onDraw, так что это сводило меня с ума, почему прокрутка была настолько плохой. Случайно я изменил параметр drawText, чтобы не использовать строку.формат и вдруг прокрутка снова стала масляным шелком. На самом деле, следующее Практически то же самое, но работает хорошо:

canvas.drawText("" + position + ": " + w + "x" + h,
    10, 15, cached_paint);

Я ошеломлен. Почему последнее быстрее, чем вызов строки.формат? Я можно было бы ожидать, что конкатенация объектов создаст больше промежуточных объектов и в целом улучшит производительность, но я обнаружил прямо противоположное. На самом деле, когда работает со строкой.формат я получал много сообщений о выделении/освобождении от виртуальной машины.

Так почему же строка?формат настолько медленный, когда он, по-видимому, может быть быстрее (по крайней мере, когда он приходит из других языков программирования, где создание объектов дорого)?

3   5  

3 ответа:

Конкатенация строк с + не создает много промежуточных объектов; в основном это StringBuffer и его внутренний символьный массив (который может быть перераспределен, если у него закончится емкость). О, и Струна, когда она соединяется.

Также, с помощью +, большая часть работы по анализу типов данных объектов, входящих в строку, выполняется во время компиляции. с шнуром.форматирование, которое выполняется во время выполнения. В довершение всего, каждый примитивный тип, который вы передаете в String.формат должен быть автобоксинг, который генерирует намного больше объектов.

Я ошеломлен.

Почему?

Почему последнее быстрее, чем вызов строки.формат?

Потому что она написана на Java. %02d: %dx%d - это не Java. Это должно быть проанализировано каждый раз, и правила выполняются каждый раз. Эти правила выполняются в Java, через java.util.Formatter.

Теперь String.format() можно было бы оптимизировать, заменив его реализацией собственного кода (C/C++), но я думаю, что varargs и JNI становятся грязными.

Я бы ожидал, что объект конкатенация генерирует больше промежуточных объектов и в целом ухудшает производительность, но я обнаружил прямо противоположное. На самом деле, когда работает со строкой.формат я получал много сообщений о выделении/освобождении от виртуальной машины.

Это было бы потому, что String.format() довольно сложен и реализован в Java.

Чтобы получить максимальную производительность, вы можете создать форматер из пакета java.text и кэшировать его.

final static DecimalFormat myFormat = new DecimalFormat("###");
@Override
public void onDraw(Canvas canvas)
{
    super.onDraw(canvas);
    ....
    canvas.drawText(myFormat.format(w) + "x" + myFormat(h));
}

Для еще большей производительности можно использовать более быструю конкатенацию строк. Но это другой вид оптимизации, и не по теме для этого вопроса.