Проблема с производительностью ARM neon

Рассмотрим два следующих фрагмента кода, первый — это версия C:

void __attribute__((no_inline)) proj(uint8_t * line, uint16_t length)
{
    uint16_t i;
    int16_t tmp;
    for(i=HPHD_MARGIN; i<length-HPHD_MARGIN; i++) {
        tmp = line[i-3] - 4*line[i-2] + 5*line[i-1] - 5*line[i+1] + 4*line[i+2] - line[i+3];
        hphd_temp[i]=ABS(tmp);
    }
}

Вторая — та же функция (за исключением рамки), использующая неоновые встроенные функции.

void  __attribute__((no_inline)) proj_neon(uint8_t * line, uint16_t length)
{
    int i;
    uint8x8_t b0b7, b8b15, p1p8,p2p9,p4p11,p5p12,p6p13, m4, m5;
    uint16x8_t result;

    m4 = vdup_n_u8(4);
    m5 = vdup_n_u8(5);
    b0b7 = vld1_u8(line);
    for(i = 0; i < length - 16; i+=8) {
        b8b15 = vld1_u8(line + i + 8);
        p1p8  = vext_u8(b0b7,b8b15, 1);
        p2p9  = vext_u8(b0b7,b8b15, 2);
        p4p11 = vext_u8(b0b7,b8b15, 4);
        p5p12 = vext_u8(b0b7,b8b15, 5);
        p6p13 = vext_u8(b0b7,b8b15, 6);

        result = vsubl_u8(b0b7, p6p13); //p[-3]
        result = vmlal_u8(result, p2p9, m5); // +5 * p[-1];
        result = vmlal_u8(result, p5p12, m4);// +4 * p[1];
        result = vmlsl_u8(result, p1p8, m4); //-4 * p[-2];
        result = vmlsl_u8(result, p4p11, m5);// -5 * p[1];
        vst1q_s16(hphd_temp + i + 3, vabsq_s16(vreinterpretq_s16_u16(result)));
        b0b7 = b8b15;
    }
    /* todo : remaining pixel */

}

Я разочарован приростом производительности: он составляет около 10-15%. Если я посмотрю на сгенерированную сборку:

  • Версия C преобразована в цикл из 108 инструкций.
  • Неоновая версия трансформируется в цикл из 72 инструкций.

Но один цикл в неоновом коде вычисляет в 8 раз больше данных, чем итерация в цикле C, так что должно быть заметно значительное улучшение.

Есть ли у вас какое-либо объяснение относительно небольшой разницы между двумя версиями?

Дополнительные сведения: Тестовые данные представляют собой изображение с разрешением 10 Мпикс, время расчета составляет около 2 секунд для версии C.

Процессор: ARM Cortex A8


person shodanex    schedule 10.06.2013    source источник
comment
Нередко можно увидеть меньший прирост производительности, чем ожидалось, когда вы говорите о количестве инструкций и времени производительности. Особенно при работе с большими объемами данных, такими как изображения. Вполне вероятно, что, несмотря на разницу в 36 инструкций, большую часть времени вы ждете, пока что-то переместится в памяти, и что даже с такой разницей в инструкциях большая часть вашего прироста производительности исходит от неоновой памяти обработки кода. лучше (предсказание ветвления, большие куски в цикле, меньше инструкций и т. д.), чем от количества выполненных инструкций.   -  person ChrisCM    schedule 10.06.2013
comment
Какой компилятор, версию компилятора и параметры командной строки компилятора вы используете? Не могли бы вы включить дизассемблирование встроенной версии?   -  person unixsmurf    schedule 11.06.2013
comment
@ChrisCM: речь идет не о 108 против 72, а о 108 * 8 против 72. Даже принимая во внимание двойную проблему, я все равно могу ожидать 6-кратного улучшения.   -  person shodanex    schedule 11.06.2013
comment
Я немного удивлен, что вы не переставляете свои термины, такие как tmp = 1*(line[i-3] - line[i+3]) + 4*(line[i+2] - line[i-2]) + 5*(line[i-1] - line[i+1]), а просто вычисляете три разности плюс скалярное произведение в конце (это будет другая последовательность сбора-нагрузки/векторной подпрограммы/параллельного умножения). Но это поможет только в том случае, если вы уже не ограничены пропускной способностью памяти. Было бы целесообразно выдать __pld(*(line + 8)) перед циклом и __pld(*(line + i + 16)) внутри цикла.   -  person FrankH.    schedule 11.06.2013
comment
Можете ли вы опубликовать дизассемблированный код? Хорошо известно, что компилятор GNU иногда делает что-то сумасшедшее со встроенными функциями NEON. (Я чувствую запах крысы здесь)   -  person Jake 'Alquimista' LEE    schedule 12.06.2013


Ответы (1)


Я собираюсь сделать дикую догадку и сказать, что кэширование (данных) является причиной того, что вы не видите большого прироста производительности, которого ожидаете. Хотя я не знаю, поддерживает ли ваш чипсет кэширование или на каком уровне, если данные охватывают строки кэша, имеют плохое выравнивание или работают в среде, где ЦП одновременно выполняет другие задачи (прерывания, потоки и т. д.). .), то это также может исказить ваши результаты.

person Michael Dorgan    schedule 10.06.2013
comment
Действительно, при удалении операций, внешних по отношению к этим функциям, но происходящих между каждым вызовом, я увидел увеличение в 4 раза. - person shodanex; 11.06.2013