提问人:Gendai 提问时间:11/2/2023 最后编辑:Gendai 更新时间:11/2/2023 访问量:118
Windows 和 Linux 之间使用 SIMD 的代码速度差异
Speed difference of code using SIMD between Windows and Linux
问:
我正在做一个项目,我第一次尝试使用矢量化来加快计算时间。 总体思路是给出一个足够大的数组,应用一些按位掩码,并计算具有位奇偶校验的uint16_t数。 以下代码是检查性能的测试用例,它生成给定大小的随机数据,然后执行对 std::transform 的调用。
#include <map>
#include <vector>
#include <execution>
#include <immintrin.h>
#include <cstdint>
#include <chrono>
#include <random>
#include <memory>
void test_si128()
{
std::random_device dev;
std::mt19937 rng(dev());
std::uniform_int_distribution<std::mt19937::result_type> dist6(0, 8192);
const int kVectorSize = 32767;
const unsigned long sectionSize = ((kVectorSize + 1) / 2) / 8;
std::vector<std::vector<std::uint16_t>> vectorTest;
vectorTest.resize(kVectorSize);
for (int d0 = 0; d0 < kVectorSize; ++d0)
{
for (int d1 = d0 + 1; d1 < kVectorSize + 1; ++d1)
{
const int index = d0 ^ d1;
vectorTest[index - 1].push_back(static_cast<std::uint16_t>(dist6(rng)));
}
}
__m128i** masks = new __m128i*[kVectorSize];
int i = 0;
for (const auto& innerList : vectorTest)
{
__m128i* sectionPtr = new __m128i[sectionSize];
for (auto section = 0; section < sectionSize; ++section)
{
sectionPtr[section] = _mm_loadu_si128((__m128i*)(&innerList[0] + (section * 8)));
}
masks[i] = sectionPtr;
i++;
}
unsigned long* pRes = new unsigned long[kVectorSize];
// Tests masks
const __m128i oneBuf = _mm_set_epi16(1, 1, 1, 1, 1, 1, 1, 1);
const __m128i sectionMask = _mm_set_epi16(6, 6, 6, 6, 6, 6, 6, 6);
auto startChrono = std::chrono::high_resolution_clock::now();
std::transform(std::execution::par_unseq, masks, masks + kVectorSize, pRes,
[§ionMask = std::as_const(sectionMask), &oneBuf = std::as_const(oneBuf), §ionSize = std::as_const(sectionSize)](const __m128i* const& list) {
__m128i accu = _mm_setzero_si128();
for (auto section = 0; section < sectionSize; ++section)
{
const __m128i res = _mm_and_si128(list[section], sectionMask);
__m128i y = _mm_srli_epi16(res, 1);
y = _mm_xor_si128(res, y);
__m128i yTmp = _mm_srli_epi16(y, 2);
yTmp = _mm_xor_si128(y, yTmp);
y = _mm_srli_epi16(yTmp, 4);
yTmp = _mm_xor_si128(y, yTmp);
y = _mm_srli_si128(yTmp, 1);
y = _mm_xor_si128(yTmp, y);
y = _mm_and_si128(y, oneBuf);
accu = _mm_add_epi16(accu, y);
}
const unsigned long red =
_mm_extract_epi16(accu, 0)
+ _mm_extract_epi16(accu, 1)
+ _mm_extract_epi16(accu, 2)
+ _mm_extract_epi16(accu, 3)
+ _mm_extract_epi16(accu, 4)
+ _mm_extract_epi16(accu, 5)
+ _mm_extract_epi16(accu, 6)
+ _mm_extract_epi16(accu, 7);
return red;
});
auto endChrono = std::chrono::high_resolution_clock::now();
std::cout << "Time: " << std::chrono::duration_cast<std::chrono::milliseconds>(endChrono - startChrono).count() << " milliseconds\n";
}
int main(int argc, char** argv)
{
test_si128();
}
我的问题是,在同一台机器(x64,I5-13600k)上提供了相同的精确代码,我看到在Windows上使用MSVC(2022)和linux使用g++(11.4.0)编译的代码之间存在非常明显的速度差异。 在这两种情况下,我都在版本中编译,启用了 AVX 的 C++17 并进行了全面优化。 在 Windows 上,此代码在大约 ~20 毫秒内完成,在 Linux 上需要 ~90 毫秒。
速度差异似乎来自直接在 128 位上执行的内部函数,因为当我评论它们时,代码在 Linux 上运行得更快,并且达到比在 Windows 上相似的性能。我想到了一个内存对齐问题,但即使使用 std::align_alloc 来分配 sectionPtr,执行时间也是相同的。
Windows 和 Linux 之间是否存在这种速度差异?
答: 暂无答案
评论
std::chrono::high_resolution_clock
std::chrono::system_clock
std::chrono::steady_clock