Well, here is my own final-for-now answer. While I'm answering my own question here, I owe a lot to the comments.
Ben Voigt and J Trana's comments made me realise something. While my specific question is a boolean one, the general question is not:
Pretty much all modern processors have a performance hit for unaligned reads, it's just that with some that hit is so slight as to be insignificant compared to the cost of avoiding it.
As such, there really isn't an answer to the question, "which processors allow unaligned reads cheaply enough?" but rather, "which processors allow unaligned reads cheaply enough for my current situation. As such, any fully consistent and reliable method isn't just impossible, but as a question unrelated to a particular case, meaningless.
And as such, white-listing cases known to be good enough for the code at hand, is the only way to go.
It's to Stu though that I owe managing to get my success with Mono on *nix up to that I was having with .NET and Mono on Windows. The discussion in the comments above brought my train of thought to a relatively simple, but reasonably effective, approach (and if Stu posts an answer with "I think you should base your approach on having platform-specific code run safely", I'll accept it, because that was the crux of one of his suggestions, and the key to what I've done).
As before I first try checking an environment variable that will generally be set in Windows, and not set on any other OS.
If that fails, I try to run uname -p
and parse the results. That can fail for a variety of reasons (not running on *nix, not having sufficient permissions, running on one of the forms of *nix that has a uname
command but no -p
flag). With any exception, I just eat the exception, and then try uname -m
, which his more widely available, but has a greater variety of labels for the same chips.
And if that fails, I just eat any exception again, and consider it a case of my white-list not having been satisfied: I can get false negatives which will mean sub-optimal performance, but not false positives resulting in error. I can also add to the white-list easily enough if I learn a given family of chips is similarly better off with the code-branch that doesn't try to avoid unaligned reads.
The current code looks like:
[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;
}