Обновить
67
0.1
Nagg@Nagg

Разработчик

Отправить сообщение

Я бы предложил такой:

private static string StackallocBuffer(string s)
{
    var buffer = s.Length <= 64 ? stackalloc char[64] : new char[s.Length];
    var index = 0;
    foreach (var c in s)
    {
        if (!char.IsWhiteSpace(c))
            buffer[index++] = c;
    }
    return new string(buffer[..index]);
}

Здесь нет ничего небезопасного, он не взорвется с StackOverflow на большой строке и const-sized стекаллок проинициализируется нулями примерно 2 инструкциями (через avx512 zmm) + как показывает практика, большинств строк - маленькие, да и не надо боятся эфемерных GC аллокаций, они не создают проблем для гц.

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

Указатели не единственное что является unsafe кодом в .NET. На данный момент это так, в .NET 11/12 это изменится и много текущих API cтанут unsafe + Span который инициализируется мусором со стека (т.е. с SkipLocalsInit) станет требовать unsafe блок, вы можете прочитать этот тут: https://github.com/dotnet/csharplang/blob/main/proposals/unsafe-evolution.md#stack-allocation

The stackalloc_expression is used within a member that has SkipLocalsInitAttribute applied.

Но мне кажется это и так очевидно что переменные инициализируемые мусором со стека воняют unsafe кодом.

SkipLocalsInit требует AllowUnsafeBlock, ваш код всё ещё unsafe.

вы же видите что у вас Error примерно равен разнице результатов?

Полагаю требуется некий контекст, вполне есть ситуации где можно гарантировать отсутствие как освобождения в try, так и использования после finally, а исключения остаются проблемой.

Я имел ввиду как общий совет это неверно, и уже приводило к CVE, поэтому в коде dotnet/runtime такой паттерн избегают (хотя там есть и try-finally).

Ещё finally намертво предотвращает инлайнинг,

Начиная с .NET 9.0 (или 10.0, надо проверить) это уже не так

Лучше делать это в блоке finally, чтобы гарантировать возврат даже если в процессе обработки вылетит исключение.

Это неверный совет. Лучше никогда не возращать массив в пул чем вернуть его в finally с риском use-after-free/double-free. Ничего страшного не будет, если вы не вернете массив назад в пул, это не приведет к мемори ликам. Это даже прописано в гайдлайнах к unsafe code. (Да, ArrayPool это по факту - unsafe code).

Забавно что вы написали небезопасный хэшер который считает с мусором в хеше, и потом героически решили это проблему через другую библиотеку, которая в комплект не входит :D кстати у вас там явно баг в коде https://github.com/viruseg/GxHash.128bits.Overloads/blob/master/GxHash.128bits.Overloads/Hash128Methods.cs#L190 - вместо buffer.Length должно было видимо бы asBytes.Length

Мой поинт в том что вы рекламируете это как библиотеку и у вас ни слова нет про то насколько это хрупкий/ненадежный около-UB/unsafe инструмент. Что бы вы понимали, бинарные сериализаторы, которые делают похожие хаки сериализуя объекты в байт массивы (и десереиализуя) без учета паддингов были не раз причиной CVE.

Вашу задачу невозможно решить в общем случае без около-UB люто unsafe кода (который обязательно выстрелит в виде CVE) в .NET. Как я вам уже написал: Единственное когда это возможно это для структур без падингов и блиттабл полями, но в дотнете нет АПИ через которое вы можете это проверить

  1. Если вы не используете unsafe код, вы никогда не столкнетесь с никакими багами из-за мусора в паддинга (он там может быть даже без интеропа, просто со стека)

  2. Абсолютно бессмысленнго его занулять для структур без StructLayout.Explicit, потому что JIT может легко структуру с зануллеными падингами временно куда-то скопировать на стек только полями и будет опять мусор со стека в полях, короче говоря, это UB.

  3. Никогда не пишите код который считает хэш, сравнивает структуры через memcpy/memcmp идиомы, это отличный способ выстрелить себе в ногу с гранатомета. Единственное когда это возможно это для структур без падингов и блиттабл полями, но в дотнете нет АПИ через которое вы можете это проверить (+ может быть различие на разных архитектурах/платформах)

Рекомендую к прочтению статью.

похоже GC .net останавливает потоки в основном на аккокаторах

это не так. часть методов в дотнет полностью fully interruptible и в какой момент бы поток не приостановился - он будет в сейфпоинте. в других методах используется return address hijacking т.е. любой вызов любого метода становится потенциальным сейфпоинтом.

Плюсы: никаких лишних инструкций в циклах и где либо еще, мгновенная остановка треда в fully-interruptible
Минусы: большой (по размеру) GC info (чтобы precise GC знал что сканировать на каждой инструкции fully-interruptible метода. Менее прогнозируемое время до паузы при call hijacking. необходимость сохранять и ресторить return address в стек в каждом методе

Нет. это не GC poll, вы смотрите дебажный асм (даже если запустили в Release, т.к. при отладке вижла всегда сбрасывает код в Tier0-dbg) и условный переход который вы мне показываете это

       cmp      dword ptr [(reloc 0x7ffe048bca18)], 0
       je       SHORT G_M000_IG04
G_M000_IG03:                ;; offset=0x0021
       call     CORINFO_HELP_DBG_IS_JUST_MY_CODE

Это подсказка деббагеру что код вошёл в "my code" для фичи "Debug just my code"

Ещё раз повторю - .NET не использует явные сейф поинты (как, например, Java) - у этого подхода свои плюсы/минусы.

нет никаких ассемблерных вставок для GC_POLL в .NET. Покажите мне где вот тут (sharplab) вставка?

Если вы использовали .NET чтобы рассказать как работает GC то ваша интерпретация очень далека от того как он работает - там нет GC_POLL вызовов в коде (за очень редким исключением аля pinvokes) циклах и т.п.

У регулярок есть 3 режима:

  1. Обычное интерпретирование заданных правил из строки (поведение по-умолчанию)

  2. Динамического создания нового IL метода который оптимизирует какие-то операции конкретно под заданную регулярку.

  3. Тоже самое что 2) только через Source-Gen на этапе компиляции C# кода, а не как в случае 2) - во время работы

В целом рекомендуется использовать 3ий вариант, он самый быстрый и вы не делаете лишней работы во время запуска приложения (= ускорение запуска) + дебажить просто. Есть всякие автоматические инспекции которые помогут привести обычную регулярку к нему

Как видите, никакое сохранение ни в какую переменную само по себе ничего не дает (кроме разве что сохранения аллокации 1 объекта)

Ваша реализация всё ещё проигрывает озвученному выше return value.AsSpan().IndexOfAnyExceptInRange('0', '9') == -1;

https://github.com/dotnet/runtime/pull/114675 и https://github.com/dotnet/runtime/pull/114861 - вот переезд + много пулл-реквестов после этого

он переехал. вы ожидали увидеть удаленную ветку? зачем?

Можете обновить часть "На текущий момент async2 остаётся экспериментом, доступным в runtimelab в ветке feature/async2-experiment" -- оно уже переехало в апстрим (main dotnet/runtime) и продолжается разработка уже там (довольно активно).

1
23 ...

Информация

В рейтинге
3 087-й
Зарегистрирован
Активность