【发布时间】:2011-04-21 05:26:42
【问题描述】:
通常,我在“网上”遇到的与 SSE/MMX 相关的所有内容都是向量和矩阵的数学知识。但是,我正在寻找 SSE 优化的“标准函数”库,例如 Agner Fog 提供的库,或者 GCC 中的一些基于 SSE 的字符串扫描算法。
简要概括一下:这些内容包括 memset、memcpy、strstr、memcmp BSR/BSF,即从 SSE 指令构建的 stdlib-esque
我希望它们使用内在函数而不是汇编用于 SSE1(正式 MMX2),但两者都可以。希望这不会太宽泛。
更新 1
经过一番搜索,我发现了一些有前途的东西,一个图书馆引起了我的注意:
- LibFreeVec:似乎只有 mac/IBM(由于基于 AltiVec),因此用处不大(对我来说),而且我似乎找不到直接下载链接,也没有说明支持的最低 SSE 版本李>
我还在一些向量化字符串函数(strlen、strstr strcmp)上遇到了an article。但是 SSE4.2 是我无法企及的方式(如前所述,我想坚持使用 SSE1/MMX)。
更新 2
Paul R 激励我做一些基准测试,不幸的是,由于我的 SSE 汇编编码经验接近 zip,我使用了其他人 (http://www.mindcontrol.org/~hplus/) 的基准测试代码并添加到其中。所有测试(不包括原始版本,即 VC6 SP5)均在 VC9 SP1 下编译,并具有完全/自定义优化和/arch:SSE on。
第一个测试是我的一台家用机器(AMD Sempron 2200+ 512mb DDR 333),上限为 SSE1(因此没有通过 MSVC memcpy 进行矢量化):
comparing P-III SIMD copytest (blocksize 4096) to memcpy
calculated CPU speed: 1494.0 MHz
size SSE Cycles thru-sse memcpy Cycles thru-memcpy asm Cycles thru-asm
1 kB 2879 506.75 MB/s 4132 353.08 MB/s 2655 549.51 MB/s
2 kB 4877 598.29 MB/s 7041 414.41 MB/s 5179 563.41 MB/s
4 kB 8890 656.44 MB/s 13123 444.70 MB/s 9832 593.55 MB/s
8 kB 17413 670.28 MB/s 25128 464.48 MB/s 19403 601.53 MB/s
16 kB 34569 675.26 MB/s 48227 484.02 MB/s 38303 609.43 MB/s
32 kB 68992 676.69 MB/s 95582 488.44 MB/s 75969 614.54 MB/s
64 kB 138637 673.50 MB/s 195012 478.80 MB/s 151716 615.44 MB/s
128 kB 277678 672.52 MB/s 400484 466.30 MB/s 304670 612.94 MB/s
256 kB 565227 660.78 MB/s 906572 411.98 MB/s 618394 603.97 MB/s
512 kB 1142478 653.82 MB/s 1936657 385.70 MB/s 1380146 541.23 MB/s
1024 kB 2268244 658.64 MB/s 3989323 374.49 MB/s 2917758 512.02 MB/s
2048 kB 4556890 655.69 MB/s 8299992 359.99 MB/s 6166871 484.51 MB/s
4096 kB 9307132 642.07 MB/s 16873183 354.16 MB/s 12531689 476.86 MB/s
第二批测试是在大学工作站上完成的(Intel E6550、2.33Ghz、2gb DDR2 800?)
VC9 SSE/memcpy/ASM:
comparing P-III SIMD copytest (blocksize 4096) to memcpy
calculated CPU speed: 2327.2 MHz
size SSE Cycles thru-sse memcpy Cycles thru-memcpy asm Cycles thru-asm
1 kB 392 5797.69 MB/s 434 5236.63 MB/s 420 5411.18 MB/s
2 kB 882 5153.51 MB/s 707 6429.13 MB/s 714 6366.10 MB/s
4 kB 2044 4447.55 MB/s 1218 7463.70 MB/s 1218 7463.70 MB/s
8 kB 3941 4613.44 MB/s 2170 8378.60 MB/s 2303 7894.73 MB/s
16 kB 7791 4667.33 MB/s 4130 8804.63 MB/s 4410 8245.61 MB/s
32 kB 15470 4701.12 MB/s 7959 9137.61 MB/s 8708 8351.66 MB/s
64 kB 30716 4735.40 MB/s 15638 9301.22 MB/s 17458 8331.57 MB/s
128 kB 61019 4767.45 MB/s 31136 9343.05 MB/s 35259 8250.52 MB/s
256 kB 122164 4762.53 MB/s 62307 9337.80 MB/s 72688 8004.21 MB/s
512 kB 246302 4724.36 MB/s 129577 8980.15 MB/s 142709 8153.80 MB/s
1024 kB 502572 4630.66 MB/s 332941 6989.95 MB/s 290528 8010.38 MB/s
2048 kB 1105076 4211.91 MB/s 1384908 3360.86 MB/s 662172 7029.11 MB/s
4096 kB 2815589 3306.22 MB/s 4342289 2143.79 MB/s 2172961 4284.00 MB/s
可以看出,SSE 在我的家庭系统上非常快,但落在 intel 机器上(可能是由于编码错误?)。我的 x86 汇编变体在我的家用机器上排名第二,在英特尔系统上排名第二(但结果看起来有点不一致,一个拥抱块它主导了 SSE1 版本)。 MSVC memcpy 赢得了英特尔系统测试,这是由于 SSE2 矢量化,但在我的家用机器上,它失败得很惨,即使是可怕的 __movsd 也胜过它......
陷阱:内存是 2 的所有对齐幂。缓存(希望)被刷新。 rdtsc 用于计时。
兴趣点:MSVC 有一个(未在任何参考中列出)__movsd 内在函数,它输出与我正在使用的相同的汇编代码,但它失败得很惨(即使是内联的!)。这可能就是它未上市的原因。
VC9 memcpy 可以在我的非 sse 2 机器上强制向量化,但是它会破坏 FPU 堆栈,它似乎也有一个错误。
这是我用来测试的full source(包括我的更改,再次感谢http://www.mindcontrol.org/~hplus/ 的原始版本)。项目文件的二进制文件可根据要求提供。
总之,切换变体似乎是最好的,类似于 MSVC crt 变体,只是更坚固,有更多选项和一次性检查(通过内联函数指针?或者像内部直接调用补丁这样更狡猾的东西),但是内联可能不得不使用最佳案例方法来代替
更新 3
Eshan 提出的一个问题提醒了一些有用的和与此相关的东西,虽然仅适用于位集和位操作,BitMagic 并且对于大型位集非常有用,它甚至在 SSE2 (bit) optimization 上有一篇不错的文章。不幸的是,这仍然不是 CRT/stdlib esque 类型库。似乎这些项目中的大多数都致力于特定的一小部分(问题)。
这提出了一个问题,是否值得创建一个开源的、可能是多平台性能的 crt/stdlib 项目,创建各种版本的标准化函数,每个版本都针对特定情况进行了优化,以及“最佳情况/通用函数变体,具有标量/MMX/SSE/SSE2+(à la MSVC)的运行时分支或强制编译时标量/SIMD 切换。
这对于 HPC 或对每一点性能都很重要的项目(如游戏)很有用,让程序员不必担心内置函数的速度,只需稍作调整即可找到最佳优化变体。
更新 4
我认为应该扩展这个问题的性质,包括可以使用 SSE/MMX 来优化非向量/矩阵应用程序的技术,这也可能用于 32/64 位标量代码。一个很好的例子是如何使用标量技术(位操作)、MMX 和 SSE/SIMD 一次检查给定 32/64/128/256 位数据类型中的字节是否出现
另外,我看到很多“只使用 ICC”的答案,这是一个很好的答案,这不是我的答案,因为首先,ICC 不是我可以连续使用的东西(除非英特尔有windows 的免费学生版),因为有 30 次试用。其次,更重要的是,我不仅关注库本身,还关注用于优化/创建它们包含的功能的技术,以供我个人改进和改进,因此我可以将这些技术和原则应用于我自己的代码(如果需要),结合使用这些库。希望这可以清除那部分:)
【问题讨论】:
-
是什么让您认为这些功能尚未实现以尽可能使用 SSE?
-
@jalf:因为各种原因,我经常调试我的应用程序(使用 ollydbg),所以我知道它的输出代码,我可以看到它的缺点。我唯一一次看到一些 SSE 代码是针对 memset 的,当
/arch:SSE2开启时,它的 SSE2 代码却不会在我的系统上运行。也是一名逆向工程师,我从一般活动中知道 MSVC 如何实现 crt 功能:P
标签: c++ c visual-c++ assembly sse