Информация об изменениях

Сообщение Re[19]: Эльбрус мёртв, да здравствует Эльбрус-Б! от 28.05.2025 9:14

Изменено 28.05.2025 17:14 vdimas

Re[19]: Эльбрус мёртв, да здравствует Эльбрус-Б!
Здравствуйте, Sinclair, Вы писали:

V>>Или говоришь предметно, раскрывая свою мысль тезисно, или идёшь менять себе памперсы.

S>Я спорил с тезисом о том, что ускорение межпоточного обмена информацией требует "размечать память вручную".

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


S>Более того — в самом LMAX Disruptor нет никаких следов ручной разметки памяти.


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

В Disruptor операции просто над буфером байт, т.е. вся типизация происходит "в уме" — просто по смещениям читают и пишут данные как в ассемблере, когда пишут и читают в сырую нетипизированную память.

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

В общем, максимальная пропускная способность Disruptor достигается, когда курсор чтения заметно отстаёт от курсора записи, т.е. при наихудшей задержке.
Такие системы неплохо себя показывают в узких диапазонах нагрузок.
Как только нагрузка динамически скачет, в т.ч. при разделённой обработке данных "нагрузкой" в том числе считаются задержки в прикладных колбэках — система резко впадает в ступор, потом что Distruptor не может писать в очередь, он начинает вечно сидеть в CAS-цикле, замораживая колбэк в потоке Producer, т.е. в этом сценарии LMAX начинает терять входящие UDP-пакеты.

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

Учитывая, что во всём остальном косвенность при агрегировании объектов из их составных частей примерно на порядок меньше, чем в Джаве, это относительно дешевый размен.

Плюс, хорошо еще работает предвыделение "вагончиков" в их пулы свободных объектов, т.е. заведомое их последовательное расположение в более крупных кластерах памяти, которыми оперируют кеши более высоких уровней. Тот же эффект достигается в круговой очереди автоматически — это единственный её плюс. ))
Re[19]: Эльбрус мёртв, да здравствует Эльбрус-Б!
Здравствуйте, Sinclair, Вы писали:

V>>Или говоришь предметно, раскрывая свою мысль тезисно, или идёшь менять себе памперсы.

S>Я спорил с тезисом о том, что ускорение межпоточного обмена информацией требует "размечать память вручную".

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


S>Более того — в самом LMAX Disruptor нет никаких следов ручной разметки памяти.


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

В Disruptor операции просто над буфером байт, т.е. вся типизация происходит "в уме" — просто по смещениям читают и пишут данные как в ассемблере, когда пишут и читают в сырую нетипизированную память.

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

В общем, максимальная пропускная способность Disruptor достигается, когда курсор чтения заметно отстаёт от курсора записи, т.е. при наихудшей задержке.
Такие системы неплохо себя показывают в узких диапазонах нагрузок.
Как только нагрузка динамически скачет, в т.ч. при разделённой обработке данных "нагрузкой" в том числе считаются задержки в прикладных колбэках — система резко впадает в ступор, потом что Distruptor не может писать в очередь, он начинает вечно сидеть в CAS-цикле, замораживая колбэк в потоке Producer, т.е. в этом сценарии LMAX начинает терять входящие UDP-пакеты.

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

Учитывая, что во всём остальном косвенность при агрегировании объектов из их составных частей примерно на порядок меньше, чем в Джаве, это относительно дешевый размен.

Плюс, хорошо еще работает предвыделение "вагончиков" в их пулы свободных объектов, т.е. заведомое их последовательное расположение в более крупных кластерах памяти, которыми оперируют кеши более высоких уровней. Тот же эффект достигается в круговой очереди автоматически — это единственный её плюс. ))