using the Uninitialized variant lets the GC hand back arrays without forcefully clearing them (unless they contain references, in which case it must clear at least those)
Интересно, на сколько этот эффект распространяется не в сторону увеличения скорости выполнения задачи, а в сторону уменьшения ресурсов на эту же задачу?
На милипизерных виртуалочках?
На старых компах?
На какой-нибудь встройке?
Здравствуйте, Mihas, Вы писали:
M>Здравствуйте, Serginio1, Вы писали:
M>Интересно, на сколько этот эффект распространяется не в сторону увеличения скорости выполнения задачи, а в сторону уменьшения ресурсов на эту же задачу? M>На милипизерных виртуалочках? M>На старых компах? M>На какой-нибудь встройке?
Ну там приводится размер ассеблерного кода.
Который для .Net 5 значительно меньше.
Грядет эра IoT. К нему и готовятся. Плюс развивается CoreRT
Здравствуйте, Serginio1, Вы писали: S>Думал, что 3.1 это потолок, но и в .Net 5 нехило докрутили
Да какой там потолок — там ещё копать и копать.
Та же tiered compilation — детский лепет.
Ну или вот лично меня, к примеру, оскорбляет то, что один и тот же MSIL оптимизируется по-разному, будучи зачитан в сборке с диска, и будучи запихан в динамически сгенерированный код (неважно, через dynamic method или TypeBuilder).
Это зарубает целый класс оптимизаций — вот мы берём какой-то DSL, из которого порождаем дотнетовый код на лету. Динамическое построение как раз призвано сделать его быстрым — потому что иначе можно просто нагенерировать C# код для самого общего случая. И вот мы типа делаем частичные вычисления и ручную специализацию. Вначале — на примерах типа 2*2 — всё хорошо; а потом хлоп! И порождаемый код вместо 60% времени работы неоптимизированного аналога начинает тратить 170%.
Понятно, что можно пооптимизировать порождаемый MSIL, но какого, простите, хрена?
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Mihas, Вы писали:
M>Интересно, на сколько этот эффект распространяется не в сторону увеличения скорости выполнения задачи, а в сторону уменьшения ресурсов на эту же задачу?
Какой эффект? Там по ссылке большая статья с целой кучей разнородных изменений. Какие то изменения влияют и на потребление ресурсов — устранение проверок, оптимизация GC, тип half и т.п. Примеры по ссылке есть.
Здравствуйте, Sinclair, Вы писали:
S>Здравствуйте, Serginio1, Вы писали: S>>Думал, что 3.1 это потолок, но и в .Net 5 нехило докрутили S>Да какой там потолок — там ещё копать и копать. S>Та же tiered compilation — детский лепет. S>Ну или вот лично меня, к примеру, оскорбляет то, что один и тот же MSIL оптимизируется по-разному, будучи зачитан в сборке с диска, и будучи запихан в динамически сгенерированный код (неважно, через dynamic method или TypeBuilder). S>Это зарубает целый класс оптимизаций — вот мы берём какой-то DSL, из которого порождаем дотнетовый код на лету. Динамическое построение как раз призвано сделать его быстрым — потому что иначе можно просто нагенерировать C# код для самого общего случая. И вот мы типа делаем частичные вычисления и ручную специализацию. Вначале — на примерах типа 2*2 — всё хорошо; а потом хлоп! И порождаемый код вместо 60% времени работы неоптимизированного аналога начинает тратить 170%.
А CLR или DRL за этот код отвечает? По идее CLR, но видать не все так просто...
Здравствуйте, Sinclair, Вы писали:
S>Да какой там потолок — там ещё копать и копать. S>Та же tiered compilation — детский лепет.
Вроде как и классная штука, но блин, это издевательство. Tier-0 часто не инлайнит даже простые геттеры, а к тому моменту когда наступит Tier-1 консольная утилита уже всё сделала.
Inlining Analyzer в студии тоже тоже судя по всему получает выхлоп от tier-0, и фактически врёт о возможности инлайнинга.
using the Uninitialized variant lets the GC hand back arrays without forcefully clearing them (unless they contain references, in which case it must clear at least those)
Это конечно же отлично, вот только как держали за дебилов пользователей рантайма — так и держат.
Да, сейчас, уже в 3.1 можно сделать полный не ущербный аналог List<T> (раньше ведь и того нельзя было), благодаря доступному публично System.Runtime.CompilerServices.RuntimeHelpers.IsReferenceOrContainsReferences<T>. Но, практика создавать неуниверсальные решения — по прежнему тут. Span из стандартного List? На кой она нахрен сдалась вообще, если это сделано как специальный случай? Что делать владельцам других коллекций? Т.е. пока один разрыв устранен — с т.з. API (usability, ага) — добавляем новый разрыв. Т.е. решения — костыли, а не фундаментальные решения которые работают одинаково хорошо для всех. И такого — там очень много (в плане эксплуатации знания о внутренностях, но их невозможно повторить в отдельно стоящей библиотеки, без форка рантайма). Ну ладно, там этого не так и много, но там это есть. Мне (лично) привычнее видеть когда всё "честно" (например в C++).
Со структурами это вообще прикол — теперь вообще невозможно понять когда будет создана "defensive copy" кроме как анализ IL (а ещё лучше выхлопа JIT, но для того что бы добраться до него, нужно приседаний поболее). Тем не менее всё, что произошло со структурами — это супер. Теперь, их наконец-то можно использовать как poor-man врапперы которые не стоят практически ничего. Но, с точки зрения диагностики возможных проблем — это кошмарчик. Работающих анализаторов просто на данный момент не существует (я про из коробки). Поэтому, по прежнему, ридонли предпочтительно. Введение кстати readonly-методов работы со структурами это кстати класс — позволяет делать структуру враппер как с возможностью записи так и без неё. Это, реально супер — т.к. избавляет от необходимости дублирования. У меня востребовано. Но, опять же — а почему методы класса не могут быть такими? Почему, вопреки документации get автопроперти не становятся автоматически ридонли? И наконец — а компилятор, не видит сам, кто здесь ридонли...?
Это, я к чему — работы там — непаханное болото. Очень классно, что оно активно сдвигается, но как по мне GC.AllocateUninitializedArray и подобные вещи на этом фоне — меркнут. Да, они позволяют делать легально некоторые вещи, но в то же время, ArrayPool это же самое не использует? Более того, провоцирует возвращать очищенные массивы в пул. А оно надо? Да, есть параметр. Но, это именно вывернутый вопрос наизнанку, т.к. сейчас любая библиотека может вернуть как очищенный так и не очищенный буффер в него. Это инфраструктурный фейл. Нам впарили фичу которая не нужна, и нам дали инструмент который может дать обойти фичу которая нам не нужна. Но из коробки, инструмента который бы оперировал понятным образом (т.е. поведение не варировалось бы от левой пятки имплементации сторонних библиотек) — нет, что ставит под сомнение адекватность этого API. (Т.е. если ты очень хочешь получить чистый буффер — то не должен использовать пул, или использовать свой пул, на их же основе и возвращать туда только чистые буфера... — т.е. если бы стандартный пул возвращал всегда не очищенные буфера — ничего бы не произошло, код на основании тех же конвенций при необходимости мог бы его очищать...) Да, да. Имхо, подобного там много.
Тут, я конечно в целом высыпал какой-то несвязанный негатив, прошу прощения. Вообще — в целом — я очень доволен тем, что оно развивается просто семимильными шагами, по сравнению с тем как было.
PS: Вот — жду нормальных указателей на функции. — вроде скоро должны появиться. Врапить указатели в делегаты и кешировать, это нормально и так, но если можно без этого... ух!
Здравствуйте, Mystic Artifact, Вы писали:
S>>Та же tiered compilation — детский лепет. MA> Вроде как и классная штука, но блин, это издевательство. Tier-0 часто не инлайнит даже простые геттеры, а к тому моменту когда наступит Tier-1 консольная утилита уже всё сделала.
Для консольных можно отключить, если это так важно
Спасибо. Я вроде как и вкурсе, но вчера пока баловался с COMPlus_* параметрами, не видел разницы изучая выхлоп джита. Скорее всего было поздно и я что-то напутал.
Здравствуйте, Mystic Artifact, Вы писали:
MA> Это, я к чему — работы там — непаханное болото. Очень классно, что оно активно сдвигается, но как по мне GC.AllocateUninitializedArray и подобные вещи на этом фоне — меркнут. Да, они позволяют делать легально некоторые вещи, но в то же время, ArrayPool это же самое не использует? Более того, провоцирует возвращать очищенные массивы в пул. А оно надо? Да, есть параметр. Но, это именно вывернутый вопрос наизнанку, т.к. сейчас любая библиотека может вернуть как очищенный так и не очищенный буффер в него. Это инфраструктурный фейл. Нам впарили фичу которая не нужна, и нам дали инструмент который может дать обойти фичу которая нам не нужна. ...
В .NET 5 ArrayPool уже использует GC.AllocateUninitializedArray и при возврате буфера, по умолчанию он не очищается (как и в 3.1). Надо быть аккуратнее с заявлениями.
И тем не менее, нашел что запутало
Неинициализированные массивы используются в ArrayPool.Shared, но пул возвращаемый ч/з ArrayPool.Create — на сейчас (net5.0-preview8) — использует обычное создание массива. То ли недосмотр, то ли баг.
Здравствуйте, Mystic Artifact, Вы писали:
MA>> Это, я к чему — работы там — непаханное болото. Очень классно, что оно активно сдвигается, но как по мне GC.AllocateUninitializedArray и подобные вещи на этом фоне — меркнут. Да, они позволяют делать легально некоторые вещи, но в то же время, ArrayPool это же самое не использует? Более того, провоцирует возвращать очищенные массивы в пул. А оно надо? Да, есть параметр. Но, это именно вывернутый вопрос наизнанку, т.к. сейчас любая библиотека может вернуть как очищенный так и не очищенный буффер в него. Это инфраструктурный фейл. Нам впарили фичу которая не нужна, и нам дали инструмент который может дать обойти фичу которая нам не нужна. ...
MA> В .NET 5 ArrayPool уже использует GC.AllocateUninitializedArray и при возврате буфера, по умолчанию он не очищается (как и в 3.1). Надо быть аккуратнее с заявлениями.
Здравствуйте, Codealot, Вы писали:
C>Хм. Ты сам с собой споришь?
Нет, просто даю уточнения по написанному. А так же хочу сказать, что куча мест в фреймворке не использует неинициализированные массивы ни пулы для буферов в очевидных местах. Ровно как и дефолтные реализации некоторых новых методов ради совместимости вызывают глубокое сомнение в адекватности происшедшего (напрмер Stream::Read(Span<byte>)).
Ну и если вернуться к ArrayPool — то Shared он шарится по TLS (что нихера не всегда оптимально), а ConfigurableArrayPool — не использует неинициализированные массивы, но причин на это нет.