Обновить
70
nagg@Nagg

Разработчик

45
Подписчики
Отправить сообщение

Запрет обходится второй переменной: DOTNET_PreferredVectorBitWidth=512. С парой переменных W-2255 наконец показывает Count = 16, и Sum() пересаживается на zmm

если что, Vector<T> не использует avx512 исключительно в цельях совместимости со старым кодом, который мог бы полагать что под капотом либо 16 либо 32 байтовый вектор. Со временем это ограничение уберут.

PS: в LINQ особо не пытаются оптимизировать симдами всё что можно, скорее по принципу “если это немного кода и работает на всех платформах - берем”. потому что для every nanosecond matters надо брать TensorPrimitives.

У меня проф.деформация, я повидал слишком много очень плохого кода, авторы которого свято верили что делают благое дело микрооптимизируя код. в dotnet/runtime написали новую либу, 99% safe код. в одном узком месте просился Unsafe.As чтобы убрать оверхед от ковариантного каста. Угадайте из-за какого кода в итоге в этой библиотеки был официальный CVE? И это написано экспертами, теми кто все нюансы рантайма знает по долгу службы, а на деле чаще всего unsafe код пишется скучающими программистами которые борятся с imaginary scaling issues или ставят сами себе челенджы

(9) удаление проверок границ - популярная причина CVE когда девелопер думает что он самый умный оптимизируя на 2 наносекунды какой-то код от скуки

(10) тоже самое только со вкусом редковоспроизводимых багов из-за сломанной атомарности (джит мержит доступы к памяти там где это ничего не сломает сам)

(12) сериализация секретов со стека в паддинги и отправка по сети. десериализация untrusted input из массива байт - это прям хорошо известные CVE, внутри мсфт прям есть список забаненных либ которые такое делают

(13) там в статье написано что с этим не так

(16) опять ковбой-стайл удаление проверок границ, ну удачи вашему продакшну.

Дальше, на х64 спан занимает 16 байт, тогда как размер без паддинга - 12. Для единственного инстанса ок, но представим, что вы храните несколько ссылок на буферы в ref struct, которая кроме этого содержит и множество полей - вполне себе оверхед при передаче по значению.

тут вообще не понял что конкретно вы экономите на передаче спана vs byref+length. передача byref+length это как минимум - два 8 байтовых регистра

(19) “C# всё равно не позволяет обратиться к неинициализированной переменной.”

во-первых, вполне позволяет. во-вторых, неинициализированные переменные вполне классифицируются как memory safety issue в любом языке

короче у вас претензии в стиле “не мешайте мне стрелять в ногу”, а смысл статьи - криминализировать байтое*лю, как это сделано в раст, где за любой unsafe код насмехаются, а не “блин, ну ты крут конечно, понимаешь как под капотом всё работает”

как и ваш перевод статьи об ансейф коде, с которой я во многом не согласен как в части рекомендаций

даже интересно стало - с чем конкретно? :-) а то вроде исключительно база

Но что умеешь - за плечами не носить, как говорится. Лично я был бы рад, если бы в своё время имел возможность начитаться о всякого рода трюках чисто про запас - меньше пришлось бы тратить времени на “изобретения”, когда они реально понадобились.

Не понял что вы хотели сказать этим) Мой посыл в том что джит активно развивают, то что вчера надо было костылить сегодня уже работает как надо

loop unrolling много лет есть в JIT, но включается для совсем простых случаев. мало развернуть цикл, нужно еще что-то сделать с тем что получилось чтобы был какой-то профит с этого. А тут в дело вступает модель памяти которая не позволит легально что-то засимдовать в большинстве случаев.

Там, где нужно гарантированно избавиться от bounds checking - ref var, MemoryMarshal.Get*Reference + Unsafe.Add.

Это всё будет требовать unsafe {} контекст.

При желании, с их помощью можно соорудить цикл с обратным счётчиком и при этом “прямым” доступом к данным - экономим инструкции на загрузке предельного значения в регистр и сравнении счётчика с регистром.

Большинство обычного вида циклов не нуждаются в этом, jit вставит оптимальный код как есть

