Re[48]: MS забило на дотнет. Питону - да, сишарпу - нет?
От: Sinclair Россия https://github.com/evilguest/
Дата: 09.09.21 07:41
Оценка:
Здравствуйте, vdimas, Вы писали:

V>А как, по-твоему, возращается по-значению структура, размер которой больше ширины регистра?

Как и всё — кладётся в стек.
V>Для инфы, в этом сценарии существует т.н. return value optimization (гугл).

V>>>Получить ref или readonly ref-ссылку на поле структуры через метод самой структуры нельзя, можно только через метод-расширение, где саму структуру передавать через ref this.


V>Офигеть. ))

V>Ты только что нашёл баг компилятора:
Нда, прикольно.
V>Надо проверить в 10-м C# и зарепортить, если еще не починили.

V>В любых flexible-структурах фактическая длина известна после получения данных из некоторого АПИ.

V>Некий публичный метод или св-во могли бы возвращать Span.
Да, можно так. А можно и скрыть факт наличия спана (если мы не хотим, чтобы его ещё куда-то кастили), и просто выставлять наружу индексер.

V>1. Дотнет не гарантирует фактический порядок расположение полей, необходимо задать StructLayout хотя бы Sequential

Ну да, естественно.

V>Мне показалось, что ты хотел сделать Span как часть описания структуры.

Нет, тогда конечно же сломается Layout.


V> public Span<int> AsSpan() => MemoryMarshal.CreateSpan(ref _data0[0], Length);

Я так и использую — в тех местах, где мне нужен Span.
Вот, текущее состояние экспериментов
https://github.com/evilguest/atropos/blob/main/Atropos/BranchNode.cs#L37
К сожалению, ожидаемый инлайнинг this[] в https://github.com/evilguest/atropos/blob/main/Atropos/BranchNode.cs#L28 не происходит, несмотря на то, что T — это struct тип, точно известный в момент JIT
Поэтому с перформансом у текущей версии всё гораздо хуже, чем у предыдущей.

V>Тогда for-циклы по полному Span-у соптимизируют одну проверку за выход за диапазон, как это делается для обычных массивов.

Именно.

V>Неблокирующий вызов не означает не использование примитивов синхронзации, а там сказано:

V>

V>* Not manipulate locks or other concurrency primitives

Опять же в доке сказано, что из разных потоков работать с зокетами нельзя. Так что явно там нет никаких примитивов синхронизации.

V>На моей машине в 17 раз.

V>И у тебя там замеры вообще странные были, если по твоим замерам простой вызов выходил 7 ns.
Возможно, я там поделил неверно. В текущей версии количество внутренних итераций поправлено, так что можно просто заменять миллисекунды на наносекунды.

V>Т.е., при каждом обновлении.

Чаще

V>Выглядит так, что у тебя своеобразные представления как о возможностях AOT, так и о возможностях hot-spot оптимизаций.

У меня своеобразные представления о том, как что должно работать.

V>>>Скорее, оптимизатор заинлайнит что-то в кишках XML-сериализатора, но верхние уровни пойдут как есть.

S>>Ну, в нашем случае мы написали высокопроизводительный XML-сериализатор на $"<flightData><flightNo>{Escape(d.GetString(0))}</flightNo><departureDate>{d.GetDate(1).ToString(r)}</departureDate></flightData>"

V>Вручную писаный сериализатор будет оптимизирован и в АОТ,

Каким образом? На основании чего АОТ будет выполнять спекулятивный инлайнинг?

V>Верно, АОТ потенциально способно убирать лишнюю косвенность.

V>Например, "выпрямлять" в памяти банальный List<T>.
Что значит "выпрямлять"? Он же уже и так прямой.

V>Статистика в основном нужна для определения мест, где требуется оптимизация.

Мы ходим по кругу. Ну вот мы увидели, что у нас в горячем цикле вызывается _serializer.Serialize(dataReader, stringBuilder).
Дальше что? Это косвенный вызов по интерфейсу.

V>В рекордсете из 1001 элемента первые 1000 будут прочитаны без оптимизации.

V>Затем будет выполнена дорогостоящая оптимизация и последний элемент прочитают оптимальным образом.
Ага. Зато последующие обращения к этому же датасету будут уже оптимальны. А АОТ так и оставит косвенный вызов через VMT.

V>У тебя в "одном и том же месте" каждый раз будут разные экземпляры DataReader и потенциально хотя бы немного отличающиеся схемы, хотя бы в плане nullable-полей, особенно если со стороны базы позвали какой-нить union. И даже если схемы будут те же — это будут другие экземпляры схем, вот в чём прикол.

При чём тут экземпляры? Речь о коде методов, т.е. о содержимом VMT. И о ветвлениях.

V>В случае DataReader в нынешнем виде не прокатит — слишком большая иерархия объектов, всю её проверять на соответствие текущему хот-спот-коду будет накладней получаемых от хот-спот плюшек.

И удивительным образом, Java ухитряется сворачивать вполне себе прикладной код в приемлемый x86/x64.

V>Тогда динамически (по индексу) будет обращение только к строке такой таблицы, а столбец известен статически.

V>Тогда проверка соответствия лейаута объекта будет сводиться к проверке только ссылок на метаинформацию столбцов рекодрсета, где эти столбцы ссылаются на одни и те же статические-заготовленные конвертеры.
И даже такую конструкцию способен улучшить хотспот.

