gpt4 book ai didi

c# - 检测 CPU 对齐要求

转载 作者:可可西里 更新时间:2023-11-01 07:58:44 24 4
gpt4 key购买 nike

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

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

在某些 CPU 上,这工作正常。在某些情况下,它会非常慢。在其他人身上,它会导致错误(异常或不正确的结果)。

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

但是,我自己的机器有一个 Intel x86-64。这足以容忍未对齐的读取,如果我忽略对齐问题,它会提供更快的性能,就像 x86 一样。它还允许使用类似于 memcpymemzero 的方法来处理 64 字节 block 以实现另一个提升。这两项性能改进是相当可观的,足以使这种优化远非过早。

所以。我有一个非常值得在某些芯片上进行的优化(就此而言,可能是最有可能在其上运行此代码的两个芯片),但在其他芯片上可能是致命的或性能较差。显然,理想的做法是检测我正在处理的是哪种情况。

一些进一步的要求:

  1. 这旨在成为支持 .NET 或 Mono 的所有系统的跨平台库。因此,特定于给定操作系统的任何内容(例如 P/调用操作系统调用)都是不合适的,除非它可以在调用不可用时安全地降级。

  2. 假阴性(将实际上安全的芯片识别为对优化不安全)是可以容忍的,而假阳性则不是。

  3. 昂贵的操作可以,只要能做一次,然后把结果缓存起来。

  4. 该库已经使用了不安全的代码,因此没有必要避免这种情况。

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

首先是初始化我的标志:

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)上,stackalloc处理 64 位类型有时可能会返回一个 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 字节对齐,这会产生同样的问题。

是否有任何改进的想法,或者更好的是,一种不会像上述两种方法那样产生漏报的方法?

最佳答案

好吧,这是我自己的最终答案。当我在这里回答我自己的问题时,我对评论有很大的帮助。

Ben Voigt 和 J Trana 的评论让我意识到了一些事情。虽然我的具体问题是一个 bool 问题,但一般问题不是:

几乎所有现代处理器都会因未对齐读取而受到性能影响,只是其中一些影响非常小,与避免它的成本相比微不足道。

因此,“哪些处理器允许非对齐读取的成本足够低?”这个问题确实没有答案。而是,“对于我目前的情况,哪些处理器允许未对齐读取足够便宜。因此,任何完全一致和可靠的方法不仅是不可能的,而且作为与特定情况无关的问题,毫无意义。

因此,已知足以处理手头代码的白名单案例是唯一的出路。

虽然在 Windows 上使用 .NET 和 Mono 时我在 *nix 上使用 Mono 获得成功,但我应该归功于 Stu。上面评论中的讨论将我的思路引向了一种相对简单但相当有效的方法(如果 Stu 发布了一个答案“我认为你应该将你的方法建立在让特定于平台的代码安全运行的基础上”,我会接受它,因为这是他的一个建议的症结所在,也是我所做工作的关键)。

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

如果失败,我会尝试运行 uname -p并解析结果。这可能会因多种原因而失败(未在 *nix 上运行,没有足够的权限,在具有 uname 命令但没有 -p 标志的 *nix 形式之一上运行)。任何异常,我只是吃掉异常,然后尝试uname -m ,它的使用范围更广,但相同芯片的标签种类更多。

如果失败了,我就再次吃掉任何异常,并认为这是我的白名单没有得到满足的情况:我可以得到漏报,这意味着性能不佳,但不会导致错误的误报.如果我了解到给定的芯片系列与不尝试避免未对齐读取的代码分支同样更好,我也可以很容易地添加到白名单。

当前代码如下:

[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;
}

关于c# - 检测 CPU 对齐要求,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20951038/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com