Подтвердить что ты не робот

Почему добавление if-statement внутри этого метода значительно замедляет его?

Я столкнулся с этим в ответом на другой вопрос. Я пытался диагностировать, какое изменение кода оказало большее влияние на скорость. Я использовал логический флаг в цикле for, чтобы переключаться между использованием вспомогательных методов для создания Color.

Интересное поведение заключается в том, что когда я решил, какой из них был быстрее и удален, если скорость кода усиливается 10 раз. Принимая 140 мс до и только через 13 мс. Я должен только удалить один расчет из примерно 7 из цикла. Почему такое резкое увеличение скорости?

Медленный код: (работает в 141 миллисекундах, когда helperMethods является ложным) * См. edit 2

public static void applyAlphaGetPixels(Bitmap b, Bitmap bAlpha, boolean helperMethods) {
    int w = b.getWidth();
    int h = b.getHeight();
    int[] colorPixels = new int[w*h];
    int[] alphaPixels = new int[w*h];
    b.getPixels(colorPixels, 0, w, 0, 0, w, h);
    bAlpha.getPixels(alphaPixels, 0, w, 0, 0, w, h);
    for(int j = 0; j < colorPixels.length;j++){
        if(helperMethods){
            colorPixels[j] = Color.argb(Color.alpha(alphaPixels[j]), Color.red(colorPixels[j]), Color.green(colorPixels[j]), Color.blue(colorPixels[j]));
        } else colorPixels[j] = alphaPixels[j] | (0x00FFFFFF & colorPixels[j]);
    }
    b.setPixels(colorPixels, 0, w, 0, 0, w, h);
}

Быстрый код: (работает в 13 мс)

public static void applyAlphaGetPixels(Bitmap b, Bitmap bAlpha) {
    int w = b.getWidth();
    int h = b.getHeight();
    int[] colorPixels = new int[w*h];
    int[] alphaPixels = new int[w*h];
    b.getPixels(colorPixels, 0, w, 0, 0, w, h);
    bAlpha.getPixels(alphaPixels, 0, w, 0, 0, w, h);
    for(int j = 0; j < colorPixels.length;j++){
        colorPixels[j] = alphaPixels[j] | (0x00FFFFFF & colorPixels[j]);
    }
    b.setPixels(colorPixels, 0, w, 0, 0, w, h);
}

EDIT: Кажется, проблема не в том, что if находится внутри цикла. Если я подниму if вне цикла. Код работает немного быстрее, но все еще на медленных скоростях с 131ms:

public static void applyAlphaGetPixels(Bitmap b, Bitmap bAlpha, boolean helperMethods) {
    int w = b.getWidth();
    int h = b.getHeight();
    int[] colorPixels = new int[w*h];
    int[] alphaPixels = new int[w*h];
    b.getPixels(colorPixels, 0, w, 0, 0, w, h);
    bAlpha.getPixels(alphaPixels, 0, w, 0, 0, w, h);
    if (helperMethods) {
        for (int j = 0; j < colorPixels.length;j++) {
            colorPixels[j] = Color.argb(Color.alpha(alphaPixels[j]),
                                        Color.red(colorPixels[j]),
                                        Color.green(colorPixels[j]),
                                        Color.blue(colorPixels[j]));
        }
    } else {
        for (int j = 0; j < colorPixels.length;j++) {
             colorPixels[j] = alphaPixels[j] | (0x00FFFFFF & colorPixels[j]);
        }
    }

    b.setPixels(colorPixels, 0, w, 0, 0, w, h);
}

EDIT 2: Я тупой. На самом деле очень глупый. Ранее в стеке вызовов я использовал другой логический флаг для переключения между использованием этого метода и с использованием другого метода, который использует getPixel вместо getPixels. У меня был неправильный флаг для всех моих вызовов, у которых есть параметр helperMethod. Когда я сделал новые звонки на версию без helperMethod, я сделал это правильно. Повышение производительности происходит из-за getPixels не оператора if.

Фактический медленный код:

public static void applyAlphaGetPixel(Bitmap b, Bitmap bAlpha, boolean helperMethods) {
    int w = b.getWidth();
    int h = b.getHeight();
    for(int y=0; y < h; ++y) {
        for(int x=0; x < w; ++x) {
            int pixel = b.getPixel(x,y);
            int finalPixel;
            if(helperMethods){
                finalPixel = Color.argb(Color.alpha(bAlpha.getPixel(x,y)), Color.red(pixel), Color.green(pixel), Color.blue(pixel));
            } else{
                finalPixel = bAlpha.getPixel(x,y) | (0x00FFFFFF & pixel);
            }
            b.setPixel(x,y,finalPixel);
        }
    }
}

Примечание. Все скорости в среднем составляют 100 прогонов.

4b9b3361

Ответ 1

Это, вероятно, ваш код профилирования, который вас сбивает с толку. Попытайтесь изолировать часть кода, который вы хотите профилировать, и просто измеряете эту часть, избегайте операций GCable, таких как создание растровых изображений в ваших случаях.

Если я вызываю ваш тестовый код с помощью

testing.loadDrawable(this, false, true, false)

он работает медленно. Но если я назову его

testing.loadDrawable(this, true, true, false)

Это похожее (еще хуже) число. Таким образом, использование GetPixels делает всю разницу. Я думаю, что данные Bitmap попадают в локальный буфер и устанавливают результаты позже.

Ответ 2

Попробуйте вытащить условие из цикла:

if (helperMethods) {
    for (int j = 0; j < colorPixels.length;j++) {
        colorPixels[j] = Color.argb(Color.alpha(alphaPixels[j]),
                                    Color.red(colorPixels[j]),
                                    Color.green(colorPixels[j]),
                                    Color.blue(colorPixels[j]));
    }
} else {
    for (int j = 0; j < colorPixels.length;j++) {
         colorPixels[j] = alphaPixels[j] | (0x00FFFFFF & colorPixels[j]);
    }
}

Ответ 3

В "быстром коде" вы никогда не запускаете оператор

colorPixels[j] = Color.argb(Color.alpha(alphaPixels[j]), Color.red(colorPixels[j]), Color.green(colorPixels[j]), Color.blue(colorPixels[j])); 

Но в "медленном коде", если логическое значение имеет значение true, по крайней мере, как только вы запустите это вспомогательное утверждение, которое делает время дольше. Если ваше условие всегда ложно, оператор if проверяется примерно 7 раз на каждой итерации через цикл. Попробуйте поместить if вне цикла.

Ответ 4

В вашем быстром коде вы вообще не используете класс Color. Я предполагаю, что инициализация этого класса занимает некоторое время, у него много статического метода и статического кода.

Вы можете попробовать сделать следующее: убедитесь, что класс цвета полностью загружен и инициализирован перед выполнением теста (вы можете вызвать любой статический метод из класса Color перед вызовом метода applyAlphaGetPixels()). Затем запустите свой тест и сравните результаты.