V>Т.е. сравни — проверить только равенство ссылок в объекте верхнего уровня или рекурсивно пройтись по кучерявым объектам, проверяя равенство всех полей всех дочерних элементов.

V>Счётчик сложности в последнем случае скажет "извините, в другой раз".
Вот это уже интересный вопрос. Всё зависит от того, сколько у нас реальных типов используется внутри тела цикла. Там не нужно проверять "равенство всех-всех полей".

V>"Норма" там в простейших случаях.

V>Я разбирал примеры работы джавовского хот-спота — покрывает только тривиальнейшие случаи и уже даже про них трубят как о победе.
Я настолько глубоко именно в джавовском хотспоте не разбирался — в основном читал статьи и общался с людьми, которые пилили высокопроизводительные приложения. Причём ещё в начале 2000х, задолго до GraalVM.


V>Распространённая ошибка.

V>Я показал устройство таблицы конвертеров в сообщении, на которое уже давал ссылку.
V>Читабельность и поддерживаемость (то бишь расширяемость) прекрасная.
V>А в варианте с новыми указателями на ф-ии — еще и максимально-эффективная, т.е. даже, грубо, на асме или IL быстрее не сделаешь.
Навскидку не видно, благодаря чему это будет быстрее вызова через интерфейс — ведь в реальности у нас будет не просто вызов по указателю. Сначала надо будет достать по указателю таблицу функций (рукопашный аналог VMT), и уже потом из неё выбирать слот. Впрочем, нужно профилировать.

V>ОК, про гипотетический хотспот пока спорить не буду, по крайней мере пока его нет в природе для .Net.

Да, пока что наша проблема не в том, что хотспот недостаточно хорош, а в том, что его вовсе нет. Умение выбрать места для оптимизации у JIT уже есть, но этого недостаточно — с таким уровнем умения обыграть АОТ невозможно по определению.

V>Нигде.

Вот то-то и оно. Так что вы итоге мы обсуждаем гипотетический АОТ vs гипотетичексий хотспот.
В моей идеальной картине мира они сосуществуют: то есть АОТ работает заранее, возможно, с применением PGO. Как опциональный компонент, призванный улучшить время холодного старта.
А затем в дело вступает хотспот, который докомпилирует динамический код (если такой появится), и использует рантайм статистику для дальнейших оптимизаций уже и так неплохого кода.

V>Даже существующий полноценный доступен только для iOS на основе mono (ХЗ какого он там качества).

V>А виндовые UWP-приложения на .Net Core UWP компиляются серверами магазина Windows под сетку устройств, тот код публично недоступен.
Так нам же не исходники АОТ нужны. Была бы возможность плагинов — был бы публичный API.

V>Я помню обратное — ты так и не прошёл понимание про повторно-используемые "кубики" при построении плана запроса.

Я привёл пример "плана запроса" и просил показать, какие его запчасти можно повторно использовать для альтернативных планов того же запроса, или каких-то других запросов.

V>Псевдокод давался.

Да, мной.

V>Это для динамических вещей.

V>Для статических еще в момент компиляции известны наличествующие индексы.
Для любых. Дело не в динамике, а в количестве сочетаний.
V>Поэтому, комбинаторика будет только по различиям в статистике, где для многих типов индексов, которые я обозвал enum (разновидность справочных данных эдакого системного плана, т.е. которые жестко привязаны к версии приложения) — статистика известна на момент компиляции. То бишь, кол-во уникальных значений в индексе, характер этих значений (подряд или разрежёнными "островками" и т.д.).
В реальной системе таких "индексов" пренебрежимо мало.

V>Т.е., вместо

V>
V>class SomeClass { int a, b, c; }; 
V>SomeClass c1 = new SomeClass[42];
V>

V>У них идёт:
V>
V>int[] aFields = new int[42];
V>int[] bFields = new int[42];
V>int[] cFields = new int[42];
V>

Ну, так это известный трюк, связанный не столько с хотспотом, сколько с отсутствием value-типов. А иногда — и с тем, что мы хотим получить автовекторизацию, которая не работает в случае int a; int b; int c.

V>А в самых критических случаях и вовсе:

V>
V>class SomeClassHelper {
V>    const int FIELD_COUNT = 3;
V>    const int FIELD_A_OFFSET = 0;
V>    const int FIELD_B_OFFSET = 1;
V>    const int FIELD_C_OFFSET = 2;

V>    public void write(int[] array, int index, int a, int b, int c) { 
V>        index = index * FIELD_COUNT;
V>        array[index + FIELD_A_OFFSET] = a;
V>        ...
V>} 
Это тоже известный трюк

V>int[] data = ...
V>SomeClassHelper.Write(data, index, a, b, c);
V>

V>Смысл примерно должен быть понятен.
V>А если поля разных типов (по ширине разных), то всё еще забавнее происходит над массивом байт.

V>Вытягивают её ручками, помогая хот-споту.

Ну, по честному это применяется только в том случае, если перформанс прямо очень важен, а себестоимость разработки — нет. А обычные гражданские приложения, написанные идиоматическим образом, работают приемлемо быстро.

V>Сам понимаешь, поддерживаемость ТАКОГО кода ни идёт ни в какое сравнение с предложенной мною микро-оптимизацией на основе таблицы диспетчеризации конвертеров.

Да, с этим я не спорю. Именно поэтому мне CLR нравится больше, чем JVM.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.