По поводу выравниваний - влияет очень сильно, мало того, что выравнивание это лотерея, так ещё и даже пару лишних байт в горячем цикле может как серьёзно замедлить, так и ускорить процесс.

Сильно зависит от платформы, но в целом на процентов 10-15% производительности можно расчитывать + стабилизация бенчей

tl;dr: да, это перенос того же самого на уровень рантайма, просто с возможностью инлайнить и более точно знать какие локалсы надо перетаскивать

из примера выше:

public record class Cat(string Name);
public record class Dog(string Name);

public union Pet(Cat, Dog);

где тут нет объектов?

Вы не внимательно прочитали мой комментарий. Если вы записываете гуид и читаете в потоках, то максимум что вам грозит - это прочтение гуида где только часть записалась, а остальное - нули/мусор. Возможно это приведет к ошибкам в логике программы - но это уже на совести программиста. Несинхранизированная запись юниона, в котором вы делаете Unsafe.As над shared слотом в зависимости от тэга может привести к крашу рантайма. Фактически, это UB для джита/рантайма.

Нет. Присвоение структурного поля никогда не приведет к крашу рантайма, там не будет атомарности как это и обещает модель памяти, но никаких крашей рантайма не будет. А здесь без синхронизации может возникнуть ситуация когда вызывается инстанс метод (или например поле) у объекта, которые еще не успел переключится в новый объект (или наоборот - объект уже новый, а тэг старый).

Никаким образом это не решает проблему. Представьте что у вас поле класса - юнион Pet pet. вы меняете значение этого поля с Cat на Dog. в каком порядке вы должны менять тэг и shared слот чтобы читающий это поле другой поток всё это увидел без потенциального краша рантайма?

Проблема ExplicitUnion в атомарности - вы должны и значение и тэг поменять атомарно, иначе это тайп-сефити баг потенциальный. А учитывая что больше чем 64 бита атомарно не поменять…

  • Думаю не надо объяснять что object и не-обджект в одном слоте вообще даже теоретически нельзя хранить в .NET

Имею право полагать, что до выхода Native AOT, возможности -p:PublishReadyToRun=true не было

Нет, не имеете. R2R был с самых первых версий .NET Core, до этого с нулевых (по-моему с .NET 2.0 или даже 1.1) был NGen - +/- тоже самое (чуть с другой философией, fragile). Какое-то время .NET Core даже поддерживал оба режиме - NGen и R2R.

"Появилось понятие R2R"

очень странная статья, понятно что это ИИ, но как-будто какой-то устаревший, из 2023 - смесь бесполезных фактов + откровенно неверных утверждений, аля

С выходом .NET 7.0 подход изменился. Появилось понятие R2R и Native AOT.

или

И все. Их только четыре. Больше не нужно.

наверное неверных утверждений еще больше, я вскольз глянул

развивается unsafe блок в статический анализатор как было с nullable reference types

Там скорее про то что большое кол-во public API начнут требовать unsafe{} контекст, т.к. они не безопасны. Остальное вокруг этого.

Мне было бы очень интересно посмотреть на примеры, которые стабильно эмитят tail call потестить

вот вам 2 примера https://godbolt.org/z/9ne5K5G16

Какую именно это меняет семантику и результат программы? наличие или нет StackOverflowException? это не семантика

В C# любая рекурсия (т.е. вызов функцией самой себя) требует O(n) памяти только для стека

Это не так, JIT компилятор где сможет, прекрасно расставляет тейл коллы сам (а в очень редких ситуациях может даже развернуть в цикл). tail. prefix который использует F# это больше как обязательное требование к рантайму, в духе "мне все равно как, сделай тут tail call, в случае если быстрый (имплицитный) тейл-колл нельзя вставить, джиту придется вставлять специальный хелпер колл от чего будет удар по перфу.

Я не думаю что есть хоть какие шансы на появление этого в синтаксисе C#, хз зачем вы его тут перечислили.

1
23 ...

Информация

В рейтинге
Не участвует
Зарегистрирован
Активность