Sie können die Speicher nicht direkt vektorisieren, aber Sie können sie pipelieren, und Sie können die Adressberechnung auf 32-Bit-Plattformen (und in geringerem Maße auf 64-Bit-Plattformen) vektorisieren.
Das erste, was Sie tun wollen, was eigentlich nicht NEON benötigen, zu profitieren ist das Histogramm Array entrollen, so dass Sie mehr Daten im Flug auf einmal haben können:
#define NUM (7*1024*1024)
uint8 src_data[NUM];
uint32 histogram_result[256][4] = {{0}};
for (int i = 0; i < NUM; i += 4)
{
uint32_t *p0 = &histogram_result[src_data[i + 0]][0];
uint32_t *p1 = &histogram_result[src_data[i + 1]][1];
uint32_t *p2 = &histogram_result[src_data[i + 2]][2];
uint32_t *p3 = &histogram_result[src_data[i + 3]][3];
uint32_t c0 = *p0;
uint32_t c1 = *p1;
uint32_t c2 = *p2;
uint32_t c3 = *p3;
*p0 = c0 + 1;
*p1 = c1 + 1;
*p2 = c2 + 1;
*p3 = c3 + 1;
}
for (int i = 0; i < 256; i++)
{
packed_result[i] = histogram_result[i][0]
+ histogram_result[i][1]
+ histogram_result[i][2]
+ histogram_result[i][3];
}
Hinweis dass p0
bis p3
niemals auf die gleiche Adresse zeigen kann, also ist das Neuordnen ihrer Lese- und Schreibvorgänge in Ordnung.
Daraus können Sie die Berechnung von p0
zu p3
mit intrinsics vektorisieren, und Sie können die Finalisierungsschleife vektorisieren.
Testen Sie es als erstes (weil ich nicht!). Dann können Sie mit der Strukturierung des Arrays als result[4][256]
anstelle von result[256][4]
oder mit einem kleineren oder größeren Unroll-Faktor experimentieren.
Anwendung einiger NEON intrinsics dazu:
uint32 histogram_result[256 * 4] = {0};
static const uint16_t offsets[] = { 0x000, 0x001, 0x002, 0x003,
0x000, 0x001, 0x002, 0x003 };
uint16x8_t voffs = vld1q_u16(offsets);
for (int i = 0; i < NUM; i += 8) {
uint8x8_t p = vld1_u8(&src_data[i]);
uint16x8_t p16 = vshll_n_u8(p, 16);
p16 = vaddq_u16(p16, voffs);
uint32_t c0 = histogram_result[vget_lane_u16(p16, 0)];
uint32_t c1 = histogram_result[vget_lane_u16(p16, 1)];
uint32_t c2 = histogram_result[vget_lane_u16(p16, 2)];
uint32_t c3 = histogram_result[vget_lane_u16(p16, 3)];
histogram_result[vget_lane_u16(p16, 0)] = c0 + 1;
c0 = histogram_result[vget_lane_u16(p16, 4)];
histogram_result[vget_lane_u16(p16, 1)] = c1 + 1;
c1 = histogram_result[vget_lane_u16(p16, 5)];
histogram_result[vget_lane_u16(p16, 2)] = c2 + 1;
c2 = histogram_result[vget_lane_u16(p16, 6)];
histogram_result[vget_lane_u16(p16, 3)] = c3 + 1;
c3 = histogram_result[vget_lane_u16(p16, 7)];
histogram_result[vget_lane_u16(p16, 4)] = c0 + 1;
histogram_result[vget_lane_u16(p16, 5)] = c1 + 1;
histogram_result[vget_lane_u16(p16, 6)] = c2 + 1;
histogram_result[vget_lane_u16(p16, 7)] = c3 + 1;
}
Mit dem Histogramm Array abgerollt x8 anstatt x4 möchten Sie vielleicht acht skalare Speicher anstelle von vier verwenden, aber Sie müssen bedenken, dass die acht Zählregister impliziert und acht Adreßregister, die mehr Register als 32-Bit-ARM hat (da Sie SP und PC nicht verwenden können).
Leider, mit Adressberechnung in den Händen von NEON intrinsics, ich denke, der Compiler kann nicht sicher begründen, wie es in der Lage sein, Reads und schreibt neu zu ordnen, so müssen Sie sie explizit neu anordnen und hoffe, dass Sie mache es auf die bestmögliche Weise.
Es ist fast unmöglich, Histogramm mit jeder Art von SIMD, Neon enthalten zu optimieren. Die einzige nennenswerte Ausnahme ist AVX-512. –
Wie Paul sagte, benötigt Ihr Algorithmus Sammelladungen und Scatter-Stores, die die meisten SIMD-Befehlssätze nicht bieten. –