【发布时间】:2021-04-22 20:28:34
【问题描述】:
所以我不久前做了这个 strlen,一切看起来都很好。但是我开始注意到我的代码库中的错误,过了一段时间我将其追踪到这个 strlen 函数。我使用 SIMD 指令来编写它,而且我是编写内部函数的新手,所以代码可能也不是最好的。
函数如下:
inline size_t strlen(const char* data) {
const __m256i terminationCharacters = _mm256_setzero_si256();
const size_t shiftAmount = ((size_t)&data) & 31;
const __m256i* pointer = (const __m256i*) (data - shiftAmount);
size_t length = 0;
for (;; length += 32, ++pointer) {
const __m256i comparingData = _mm256_load_si256(pointer);
const __m256i comparison = _mm256_cmpeq_epi8(comparingData, terminationCharacters);
if (!_mm256_testc_si256(terminationCharacters, comparison)) {
const auto mask = _mm256_movemask_epi8(comparison);
return length + _tzcnt_u32(mask >> shiftAmount);
}
}
}
【问题讨论】:
-
如果 *(data - 1) == '\0' 并且数据未对齐,那么我认为您的循环会立即终止,但字符串可能会更长。
-
我认为解决此问题的最简单方法是仅使用未对齐的加载(这实际上是否慢得多?)或对第一个 32 个字符块执行单个未对齐的加载并测试终止字符,然后在循环中移动到对齐的负载,但从数据之后的对齐位置开始。
-
这适用于 x86_64 吗?
-
@JamesGriffin:除非您检查是否接近页面末尾,否则您不能使用未对齐的加载。如果您将一个指针传递给 C
strlen,则它必须正确工作,该指针指向距离页面末尾仅 5 个字节的 3 字节字符串,并且下一页未映射。 Is it safe to read past the end of a buffer within the same page on x86 and x64?。正确处理这一切是诀窍的一部分。例如,参见 glibc 的strlen中的手写 asm。在第一个未对齐的向量可以工作之前进行页面交叉检查,然后对齐(重叠很好) -
什么样的错误?代码又好又短,但我们要寻找什么症状?错误的结果?段错误? minimal reproducible example 的一个重要部分是一个特定的问题描述,比如一个失败的测试用例,但如果你没有它,你至少可以说它是否在这段代码中出现了段错误。 (如果是这样,请使用调试器来查找详细信息。)虽然由于您正在对齐指针(我认为是正确的),但这是不可能的,因为我认为您也会找到一个终止符(如果有的话)。
标签: c simd memory-alignment avx strlen