Cortex-M4 SIMD медленнее, чем Scalar

У меня есть несколько мест в моем коде, которые действительно можно было бы немного ускорить, когда я пытаюсь использовать инструкции CM4 SIMD, результат всегда медленнее, чем скалярная версия, например, это функция альфа-смешивания, которую я использую часто, это не очень медленно, но служит примером:

for (int y=0; y<h; y++) {
    i=y*w;
    for (int x=0; x<w; x++) {
        uint spix = *srcp++;
        uint dpix = dstp[i+x];
        uint r=(alpha*R565(spix)+(256-alpha)*R565(dpix))>>8;
        uint g=(alpha*G565(spix)+(256-alpha)*G565(dpix))>>8;
        uint b=(alpha*B565(spix)+(256-alpha)*B565(dpix))>>8;
        dstp[i+x]= RGB565(r, g, b);
    }
}

R565, G565, B565 и RGB565 - это макросы, которые извлекают и упаковывают RGB565 соответственно, игнорируйте

Теперь я попытался использовать __SMUAD и посмотреть, не изменится ли что-нибудь, результат был медленнее (или с той же скоростью, что и исходный код), даже попытался развернуть цикл, но безуспешно:

uint v0, vr, vg, vb;
v0 = (alpha<<16)|(256-alpha);
for (int y=0; y<h; y++) {
    i=y*w;
    for (int x=0; x<w; x++) {
        spix = *srcp++;
        dpix = dstp[i+x];

        uint vr = R565(spix)<<16 | R565(dpix);
        uint vg = G565(spix)<<16 | G565(dpix);
        uint vb = B565(spix)<<16 | B565(dpix);

        uint r = __SMUAD(v0, vr)>>8;
        uint g = __SMUAD(v0, vg)>>8;
        uint b = __SMUAD(v0, vb)>>8;

        dstp[i+x]= RGB565(r, g, b);
    }
}

Я знаю, что об этом уже спрашивали, но, учитывая архитектурные различия и тот факт, что ни один из ответов не решает мою проблему, я спрашиваю снова. Спасибо!

Обновлять

Скалярная разборка:

    Disassembly of section .text.blend:

