首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >检测CPU对齐要求

检测CPU对齐要求
EN

Stack Overflow用户
提问于 2014-01-06 13:27:28
回答 1查看 1.1K关注 0票数 15

我正在实现一个算法(SpookyHash),它通过将指向(ulong*)的指针转换为64位整数来处理任意数据。(这是SpookyHash工作方式所固有的,重写而不是这样做并不是一个可行的解决方案)。

这意味着它最终可能读取在8字节边界上不对齐的64位值。

在一些CPU上,这工作得很好。在某些情况下,这是非常缓慢的。在其他情况下,这将导致错误(异常或不正确的结果)。

因此,我有检测未对齐读取的代码,并在必要时将数据块复制到8字节对齐缓冲区中,然后再处理它们。

然而,我自己的机器有一个英特尔x86-64。这可以很好地容忍未对齐的读取,如果我忽略了对齐的问题,它就会提供更快的性能,x86也是如此。它还允许memcpy-like和memzero-like方法处理64字节的块,以再次提升。这两项性能改善是相当可观的,足以推动这样一种优化,而不是过早的。

所以。我有一个非常值得在某些芯片上进行的优化(就这一点而言,可能是最有可能运行此代码的两个芯片),但这将是致命的,或者会给其他芯片带来更差的性能。很明显,理想的方法是发现我所处理的是哪一种情况。

一些进一步的要求:

  1. 这是一个跨平台库,适用于所有支持.NET或Mono的系统。因此,任何特定于给定操作系统的内容(例如,对OS调用的P/调用)都是不合适的,除非它可以在调用不可用的情况下安全降级。
  2. 假底片(当芯片实际上是安全的时候,将芯片识别为不安全的芯片)是可以容忍的,假阳性是不可容忍的。
  3. 昂贵的操作是可以的,只要可以完成一次,然后缓存结果。
  4. 库已经使用了不安全的代码,因此没有必要避免这种情况。

到目前为止,我有两种方法:

第一种方法是初始化我的国旗:

代码语言:javascript
复制
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 ),64位类型的stackalloc有时可能返回一个指针,该指针对4字节对齐,但不对8字节对齐,这时我可以告诉您,不需要对齐解决方案,而且再也不要尝试它:

代码语言:javascript
复制
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字节对齐,这将产生同样的问题。

有什么改进的想法吗?或者更好的是,一种不会产生像上述两种方法那样的虚假否定的方法?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2014-01-08 10:15:39

好吧,这是我自己的最后答案。当我在这里回答我自己的问题时,我对这些评论负有很大的责任。

本·沃格特和J·特拉娜的评论让我意识到了一些事情。虽然我的具体问题是布尔型的,但一般的问题不是:

几乎所有的现代处理器对非对齐读取都有一定的性能影响,只是有些处理器的命中率很低,与避免它的成本相比是微不足道的。

因此,对于“哪些处理器允许以足够低的价格读取未对齐的数据”这一问题并没有一个答案。但是,“对于我目前的情况来说,哪种处理器允许不对齐读取足够便宜。因此,任何完全一致和可靠的方法不仅是不可能的,而且是一个与特定情况无关的问题,毫无意义。”

因此,已知的白名单案例足以满足当前代码的需要,这是唯一的方法。

不过,要感谢斯图,我应该设法在*nix上获得Mono的成功,直到我在.NET上和Mono在Windows上取得成功为止。上述评论中的讨论将我的思路引向了一种相对简单但相当有效的方法(如果Stu贴出了“我认为您应该将您的方法建立在平台特定代码安全运行的基础上”的回答,我将接受它,因为这是他的一个建议的关键,也是我所做的工作的关键)。

和之前一样,我首先尝试检查通常在Windows中设置的环境变量,而不是在任何其他操作系统上设置的环境变量。

如果失败,我尝试运行uname -p并解析结果。这可能由于各种原因而失败(没有在*nix上运行,没有足够的权限,运行在具有uname命令但没有-p标志的*nix形式之一上)。除了任何例外,我只吃例外,然后尝试uname -m,这是他的更广泛的可用,但有更多的标签相同的芯片。

如果失败了,我只会再吃一次例外,并认为这是我的白名单不满意的情况:我可以得到虚假的否定,这将意味着不理想的表现,但不是错误的错误。我也可以很容易地添加到白名单中,如果我知道一个给定的芯片家族同样更好地使用代码分支,而代码分支不试图避免未对齐读取。

当前代码如下所示:

代码语言:javascript
复制
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
  Justification = "Many exceptions possible, all of them survivable.")]
[ExcludeFromCodeCoverage]
private static bool AttemptDetectAllowUnalignedRead()
{
  switch(Environment.GetEnvironmentVariable("PROCESSOR_ARCHITECTURE"))
  {
    case "x86":
    case "AMD64": // Known to tolerate unaligned-reads well.
      return true;
  }
  // Analysis disable EmptyGeneralCatchClause
  try
  {
    return FindAlignSafetyFromUname();
  }
  catch
  {
    return false;
  }
}
[SecuritySafeCritical]
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
  Justification = "Many exceptions possible, all of them survivable.")]
[ExcludeFromCodeCoverage]
private static bool FindAlignSafetyFromUname()
{
  var startInfo = new ProcessStartInfo("uname", "-p");
  startInfo.CreateNoWindow = true;
  startInfo.ErrorDialog = false;
  startInfo.LoadUserProfile = false;
  startInfo.RedirectStandardOutput = true;
  startInfo.UseShellExecute = false;
  try
  {
    var proc = new Process();
    proc.StartInfo = startInfo;
    proc.Start();
    using(var output = proc.StandardOutput)
    {
      string line = output.ReadLine();
      if(line != null)
      {
        string trimmed = line.Trim();
        if(trimmed.Length != 0)
          switch(trimmed)
          {
            case "amd64":
            case "i386":
            case "x86_64":
            case "x64":
              return true; // Known to tolerate unaligned-reads well.
          }
      }
    }
  }
  catch
  {
    // We don't care why we failed, as there are many possible reasons, and they all amount
    // to our not having an answer. Just eat the exception.
  }
  startInfo.Arguments = "-m";
  try
  {
    var proc = new Process();
    proc.StartInfo = startInfo;
    proc.Start();
    using(var output = proc.StandardOutput)
    {
      string line = output.ReadLine();
      if(line != null)
      {
        string trimmed = line.Trim();
        if(trimmed.Length != 0)
          switch(trimmed)
        {
          case "amd64":
          case "i386":
          case "i686":
          case "i686-64":
          case "i86pc":
          case "x86_64":
          case "x64":
            return true; // Known to tolerate unaligned-reads well.
          default:
            if(trimmed.Contains("i686") || trimmed.Contains("i386"))
              return true;
            return false;
        }
      }
    }
  }
  catch
  {
    // Again, just eat the exception.
  }
  // Analysis restore EmptyGeneralCatchClause
  return false;
}
票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/20951038

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档