【发布时间】:2014-01-06 13:27:28
【问题描述】:
我正在实现一种算法 (SpookyHash),它将任意数据视为 64 位整数,方法是将指针转换为 (ulong*)。 (这是 SpookyHash 工作方式所固有的,重写为不这样做不是一个可行的解决方案。
这意味着它最终可能会读取未在 8 字节边界上对齐的 64 位值。
在某些 CPU 上,这可以正常工作。在某些情况下,它会非常慢。在其他情况下,它会导致错误(异常或不正确的结果)。
因此,我有代码来检测未对齐的读取,并在必要时将数据块复制到 8 字节对齐的缓冲区,然后再处理它们。
但是,我自己的机器有一个 Intel x86-64。这可以很好地容忍未对齐的读取,如果我像 x86 一样忽略对齐问题,它会提供更快的性能。它还允许memcpy-like 和memzero-like 方法处理 64 字节块以获得另一个提升。这两项性能改进是相当可观的,足以使这种优化远非为时过早。
所以。我有一个非常值得在某些芯片上进行的优化(就此而言,可能是最有可能在其上运行此代码的两个芯片),但对其他芯片来说可能是致命的或性能更差。显然,理想的情况是检测我正在处理哪种情况。
一些进一步的要求:
这是一个跨平台库,适用于所有支持 .NET 或 Mono 的系统。因此,任何特定于给定操作系统的东西(例如对操作系统调用的 P/Invoking)都是不合适的,除非它可以在调用不可用时安全地降级。
假阴性(将芯片识别为对优化不安全,而实际上它是安全的)是可以容忍的,而假阳性则不是。
昂贵的操作是可以的,只要它们可以完成一次,然后缓存结果。
库已经使用了不安全的代码,所以没有必要避免。
到目前为止,我有两种方法:
首先是初始化我的标志:
private static bool AttemptDetectAllowUnalignedRead()
{
switch(Environment.GetEnvironmentVariable("PROCESSOR_ARCHITECTURE"))
{
case "x86": case "AMD64": // Known to tolerate unaligned-reads well.
return true;
}
return false; // Not known to tolerate unaligned-reads well.
}
另一个是因为避免未对齐读取所需的缓冲区复制是使用stackalloc创建的,并且由于在 x86(包括 32 位模式下的 AMD64)上,stackalloc64 位类型有时可能会返回一个如果指针是 4 字节对齐但不是 8 字节对齐的,那么我可以在这一点上判断不需要对齐解决方法,并且永远不要再尝试它:
if(!AllowUnalignedRead && length != 0 && (((long)message) & 7) != 0) // Need to avoid unaligned reads.
{
ulong* buf = stackalloc ulong[2 * NumVars]; // buffer to copy into.
if((7 & (long)buf) != 0) // Not 8-byte aligned, so clearly this was unnecessary.
{
AllowUnalignedRead = true;
Thread.MemoryBarrier(); //volatile write
尽管后者仅适用于 32 位执行(即使允许未对齐的 64 位读取,stackalloc 的良好实现也不会强制它们在 64 位处理器上执行)。它还可能会产生误报,因为处理器可能会坚持 4 字节对齐,这会产生同样的问题。
有什么改进的想法,或者更好的方法,像上面两种方法一样不会产生误报吗?
【问题讨论】:
-
绝对,绝对选择第一个。一个适当的白名单是唯一的方法(如果 ARMv9 模拟但模拟效率低下,等等)。我要做的唯一更改是将白名单放在 app.config 中,这样您就可以验证新架构并启用优化,而无需重新构建/重新部署。
-
(实际上,情况更糟——使用第二个,您不仅将您的马车搭上了当前的 CPU 实现,而且还搭上了 Mono 的实现。如果他们在未来的版本中,所有突然决定对对齐问题“有帮助”?)
-
好吧,再来一个,不管你选择哪条路径:如果你在执行之间缓存你的结果,为了防止虚拟机上出现奇怪的情况,不要只缓存标志,还要缓存你得到的结果在。然后在启动时检查您是否仍在同一个拱门上运行。
-
@Stu,第一个运行良好,第二个有我在编辑中注意到的逻辑缺陷,这并不意味着处理器不能进行 4 字节对齐,但如果不是就会失败在 4 字节上。不过,我不喜欢永久缓存的想法,而不是内存中的静态变量。因为它是一个库,所以如果我不执行任何其他与文件相关的任务,IMO,我不应该要求文件写入权限(即使在某些沙箱中)。
-
@Stu,第一个的一个大风险是 x86 或 AMD64 的出现不能容忍这种对齐问题。 PowerPC 芯片 IIRC 发生了这种情况,它们的对齐方式比以前更严格了。