00000000 <blend>:
   0:   e92d 0ff0   stmdb   sp!, {r4, r5, r6, r7, r8, r9, sl, fp}
   4:   6846        ldr r6, [r0, #4]
   6:   68c4        ldr r4, [r0, #12]
   8:   b086        sub sp, #24
   a:   199e        adds    r6, r3, r6
   c:   9601        str r6, [sp, #4]
   e:   9200        str r2, [sp, #0]
  10:   68ca        ldr r2, [r1, #12]
  12:   f89d 5038   ldrb.w  r5, [sp, #56]   ; 0x38
  16:   9204        str r2, [sp, #16]
  18:   9a01        ldr r2, [sp, #4]
  1a:   426e        negs    r6, r5
  1c:   4293        cmp r3, r2
  1e:   b2f6        uxtb    r6, r6
  20:   da5b        bge.n   da <blend+0xda>
  22:   8809        ldrh    r1, [r1, #0]
  24:   6802        ldr r2, [r0, #0]
  26:   9102        str r1, [sp, #8]
  28:   fb03 fb01   mul.w   fp, r3, r1
  2c:   9900        ldr r1, [sp, #0]
  2e:   4411        add r1, r2
  30:   9103        str r1, [sp, #12]
  32:   0052        lsls    r2, r2, #1
  34:   9205        str r2, [sp, #20]
  36:   9903        ldr r1, [sp, #12]
  38:   9a00        ldr r2, [sp, #0]
  3a:   428a        cmp r2, r1
  3c:   fa1f fb8b   uxth.w  fp, fp
  40:   da49        bge.n   d6 <blend+0xd6>
  42:   4610        mov r0, r2
  44:   4458        add r0, fp
  46:   f100 4000   add.w   r0, r0, #2147483648 ; 0x80000000
  4a:   9a04        ldr r2, [sp, #16]
  4c:   f8dd a014   ldr.w   sl, [sp, #20]
  50:   3801        subs    r0, #1
  52:   eb02 0040   add.w   r0, r2, r0, lsl #1
  56:   44a2        add sl, r4
  58:   f834 1b02   ldrh.w  r1, [r4], #2
  5c:   8842        ldrh    r2, [r0, #2]
  5e:   f3c1 07c4   ubfx    r7, r1, #3, #5
  62:   f3c2 09c4   ubfx    r9, r2, #3, #5
  66:   f001 0c07   and.w   ip, r1, #7
  6a:   f3c1 2804   ubfx    r8, r1, #8, #5
  6e:   fb07 f705   mul.w   r7, r7, r5
  72:   0b49        lsrs    r1, r1, #13
  74:   fb06 7709   mla r7, r6, r9, r7
  78:   ea41 01cc   orr.w   r1, r1, ip, lsl #3
  7c:   f3c2 2904   ubfx    r9, r2, #8, #5
  80:   f002 0c07   and.w   ip, r2, #7
  84:   fb08 f805   mul.w   r8, r8, r5
  88:   0b52        lsrs    r2, r2, #13
  8a:   fb01 f105   mul.w   r1, r1, r5
  8e:   097f        lsrs    r7, r7, #5
  90:   fb06 8809   mla r8, r6, r9, r8
  94:   ea42 02cc   orr.w   r2, r2, ip, lsl #3
  98:   fb06 1202   mla r2, r6, r2, r1
  9c:   f007 07f8   and.w   r7, r7, #248    ; 0xf8
  a0:   f408 58f8   and.w   r8, r8, #7936   ; 0x1f00
  a4:   0a12        lsrs    r2, r2, #8
  a6:   ea48 0107   orr.w   r1, r8, r7
  aa:   ea41 3142   orr.w   r1, r1, r2, lsl #13
  ae:   f3c2 02c2   ubfx    r2, r2, #3, #3
  b2:   430a        orrs    r2, r1
  b4:   4554        cmp r4, sl
  b6:   f820 2f02   strh.w  r2, [r0, #2]!
  ba:   d1cd        bne.n   58 <blend+0x58>
  bc:   9902        ldr r1, [sp, #8]
  be:   448b        add fp, r1
  c0:   9901        ldr r1, [sp, #4]
  c2:   3301        adds    r3, #1
  c4:   428b        cmp r3, r1
  c6:   fa1f fb8b   uxth.w  fp, fp
  ca:   d006        beq.n   da <blend+0xda>
  cc:   9a00        ldr r2, [sp, #0]
  ce:   9903        ldr r1, [sp, #12]
  d0:   428a        cmp r2, r1
  d2:   4654        mov r4, sl
  d4:   dbb5        blt.n   42 <blend+0x42>
  d6:   46a2        mov sl, r4
  d8:   e7f0        b.n bc <blend+0xbc>
  da:   b006        add sp, #24
  dc:   e8bd 0ff0   ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, fp}
  e0:   4770        bx  lr
  e2:   bf00        nop

Разборка SIMD:

    sassembly of section .text.blend:

00000000 <blend>:
   0:   e92d 0ff0   stmdb   sp!, {r4, r5, r6, r7, r8, r9, sl, fp}
   4:   6846        ldr r6, [r0, #4]
   6:   68c4        ldr r4, [r0, #12]
   8:   b086        sub sp, #24
   a:   199e        adds    r6, r3, r6
   c:   9601        str r6, [sp, #4]
   e:   9200        str r2, [sp, #0]
  10:   68ca        ldr r2, [r1, #12]
  12:   f89d 5038   ldrb.w  r5, [sp, #56]   ; 0x38
  16:   9204        str r2, [sp, #16]
  18:   9a01        ldr r2, [sp, #4]
  1a:   f5c5 7680   rsb r6, r5, #256    ; 0x100
  1e:   4293        cmp r3, r2
  20:   ea46 4505   orr.w   r5, r6, r5, lsl #16
  24:   da5d        bge.n   e2 <blend+0xe2>
  26:   8809        ldrh    r1, [r1, #0]
  28:   6802        ldr r2, [r0, #0]
  2a:   9102        str r1, [sp, #8]
  2c:   fb03 fb01   mul.w   fp, r3, r1
  30:   9900        ldr r1, [sp, #0]
  32:   4411        add r1, r2
  34:   9103        str r1, [sp, #12]
  36:   0052        lsls    r2, r2, #1
  38:   9205        str r2, [sp, #20]
  3a:   9903        ldr r1, [sp, #12]
  3c:   9a00        ldr r2, [sp, #0]
  3e:   428a        cmp r2, r1
  40:   fa1f fb8b   uxth.w  fp, fp
  44:   da4b        bge.n   de <blend+0xde>
  46:   4610        mov r0, r2
  48:   4458        add r0, fp
  4a:   f100 4000   add.w   r0, r0, #2147483648 ; 0x80000000
  4e:   9a04        ldr r2, [sp, #16]
  50:   f8dd a014   ldr.w   sl, [sp, #20]
  54:   3801        subs    r0, #1
  56:   eb02 0040   add.w   r0, r2, r0, lsl #1
  5a:   44a2        add sl, r4
  5c:   f834 2b02   ldrh.w  r2, [r4], #2
  60:   8841        ldrh    r1, [r0, #2]
  62:   f3c2 07c4   ubfx    r7, r2, #3, #5
  66:   f3c1 06c4   ubfx    r6, r1, #3, #5
  6a:   ea46 4707   orr.w   r7, r6, r7, lsl #16
  6e:   fb25 f707   smuad   r7, r5, r7
  72:   f001 0907   and.w   r9, r1, #7
  76:   ea4f 3c51   mov.w   ip, r1, lsr #13
  7a:   f002 0607   and.w   r6, r2, #7
  7e:   ea4f 3852   mov.w   r8, r2, lsr #13
  82:   ea4c 0cc9   orr.w   ip, ip, r9, lsl #3
  86:   ea48 06c6   orr.w   r6, r8, r6, lsl #3
  8a:   ea4c 4606   orr.w   r6, ip, r6, lsl #16
  8e:   fb25 f606   smuad   r6, r5, r6
  92:   f3c1 2104   ubfx    r1, r1, #8, #5
  96:   f3c2 2204   ubfx    r2, r2, #8, #5
  9a:   ea41 4202   orr.w   r2, r1, r2, lsl #16
  9e:   fb25 f202   smuad   r2, r5, r2
  a2:   f3c6 260f   ubfx    r6, r6, #8, #16
  a6:   097f        lsrs    r7, r7, #5
  a8:   f3c6 01c2   ubfx    r1, r6, #3, #3
  ac:   f007 07f8   and.w   r7, r7, #248    ; 0xf8
  b0:   430f        orrs    r7, r1
  b2:   f402 52f8   and.w   r2, r2, #7936   ; 0x1f00
  b6:   ea47 3646   orr.w   r6, r7, r6, lsl #13
  ba:   4316        orrs    r6, r2
  bc:   4554        cmp r4, sl
  be:   f820 6f02   strh.w  r6, [r0, #2]!
  c2:   d1cb        bne.n   5c <blend+0x5c>
  c4:   9902        ldr r1, [sp, #8]
  c6:   448b        add fp, r1
  c8:   9901        ldr r1, [sp, #4]
  ca:   3301        adds    r3, #1
  cc:   428b        cmp r3, r1
  ce:   fa1f fb8b   uxth.w  fp, fp
  d2:   d006        beq.n   e2 <blend+0xe2>
  d4:   9a00        ldr r2, [sp, #0]
  d6:   9903        ldr r1, [sp, #12]
  d8:   428a        cmp r2, r1
  da:   4654        mov r4, sl
  dc:   dbb3        blt.n   46 <blend+0x46>
  de:   46a2        mov sl, r4
  e0:   e7f0        b.n c4 <blend+0xc4>
  e2:   b006        add sp, #24
  e4:   e8bd 0ff0   ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, fp}
  e8:   4770        bx  lr
  ea:   bf00        nop

person iabdalkader    schedule 21.08.2014    source источник
comment
попробуйте загрузить 32-битные вместо 16-битных, а затем обработайте два пикселя в цикле самостоятельно (вроде как развернуть).   -  person auselen    schedule 21.08.2014
comment
@auselen развернул 2 пикселя без значительного увеличения   -  person iabdalkader    schedule 21.08.2014
comment
Вы можете видеть, что при разборке это ldrh инструкция вместо ldr. Я говорю не о развертывании, а о загрузке 32-битной версии.   -  person auselen    schedule 21.08.2014
comment
это может помочь вам ускорить переход по ссылке   -  person francek    schedule 10.09.2014


Ответы (3)


Теперь с опубликованной разборкой: вы увидите, что и скалярная версия, и версия simd имеют 29 инструкций, а версия SIMD фактически занимает больше места для кода. (Скаляр - 0x58 -> 0xba для внутреннего цикла, vs SIMD - (0x5c -> 0xc2))

Вы можете видеть, что используется множество инструкций для перевода данных в правильный формат для обоих циклов ... возможно, вы сможете улучшить производительность больше, работая над распаковкой / переупаковкой битов RGB, а не над вычислением альфа-смешения!

Изменить: вы также можете рассмотреть возможность одновременной обработки пар пикселей.

person jtlim    schedule 21.08.2014
comment
но распаковка / упаковка происходит в обеих версиях, поэтому она отменяется, с SIMD я ожидал некоторого улучшения или, по крайней мере, такой же производительности, каждая инструкция заменяет 2 умножения и сложение. Также пробовал развернуть, как упоминалось. - person iabdalkader; 21.08.2014
comment
Внутренний цикл имеет такое же количество инструкций, поэтому 2 умножения и сложение заменяются дополнительным перемещением бит и инструкцией smuad. Если вы можете избежать / решить проблему смещения долота, вы можете получить выгоду от SIMD. - person jtlim; 21.08.2014
comment
Теперь я понимаю, что вы имеете в виду, и проверка сборки очень помогла, я разрываюсь между этим ответом и другим, они оба мне очень помогли, но ваши комментарии прояснили, поэтому принимаю ваш и поддерживаю всех :) - person iabdalkader; 21.08.2014
comment
Я не уверен, что это за макросы RGB, но я попытался скомпилировать код с моими собственными макросами RGB, и внутренний цикл состоял всего из 21 инструкции для ARM. - person jtlim; 21.08.2014

Если вы хотите по-настоящему оптимизировать функцию, вам следует проверить вывод компилятора на ассемблере. Затем вы узнаете, как он преобразует ваш код, а затем вы узнаете, как писать код, чтобы помочь компилятору производить лучший результат, или написать необходимый ассемблер.

Одна из простых побед, которую вы быстро добьетесь в своем цикле альфа-смешивания, - это то, что деление происходит медленно.

Вместо x / 100 используйте

x * 65536 / 65536 / 100

-> x * (65536 / 100) / 65536

-> x * 655.36 >> 16

-> x * 656 >> 16

Еще лучшей альтернативой было бы использовать альфа-значения в диапазоне от 0 до 256, чтобы вы могли просто сдвинуть результат на бит, даже не прибегая к этому трюку.

Одна из причин, по которой smuad может не дать никаких преимуществ, заключается в том, что вам нужно переместить данные в формат, специально предназначенный для этой команды.

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

person jtlim    schedule 21.08.2014
comment
Спасибо за подсказку, я буду использовать 256, но эта функция является всего лишь примером, моя цель - выяснить, почему почти все инструкции SIMD, которые я пробовал до сих пор, медленнее, и делаю ли я что-то не так или нет. - person iabdalkader; 21.08.2014
comment
Я бы рекомендовал больше всего проверить сборку ... тогда вы знаете, что говорят делать процессору ... и часто вы можете обнаружить, что компилятор не так умен, как вы надеялись :) - person jtlim; 21.08.2014
comment
Проблема с приведенным выше циклом заключается в том, что стоимость умножений почти наверняка меньше стоимости дивизий, поэтому любые улучшения, которые вы сделаете, не будут очевидными. - person jtlim; 21.08.2014
comment
заменил div на ›› 8, это не имело большого значения, спасло несколько нас в скалярной версии ... - person iabdalkader; 21.08.2014
comment
Я удивлен, что это не имело большого значения (какая разница в% для нескольких нас?); но еще раз, код ассемблера - это ключ. - person jtlim; 21.08.2014
comment
около 100-200 нас, добавлена ​​разборка. - person iabdalkader; 21.08.2014
comment
То же самое для версии SIMD, я думаю, это было ожидаемо, потому что они оба использовали деление раньше. - person iabdalkader; 21.08.2014
comment
Достаточно хорошие компиляторы умеют делать статический анализ такой арифметики намного лучше, чем программисты. Так что делать код менее читабельным - плохой совет. - person auselen; 21.08.2014
comment
Хороший совет - попросить проверить сборку на предмет наличия такого небольшого кода. - person auselen; 21.08.2014

Ответ сообщества вики

Основное изменение, связанное с инструкцией типа SIMD, - это преобразование загрузки. Инструкция SMUAD может можно рассматривать как инструкцию "C", например,

/* a,b are register vectors/arrays of 16 bits */
SMUAD = a[0] * b[0] + a[1] * b[1];

Их очень легко трансформировать. Вместо того,

    u16 *srcp, dstp;
    uint spix = *srcp++;
    uint dpix = dstp[i+x];

Используйте полную шину и получайте 32 бита за раз,

    uint *srcp, *dstp;  /* These are two 16 bit values. */
    uint spix = *srcp++;
    uint dpix = dstp[i+x];
    /* scale `dpix` and `spix` by alpha */
    spix /= (alpha << 16 | alpha);     /* Precompute, reduce strength, etc. */
    dpix /= (1-alpha << 16 | 1-alpha); /* Precompute, reduce strength, etc. */
    /* Hint, you can use SMUAD here? or maybe not.  
       You could if you scale at the same time*/

Похоже, SMUL хорошо подходит для масштабирования альфа; вы не хотите складывать две половинки.

Теперь spix и dpix содержат два пикселя. Синтетика vr не нужна. Вы можете выполнять две операции одновременно.

    uint rb = (dpix + spix) & ~GMASK;  /* GMASK is x6xx6x bits. */
    uint g = (dpix + spix) & GMASK;
    /* Maybe you don't care about overflow?  
       A dual 16bit add helps, if the M4 has it? */

    dstp[i+x]= rb | g;  /* write 32bits or two pixels at a time. */

В основном, просто более эффективное использование шины, загружая 32 бита за раз, определенно ускорит вашу рутину. Стандартная 32-битная целочисленная математика может работать большую часть времени, если вы внимательно относитесь к диапазонам и не переполняете нижнее 16-битное значение в верхнее.

Для блиттерного кода Bit blog и Битовые хаки полезны для извлечения и обработки значений RGB565; будь то SIMD или прямой код Thumb2.

В основном, это никогда не бывает простой повторной компиляцией для использования SIMD. На преобразование алгоритма могут уйти недели работы. Если все сделано правильно, ускорение SIMD будет значительным, когда алгоритм не ограничен полосой пропускания памяти и не включает много условных выражений.

person Community    schedule 21.08.2014
comment
Я понимаю, что вы имеете в виду, я попробую, спасибо. - person iabdalkader; 21.08.2014
comment
Было, но здесь у вас получилось намного лучше :) Проголосовали за ваш ответ. - person jtlim; 21.08.2014