【发布时间】:2018-04-24 03:39:27
【问题描述】:
这是计算缓冲区中不同值数量的基本算法:
unsigned getCount(const uint8_t data[16])
{
uint8_t pop[256] = { 0 };
unsigned count = 0;
for (int i = 0; i < 16; ++i)
{
uint8_t b = data[i];
if (0 == pop[b])
count++;
pop[b]++;
}
return count;
}
这可以通过加载到 q-reg 并做一些魔术来有效地在 neon 中完成吗?或者,我可以有效地说data 的所有元素都相同,还是只包含两个不同的值或两个以上?
例如,使用vminv_u8 和vmaxv_u8 我可以找到最小和最大元素,如果它们相等,我知道data 具有相同的元素。如果没有,那么我可以vceq_u8 使用最小值和vceq_u8 使用最大值然后vorr_u8 这些结果并比较我在结果中的所有1-s。基本上,在霓虹灯中可以这样做。有什么想法可以让它变得更好吗?
unsigned getCountNeon(const uint8_t data[16])
{
uint8x16_t s = vld1q_u8(data);
uint8x16_t smin = vdupq_n_u8(vminvq_u8(s));
uint8x16_t smax = vdupq_n_u8(vmaxvq_u8(s));
uint8x16_t res = vdupq_n_u8(1);
uint8x16_t one = vdupq_n_u8(1);
for (int i = 0; i < 14; ++i) // this obviously needs to be unrolled
{
s = vbslq_u8(vceqq_u8(s, smax), smin, s); // replace max with min
uint8x16_t smax1 = vdupq_n_u8(vmaxvq_u8(s));
res = vaddq_u8(res, vaddq_u8(vceqq_u8(smax1, smax), one));
smax = smax1;
}
res = vaddq_u8(res, vaddq_u8(vceqq_u8(smax, smin), one));
return vgetq_lane_u8(res, 0);
}
通过一些优化和改进,一个 16 字节的块可能可以在 32-48 个 neon 指令中处理。这可以在手臂上做得更好吗?不太可能
我问这个问题的一些背景。当我正在研究一种算法时,我正在尝试不同的方法来处理数据,但我不确定我最终会使用什么。可能有用的信息:
- 每 16 字节块的不同元素计数
- 每 16 字节块重复次数最多的值
- 平均每块
- 每块的中位数
- 光速?.. 开个玩笑,它不能用 16 字节块在霓虹灯中计算:)
所以,我正在尝试一些东西,在我使用任何方法之前,我想看看该方法是否可以得到很好的优化。例如,arm64 上的平均每块速度基本上就是 memcpy 速度。
【问题讨论】:
-
无需在
pop[b]++中进行post incr,只需这样做pop[b]=1,因为它们不需要确定每个字节的重复数。这可以通过避免读取改变写入来帮助缓存。 -
就这个问题而言,它并没有太大的相关性,在我的实际代码中我确实使用它,所以它在这里结束了。
-
uint8_t data[16] 是如何16字节缓冲区的
-
半相关:Fallback implementation for conflict detection in AVX2 是关于找出是否有任何重复的(32 位)元素,而不是找出它们在哪里或它们是什么。 (当常见的情况是没有重复时,可用作对分散/聚集冲突的乐观快速路径检查)。我只是在结果上使用 3 次随机播放、4 次比较和 3 次 OR 将每个元素与其他元素进行了比较。
-
@Pavel - 你的“小范围”问题会得到误导性的答案。针对较大数据集的优化解决方案将 8/16 问题作为最后一步。如果处理得当,(通常效率低下的)最终输出步骤将被大多数数据的有效解决方案所掩盖。以你的方式提问,你是在浪费专家的时间/经验来解决问题的错误结果。
标签: c++ arm intrinsics neon