Как раз недавно делал пулреквест, заменил нечитаемый метод на switch expression и получил не только легко поддерживаемый код, но и небольшое улучшение производительности.
Спасибо за ответ. Получается, нужно иметь в распоряжении прокси (всё-таки нужен свой сервер)? Извиняюсь, если туплю. Я очень далёк от сетевых технологий.
Бояться отсутствия инициализации нулями там, где совершенно точно, как в этом методе, не будет чтения из неинициализированного массива, - это какой-то карго-культ боязни unsafe.
У меня нет avx512 под рукой, но на avx2 это 11% разницы. Бесплатное повышение производительности с нулевыми рисками на дороге не валяется. Хотя нет, в этом случае как раз валяется.
На всякий случай копирую сюда метод, предложенный в статье, в котором есть сырые указатели и точно такая же не инициализированная нулями память.
private static unsafe string StackallocBuffer(string s)
{
var buffer = stackalloc char[s.Length];
var index = 0;
foreach (var c in s)
{
if (!char.IsWhiteSpace(c))
{
buffer[index++] = c;
}
}
return new string(buffer, 0, index);
}
Именно его я предложил заменить. Т.е. я буквально сделал предложенный в статье код менее опасным, заменив указатели на Span. И при этом сохранил производительность метода на том же уровне.
Span вместе с SkipLocalsInit всё ещё безопаснее, чем сырой указатель. Инициализации нулями не будет в обоих случаях. Поэтому, возвращаясь к изначальному сообщению, что не так с предложенным мной вариантом?
вы же видите что у вас Error примерно равен разнице результатов?
Вы сначала докопались к тому, чего не было в моём сообщении, а теперь доказываете то, что и так очевидно. Ради чего вы это делаете?
Вместо unsafe в StackallocBuffer лучше использовать Span, но пропустить инициализацию нулями с помощью атрибута SkipLocalsInit. Производительность будет точно такая же.
[SkipLocalsInit]
static string SpanBuffer(string s)
{
Span<char> buffer = stackalloc char[s.Length];
var index = 0;
foreach (var c in s)
{
if (!char.IsWhiteSpace(c))
{
buffer[index++] = c;
}
}
return new string(buffer[..index]);
}
Другие я даже не искал. Потому что использую именно GxHash, т.к. он очень быстрый. Поэтому и написал для него обертку. Уверен, что если кто-то ставил цель получить хеш массива структур, то решил эту задачу точно так же. Span используется повсеместно.
В моём коде нет никакого около-UB. Паддинги определяются через рефлексию здесь и сейчас. Генерация метода через IL - вполне официальный способ, а не хак. Остаётся лишь вопрос к тому, кто будет применять инструмент. Но это риторический вопрос. Я совершенно точно не в ответе за тех, кто пытается сделать подобные вещи без понимания их сути.
Я привёл пример с записью в файл. Проверка хеша, если не совпали, то идёт запись. Какая тут уязвимость может появиться? В худшем случае при несовпадении байт файл будет перезаписан, когда реальные данные не изменились. В примере с игрой в худшем случае будет выполнена тяжёлая логика лишний раз, но без проверки по хешу пришлось бы каждый кадр тратить большие ресурсы на эту же проверку. Естественно, что надо понимать, для чего используется подобный инструмент и какие опасности он несёт. Но это касается любых вещей. Очевидно, что подобный хеш не должен уйти в долговременное хранение, он предназначен для здесь и сейчас, для текущей сессии.
Если структура лежит в массиве или где-то внутри класса, JIT ничего с ней не сделает. Хеш массива помогает мгновенно понять, были ли в нём изменения. И если не было, то пропустить огромное количество логики. Полезно, например, в играх, где нужно каждый кадр пересчитывать состояние мира. Или когда нужно часто писать в файл, и вместо насилия над диском проще сравнивать хеш и писать только когда реально изменились данные.
А можно написать либу один раз и не писать ручками. Я не очень понимаю, о чём спор. Если вам не нужно, это не означает, что никому не нужно. Мы же тут программированием занимаемся. Если что-то можно автоматизировать, оно должно быть автоматизировано.
C# ли бы тоже выкинуть, где сделали что-то подобное? Или собственный код, где такой подход даёт ускорение, т.к. создаёт очень много таких структур и часто. А считать хеш нужно очень редко.
[SkipLocalsInit]
ExampleStruct[] Method0()
{
Span<ExampleStruct> arr = stackalloc ExampleStruct[10];
for (var i = 0; i < arr.Length; i++)
{
ref var s = ref arr[i];
s.A = (byte) i;
s.B = i * 10;
}
return arr.ToArray();
}
struct ExampleStruct
{
public byte A;
public int B;
}
Из плюсовой dll пришел массив структур, и у них паддинги набиты мусором. А нужно посчитать хеш, например, такой либой. И тут два варианта: или копировать их в новый массив, при чём нужно будет написать логику этого копирования, используя схожие техники, описанные мной в статье. Или обнулить паддинги. Это один пример из тысяч, зачем это нужно делать.
Как раз недавно делал пулреквест, заменил нечитаемый метод на switch expression и получил не только легко поддерживаемый код, но и небольшое улучшение производительности.
Спасибо за ответ. Получается, нужно иметь в распоряжении прокси (всё-таки нужен свой сервер)? Извиняюсь, если туплю. Я очень далёк от сетевых технологий.
После прочтения я так и не понял, через что пойдёт трафик для сайтов из списка?
Рекомендую обратить внимание на Zstandard. Порт для .NET ZstdSharp. С 24 года поддерживается всеми браузерами.
Если убрать лишнее и отформатировать нормально, то вполне читаемо получается.
Бояться отсутствия инициализации нулями там, где совершенно точно, как в этом методе, не будет чтения из неинициализированного массива, - это какой-то карго-культ боязни unsafe.
У меня нет avx512 под рукой, но на avx2 это 11% разницы. Бесплатное повышение производительности с нулевыми рисками на дороге не валяется. Хотя нет, в этом случае как раз валяется.
На всякий случай копирую сюда метод, предложенный в статье, в котором есть сырые указатели и точно такая же не инициализированная нулями память.
Именно его я предложил заменить. Т.е. я буквально сделал предложенный в статье код менее опасным, заменив указатели на Span. И при этом сохранил производительность метода на том же уровне.
Span вместе с SkipLocalsInit всё ещё безопаснее, чем сырой указатель. Инициализации нулями не будет в обоих случаях. Поэтому, возвращаясь к изначальному сообщению, что не так с предложенным мной вариантом?
Вы сначала докопались к тому, чего не было в моём сообщении, а теперь доказываете то, что и так очевидно. Ради чего вы это делаете?
Но не требует у метода unsafe, и в коде не будет указателей char* buffer. Правда, нужно разжёвывать смысл моего сообщения?
Так, а я разве сказал, что через Span будет лучше/хуже? Я сказал, что будет то же самое, но без unsafe.
Вместо unsafe в StackallocBuffer лучше использовать Span, но пропустить инициализацию нулями с помощью атрибута SkipLocalsInit. Производительность будет точно такая же.
Другие я даже не искал. Потому что использую именно GxHash, т.к. он очень быстрый. Поэтому и написал для него обертку. Уверен, что если кто-то ставил цель получить хеш массива структур, то решил эту задачу точно так же. Span используется повсеместно.
Вот, можете ознакомится с исходниками: https://github.com/viruseg/GxHash.128bits.Overloads
Всё так.
Библиотека из статьи вообще не считает хеши. О чём речь вообще, для кого плохие новости?
В моём коде нет никакого около-UB. Паддинги определяются через рефлексию здесь и сейчас. Генерация метода через IL - вполне официальный способ, а не хак. Остаётся лишь вопрос к тому, кто будет применять инструмент. Но это риторический вопрос. Я совершенно точно не в ответе за тех, кто пытается сделать подобные вещи без понимания их сути.
Я привёл пример с записью в файл. Проверка хеша, если не совпали, то идёт запись. Какая тут уязвимость может появиться? В худшем случае при несовпадении байт файл будет перезаписан, когда реальные данные не изменились. В примере с игрой в худшем случае будет выполнена тяжёлая логика лишний раз, но без проверки по хешу пришлось бы каждый кадр тратить большие ресурсы на эту же проверку. Естественно, что надо понимать, для чего используется подобный инструмент и какие опасности он несёт. Но это касается любых вещей. Очевидно, что подобный хеш не должен уйти в долговременное хранение, он предназначен для здесь и сейчас, для текущей сессии.
Если структура лежит в массиве или где-то внутри класса, JIT ничего с ней не сделает. Хеш массива помогает мгновенно понять, были ли в нём изменения. И если не было, то пропустить огромное количество логики. Полезно, например, в играх, где нужно каждый кадр пересчитывать состояние мира. Или когда нужно часто писать в файл, и вместо насилия над диском проще сравнивать хеш и писать только когда реально изменились данные.
А можно написать либу один раз и не писать ручками. Я не очень понимаю, о чём спор. Если вам не нужно, это не означает, что никому не нужно. Мы же тут программированием занимаемся. Если что-то можно автоматизировать, оно должно быть автоматизировано.
C# ли бы тоже выкинуть, где сделали что-то подобное? Или собственный код, где такой подход даёт ускорение, т.к. создаёт очень много таких структур и часто. А считать хеш нужно очень редко.
Из плюсовой dll пришел массив структур, и у них паддинги набиты мусором. А нужно посчитать хеш, например, такой либой. И тут два варианта: или копировать их в новый массив, при чём нужно будет написать логику этого копирования, используя схожие техники, описанные мной в статье. Или обнулить паддинги. Это один пример из тысяч, зачем это нужно делать.