- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在尝试为以下功能编写AXV版本:
void
hashids_shuffle(char *str, size_t str_length, char *salt, size_t salt_length)
{
size_t i, j, v, p;
char temp;
if (!salt_length) {
return;
}
for (i = str_length - 1, v = 0, p = 0; i > 0; --i, ++v) {
v %= salt_length;
p += salt[v];
j = (salt[v] + v + p) % i;
temp = str[i];
str[i] = str[j];
str[j] = temp;
}
}
v %= salt_length;
。
最佳答案
仅仅问如何初始化向量,基本上就是在寻求教程。 Google提出了一些Intel使用内在函数的指南。我将假装这个问题并非微不足道,回答了有关尝试有效地实现此功能的问题。绝对不是您作为初学者矢量化的那种功能。
请参阅x86标签Wiki,以获得指向文档(尤其是esp)的链接。英特尔的内在函数指南。请参阅sse标签Wiki,以获取very nice intro to SIMD programming with SSE intrinsics, and how to use SIMD effectively的链接以及其他链接。
内容摘要:
v % salt_length
。 v++; v %= loop_invariant;
向量化。包括有关标题问题的答案,有关使用_mm_set_epi8
或为此目的初始化向量的其他方式。 % i
和swap之外的所有内容进行矢量化处理。 (即,将您便宜的所有操作矢量化,就像您问的那样)。(v + salt[v] + p)
(以及导致它的所有内容)矢量化为两个vpaddw
指令。循环外用于对p
进行矢量化的前缀总和设置非常棘手,但我最终也对其进行了矢量化。 j
元素矢量的标量内部循环中,在div
上出现瓶颈(或SVML可以执行的任何操作)和/或超大型字符串的高速缓存未命中。 salt_length = sizeof(size_t)
是一个编译时常量,并且比向量长度小2的幂,因此
v++
和v%=salt_length
在所有处都不需要循环内的任何代码,并且由于有效展开循环的副作用而免费发生并行执行多个
v
值。
size_t
,因此更改为uint64_t似乎很有意义,除非您永远不需要在机器之间共享盐腌的哈希。)
v
遵循重复模式0 1 2 3 0 1 2 3 ...(对于64位,则为0..7)。在矢量代码中,对于32字节向量中的4B个元素,我们可能一次执行8个
v
值,或者对于2B个元素一次执行16次迭代。
v
变成了循环不变的常数向量。有趣的是salt[v]
也是如此,因此我们无需在循环内进行任何盐表查找。 实际上,可以为标量和向量预先计算
v+salt[v]
。
v+salt[v]
并以4或8展开,删除LUT查找,以便所有内存/缓存吞吐量可用于实际交换。编译器可能不会为您执行此操作,因此您可能需要手动展开并编写额外的代码来处理最后一个奇数个字符串字节。 (无需展开,您仍然可以预先计算
v+salt[v]
的查找表,其类型足够宽,无法环绕)。
salt_length
也将允许更好的代码。
v %= compile_time_constant
比
div
insn便宜,并且在2的幂时非常便宜(它变成
v &= 7
)。如果标量版本可以内联,或者如果您使用
salt_length = sizeof(size_t)
而不是完全将其作为函数arg传递,则编译器可能会为您执行此操作。
v < salt_length
开头,因此我们最多只需要一个
v -= salt_length
将其包装回正确的范围并保持不变。这被称为“强度降低”操作,因为减法比除法更弱(且更便宜)。
// The scalar loop would benefit from this transformation, too.
// or better, unroll the scalar loop by 8 so everything becomes constant
v++;
if( v >= salt_length)
v-= salt_length;
salt_length <= 16
,因此我们可以使用32个uint8_t值的向量。 (并且我们可以使用pshufb来矢量化
salt[v]
LUT查找)。
// untested // Vectorizing v++; v %= unknown_loop_invariant_value;
if (!salt_length) return;
assert(salt_length <= 16); // so we can use pshufb for the salt[v] step
__m256i vvec = _mm256_setr_epi8( // setr: lowest element first, unlike set
0%salt_length, 1%salt_length, 2%salt_length, 3%salt_length,
4%salt_length, 5%salt_length, 6%salt_length, 7%salt_length,
8%salt_length, 9%salt_length, 10%salt_length, 11%salt_length,
12%salt_length, 13%salt_length, 14%salt_length, 15%salt_length,
16%salt_length, 17%salt_length, 18%salt_length, 19%salt_length,
20%salt_length, 21%salt_length, 22%salt_length, 23%salt_length,
24%salt_length, 25%salt_length, 26%salt_length, 27%salt_length,
28%salt_length, 29%salt_length, 30%salt_length, 31%salt_length);
__m256i v_increment = _mm256_set1_epi8(32 % salt_length);
__m256i vmodulus = _mm256_set1_epi8(salt_length);
// salt_lut = _mm256_set1_epi64x(salt_byval); // for known salt length. (pass it by val in a size_t arg, instead of by char*).
// duplicate the salt into both lanes of a vector. Garbage beyond salt_length isn't looked at.
__m256i salt_lut = _mm256_broadcastsi128_si256(_mm_loadu_si128(salt)); // nevermind that this could segfault if salt is short and at the end of a page.
//__m256i v_plus_salt_lut = _mm256_add_epi8(vvec, salt_lut); // not safe with 8-bit elements: could wrap
// We could use 16-bit elements and AVX512 vpermw (or vpermi2w to support longer salts)
for (...) {
vvec = _mm256_add_epi8(vvec, v_increment); // ++v;
// if(!(salt_length > v)) { v-= salt_length; }
__m256i notlessequal = _mm256_cmpgt_epi8(vmodulus, vvec); // all-ones where salt_length > v.
// all-zero where salt_length <= v, where we need to subtract salt_length
__m256i conditional_sub = _mm256_and_si256(notlessequal, vmodulus)
vvec = _mm256_sub_epi8(vvec, conditional_sub); // subtract 0 or salt_length
// salt[v] lookup:
__m256i saltv = _mm256_shuffle_epi8(salt_lut, vvec); // salt[v]
// then maybe pmovzx and vextracti128+pmovzx to zero-extend to 16-bit elements? Maybe vvec should only be a 16-bit vector?
// or unpack lo/hi with zeros (but that behaves differently from pmovzx at the lane boundary)
// or have vvec already holding 16-bit elements with the upper half of each one always zero. mask after the pshufb to re-zero,
// or do something clever with `vvec`, `v_increment` and `vmodulus` so `vvec` can have `0xff` in the odd bytes, so pshufb zeros those elements.
}
vvec = _mm256_add_epi8(vvec, _mm256_set1_epi8(salt_length)); // ++v;
vvec = _mm256_and_si256(vvec, _mm256_set1_epi8(salt_length - 1)); // v &= salt_length - 1; // aka v%=salt_length;
(stuff) % i
:困难的部分
i
的最大值,因为
i
不是2的幂,并且不是恒定的,因此取模运算会起作用。任何更大的面积都是浪费,并降低了我们的生产率。如果我们可以对整个循环的其余部分进行矢量化处理,则甚至有必要针对
str_length
的不同范围使用不同版本的函数进行专门化。 (或者循环使用64b元素直到i <= UINT32_MAX,然后循环直到i <= UINT16_MAX,依此类推)。如果您知道不需要处理大于4GiB的字符串,则可以仅使用32位数学运算来加快常见情况。 (即使高位全为零,64位除法也比32位除法慢)。
p
一样宽的元素,因为它会永远累积(直到在原始标量代码中以2 ^ 64换行)。与恒定模数不同,即使取模是分布式的,我们也不能仅使用
p%=i
对其进行检查。
(123 % 33) % (33-16) != 123 % (33-16)
。甚至对齐16也无济于事:12345%32!= 12345%48%32
p
值,这也会迅速使
i
太大而无法重复进行
i
的条件减法(直到条件掩码全为false)。
i
值的下一个向量的常量。
% i
可以使用向量完成。一旦我们接近字符串的末尾,它在L1缓存中可能很热,因此我们完全陷入
div
的瓶颈,而不是交换加载/存储。为此,我们可能会使用一个序言来获得
i
值,该值是16的倍数,因此当我们接近i = 0时,最后几个向量始终具有相同的
i
值对齐方式。否则,我们将有一个常数LUT用于一系列i值,并从中简单地进行未对齐的载荷。这意味着我们不必旋转
salt_v
和
p
。
float
和
double
可以精确地表示任何最大为尾数大小的整数。)
_mm_rem_epu16
(使用
i
的向量递减的
set1(16)
值的向量)。如果他们使用FP获得准确的结果,那就太好了。如果仅将其解压缩为标量并进行整数除法,则会浪费时间将值重新返回到向量中。
// See the godbolt link for full design-notes comments
// comments about what makes nice ASM or not.
#include <stdint.h>
#include <stdlib.h>
#include <immintrin.h>
#include <assert.h>
static inline
__m256i init_p_and_increment(size_t salt_length, __m256i *p_increment, __m256i saltv_u16, __m128i saltv_u8)
{ // return initial p vector (for first 16 i values).
// return increment vector by reference.
if (salt_length == 4) {
assert(0); // unimplemented
// should be about the same as length == 8. Can maybe factor out some common parts, like up to psum2
} else {
assert(salt_length == 8);
// SIMD prefix sum for n elements in a vector in O(log2(n)) steps.
__m128i sv = _mm256_castsi256_si128(saltv_u16);
__m128i pshift1 = _mm_bslli_si128(sv, 2); // 1 elem (uint16_t)
__m128i psum1 = _mm_add_epi16(pshift1, sv);
__m128i pshift2 = _mm_bslli_si128(psum1, 4); // 2 elem
__m128i psum2 = _mm_add_epi16(pshift2, psum1);
__m128i pshift3 = _mm_bslli_si128(psum2, 8); // 4 elem
__m128i psum3 = _mm_add_epi16(pshift3, psum2); // p_initial low 128. 2^3 = 8 elements = salt_length
// psum3 = the repeating pattern of p values. Later values just add sum(salt[0..7]) to every element
__m128i p_init_low = psum3;
__m128i sum8_low = _mm_sad_epu8(saltv_u8, _mm_setzero_si128()); // sum(s0..s7) in each 64-bit half
// alternative:
// sum8_low = _mm_bsrli_si128(p_init_low, 14); // has to wait for psum3 to be ready: lower ILP than doing psadbw separately
__m256i sum8 = _mm256_broadcastw_epi16(sum8_low);
*p_increment = _mm256_slli_epi16(sum8, 1); // set1_epi16(2*sum(salt[0..7]))
__m128i p_init_high = _mm_add_epi16(p_init_low, _mm256_castsi256_si128(sum8));
__m256i p_init = _mm256_castsi128_si256(p_init_low);
p_init = _mm256_inserti128_si256(p_init, p_init_high, 1);
// not supported by gcc _mm256_set_m128i(p_init_high, psum3);
return p_init;
}
}
void
hashids_shuffle_simd(char *restrict str, size_t str_length, size_t salt_byval)
{
//assert(salt_length <= 16); // so we can use pshufb for the salt[v] step for non-constant salt length.
// platform-dependent salt size seems weird. Why not uint64_t?
size_t salt_length = sizeof(size_t);
assert(str_length-1 < UINT16_MAX); // we do p + v + salt[v] in 16-bit elements
// TODO: assert((str_length-1)/salt_length * p_increment < UINT16_MAX);
__m128i saltv_u8;
__m256i v, saltv;
if(salt_length == 4) {
v = _mm256_set1_epi64x(0x0003000200010000); // `v%salt_length` is 0 1 2 3 repeating
saltv_u8 = _mm_set1_epi32( salt_byval );
saltv = _mm256_cvtepu8_epi16( saltv_u8 ); // salt[v] repeats with the same pattern: expand it to 16b elements with pmovzx
} else {
assert(salt_length == 8);
v = _mm256_cvtepu8_epi16( _mm_set1_epi64x(0x0706050403020100) );
saltv_u8 = _mm_set1_epi64x( salt_byval );
saltv = _mm256_cvtepu8_epi16( saltv_u8 );
}
__m256i v_saltv = _mm256_add_epi16(v, saltv);
__m256i p_increment;
__m256i p = init_p_and_increment(salt_length, &p_increment, saltv, saltv_u8);
for (unsigned i=str_length-1; i>0 ; /*i-=16 */){
// 16 uint16_t j values per iteration. i-- happens inside the scalar shuffle loop.
p = _mm256_add_epi16(p, p_increment); // p += salt[v]; with serial dependencies accounted for, prefix-sum style
__m256i j_unmodded = _mm256_add_epi16(v_saltv, p);
// size_t j = (v + saltv[v] + p) % i;
//////// scalar loop over 16 j elements, doing the modulo and swap
// alignas(32) uint16_t jbuf[16]; // portable C++11 syntax
uint16_t jbuf[16] __attribute__((aligned(32))); // GNU C syntax
_mm256_store_si256((__m256i*)jbuf, j_unmodded);
const int jcount = sizeof(jbuf)/sizeof(jbuf[0]);
for (int elem = 0 ; elem < jcount ; elem++) {
if (--i == 0) break; // in fact returns from the whole function.
// 32-bit division is significantly faster than 64-bit division
unsigned j = jbuf[elem] % (uint32_t)i;
// doubtful that vectorizing this with Intel SVML _mm_rem_epu16 would be a win
// since there's no hardware support for it. Until AVX512CD, we need each element in a gp reg as an array index anyway.
char temp = str[i];
str[i] = str[j];
str[j] = temp;
}
}
}
-fno-unroll-loops
一起提供,以提高可读性。出于性能考虑,尽管将其留在这里并不重要,因为循环开销不是瓶颈。
# The loop part of clang3.8.1's output. -march=haswell -fno-unroll-loops (only for human readability. Normally it unrolls by 2).
.LBB0_6: # outer loop # in Loop: Header=BB0_3 Depth=1
add esi, 1
.LBB0_3: # first iteration entry point # =>This Loop Header: Depth=1
vpaddw ymm2, ymm2, ymm1 # p += p_increment
vpaddw ymm3, ymm0, ymm2 # v+salt[v] + p
vmovdqa ymmword ptr [rsp], ymm3 # store jbuf
add esi, -1
lea r8, [rdi + rsi]
mov ecx, 1
.LBB0_4: # inner loop # Parent Loop BB0_3 Depth=1
# gcc's version fully unrolls the inner loop, leading to code bloat
test esi, esi # if(i==0) return
je .LBB0_8
movzx eax, word ptr [rsp + 2*rcx - 2] # load jbuf
xor edx, edx
div esi
mov r9b, byte ptr [r8] # swap
mov al, byte ptr [rdi + rdx]
mov byte ptr [r8], al
mov byte ptr [rdi + rdx], r9b
add esi, -1
add r8, -1
cmp rcx, 16 # silly clang, not macro-fusing cmp/jl because it wants to use a weird way to increment.
lea rcx, [rcx + 1]
jl .LBB0_4 # inner loop
jmp .LBB0_6 # outer loop
关于c - 如何初始化范围从0到N的SIMD vector ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39277870/
我不能解决这个问题。和标题说的差不多…… 如果其他两个范围/列中有“否”,我如何获得范围或列的平均值? 换句话说,我想计算 A 列的平均值,并且我有两列询问是/否问题(B 列和 C 列)。我只希望 B
我知道 python 2to3 将所有 xrange 更改为 range 我没有发现任何问题。我的问题是关于它如何将 range(...) 更改为 list(range(...)) :它是愚蠢的,只是
我有一个 Primefaces JSF 项目,并且我的 Bean 注释有以下内容: @Named("reportTabBean") @SessionScoped public class Report
在 rails3 中,我在模型中制作了相同的范围。例如 class Common ?" , at) } end 我想将公共(public)范围拆分为 lib 中的模块。所以我试试这个。 module
我需要在另一个 View 范围 bean 中使用保存在 View 范围 bean 中的一些数据。 @ManagedBean @ViewScoped public class Attivita impl
为什么下面的代码输出4?谁能给我推荐一篇好文章来深入学习 javascript 范围。 这段代码返回4,但我不明白为什么? (function f(){ return f(); functio
我有一个与此结构类似的脚本 $(function(){ var someVariable; function doSomething(){ //here } $('#som
我刚刚开始学习 Jquery,但这些示例对我帮助不大...... 现在,以下代码发生的情况是,我有 4 个表单,我使用每个表单的链接在它们之间进行切换。但我不知道如何在第一个函数中获取变量“postO
为什么当我这样做时: function Dog(){ this.firstName = 'scrappy'; } Dog.firstName 未定义? 但是我可以这样做: Dog.firstNa
我想打印文本文件 text.txt 的选定部分,其中包含: tickme 1.1(no.3) lesson1-bases lesson2-advancedfurther para:using the
我正在编写一些 JavaScript 代码。我对这个关键字有点困惑。如何在 dataReceivedHandler 函数中访问 logger 变量? MyClass: { logger: nu
我有这个代码: Public Sub test() Dim Tgt As Range Set Tgt = Range("A1") End Sub 我想更改当前为“A1”的 Tgt 的引
我正忙于此工作,以为我会把它放在我们那里。 该数字必须是最多3个单位和最多5个小数位的数字,等等。 有效的 999.99999 99.9 9 0.99999 0 无效的 -0.1 999.123456
覆盖代码时: @Override public void open(ExecutionContext executionContext) { super.open(executio
我想使用 preg_match 来匹配数字 1 - 21。我如何使用 preg_match 来做到这一点?如果数字大于 21,我不想匹配任何东西。 example preg_match('([0-9]
根据docs range函数有四种形式: (range) 0 - 无穷大 (range end) 0 - 结束 (range start end)开始 - 结束 (range start end st
我知道有一个UISlider,但是有人已经制作了RangeSlider(用两个拇指吗?)或者知道如何扩展 uislider? 最佳答案 我认为你不能直接扩展 UISlider,你可能需要扩展 UICo
我正在尝试将范围转换为列表。 nums = [] for x in range (9000, 9004): nums.append(x) print nums 输出 [9000] [9
请注意:此问题是由于在运行我的修饰方法时使用了GraphQL解析器。这意味着this的范围为undefined。但是,该问题的基础知识对于装饰者遇到问题的任何人都是有用的。 这是我想使用的基本装饰器(
我正在尝试创建一个工具来从网页上抓取信息(是的,我有权限)。 到目前为止,我一直在使用 Node.js 结合 requests 和 Cheerio 来拉取页面,然后根据 CSS 选择器查找信息。我已经
我是一名优秀的程序员,十分优秀!