Cortex-M4 SIMD медленнее, чем скаляр
У меня есть несколько мест в моем коде, которые действительно могут использовать некоторое ускорение, когда я пытаюсь использовать инструкции 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
3 ответа
Теперь, после разборки: вы увидите, что и у скалярной версии, и у версии simd 29 инструкций, а версия SIMD на самом деле занимает больше места для кода. (Скаляр 0x58 -> 0xba для внутреннего цикла, в отличие от SIMD (0x5c -> 0xc2))
Вы можете видеть, что используется много инструкций для получения данных в правильном формате для обоих циклов... может быть, вы можете улучшить производительность, работая над распаковкой / переупаковкой бита RGB, а не вычислением альфа-смеси!
Изменить: Вы также можете рассмотреть возможность обработки пар пикселей одновременно.
Если вы хотите действительно оптимизировать функцию, вы должны проверить выходные данные компилятора на ассемблере. Затем вы узнаете, как он преобразует ваш код, а затем вы сможете научиться писать код, помогающий компилятору выводить лучше, или писать необходимый ассемблер.
Одна из простых побед, которую вы быстро добьетесь в своем цикле альфа-смешивания, - медленное деление.
Вместо x / 100
использовать
x * 65536 / 65536 / 100
-> x * (65536 / 100) / 65536
-> x * 655.36 >> 16
-> x * 656 >> 16
Еще лучшей альтернативой было бы использование альфа-значений в диапазоне от 0 до> 256, чтобы вы могли просто сдвинуть результат с битами, даже не выполняя этот трюк.
Одна из причин, по которой smuad может не принести никакой выгоды, заключается в том, что вам нужно переместить данные в формат, предназначенный специально для этой команды.
Я не уверен, сможете ли вы добиться большего успеха в целом, но я подумал, что укажу способ избежать разделения в вашей выборке. Кроме того, если вы осмотрите сборку, вы можете обнаружить, что существует генерация кода, которая, как вы ожидаете, может быть устранена.
Сообщество Wiki Ответ
Основное изменение в соответствии с инструкцией типа 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-битное значение на верхнее.
Для блиттер-кода Бит-блог и Бит-хаки полезны для извлечения и манипулирования значениями RGB565; будь то SIMD или прямой код Thumb2.
Главным образом, это никогда не простая перекомпиляция для использования SIMD. Это может быть недели работы, чтобы преобразовать алгоритм. Если все сделано правильно, ускорения SIMD значительны, когда алгоритм не ограничен пропускной способностью памяти и не включает много условных выражений.