Я реализую алгоритм (SpookyHash), который обрабатывает произвольные данные как 64-битные целые числа, путем литья указателя на (ulong*)
. (Это связано с тем, как работает SpookyHash, переписывание, чтобы не делать этого, не является жизнеспособным решением).Определение требований к выравниванию ЦП
Это означает, что он может завершить чтение 64-битных значений, которые не выровнены по 8-байтовым границам.
На некоторых процессорах это работает нормально. На некоторых это было бы очень медленно. В других случаях это приведет к ошибкам (либо исключениям, либо неправильным результатам).
Поэтому у меня есть код для обнаружения невыровненных чтений и копирование кусков данных в 8-байтовые выровненные буферы, когда это необходимо, перед их работой.
Однако, моя собственная машина имеет Intel x86-64. Это допускает неглавные чтения достаточно хорошо, что дает гораздо более высокую производительность, если я просто игнорирую проблему выравнивания, равно как и x86. Он также позволяет использовать memcpy
-like и memzero
-подобные методы для работы в 64-байтовых кусках для другого повышения. Эти два улучшения производительности являются значительными, более чем достаточно для того, чтобы сделать такую оптимизацию далеко не преждевременной.
So. У меня есть оптимизация, которая стоит делать на некоторых чипах (и, если на то пошло, вероятно, два чипа, которые, скорее всего, будут работать на этом коде), но будут фатальными или ухудшат производительность для других. Ясно, что идеальным является определение того, к какому случаю я имею дело.
Некоторые дополнительные требования:
Это предназначено для кросс-платформенной библиотеки для всех систем, поддерживающих .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 (в том числе AMD64 в 32-разрядном режиме), stackalloc
В 64-битном типе иногда может возвращаться указатель, который выровнен по 4 байта, но не 8-байтовый, поэтому я могу тогда сказать, что выравнивание обходного пути не является eeded, и никогда не пытаться его снова:
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, чтобы вы могли проверять новые архитектуры и включать оптимизацию без переустановки/перераспределения. – Stu
(На самом деле, это хуже - со вторым вы не просто зацепляете свой вагон с текущими реализациями ЦП, но и за реализацию Mono. Что делать, если в будущей версии они вдруг станут «полезными» «с вопросами выравнивания?) – Stu
Хорошо, еще один, независимо от того, какой путь вы выберете: если вы кешируете свой результат между выполнением, чтобы избежать странных ситуаций на виртуальных машинах, не просто кешируйте флаг, а также кешируйте, на какой дуге вы получили результат. Затем при запуске проверьте, все ли вы работаете на одной арке. – Stu