Ada_Ru форум

Обсуждение языка Ада

Что одним аскетизм, то другим геморрой (про счётчики ссылок)

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

Сообщения

Иван Левашев
Что одним аскетизм, то другим геморрой (про счётчики ссылок)
2016-11-06 16:30:23

Здравствуйте!

 

Хотел бы поделиться своими проблемами. Полезно на будущее иметь в виду при выборе инструментария и при написании своего.

 

Использую я сейчас обычно AWS, GNATCOLL.JSON и стандартную библиотеку. AWS многопоточен, и когда открывается страница с XHR подгрузками, там неиллюзорно потоки, соответствующие разным запросам, начинают в

параллель идти. И, чтоб одно другому не мешало, использую встроенные средства многозадачности. AWS свои задачи создаёт, я ещё создаю и активирую по таймеру, через protected синхронизирую.

 

Меня озаботило то, что мне приходится постоянно смотреть, а не

рефлексировал ли случайно разработчик очередной библиотеки по поводу того, делать ли операции со счётчиками ссылок безопасными в

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

 

Вот, например, GNATCOLL.JSON делали аскеты. Это значит, что если я считал файл конфигурации в формате JSON в глобальную переменную, я не могу обращаться к ней из других потоков, потому что они там неправильно надёргают счётчик ссылок, и получится чёрти что. Сначала приходится преобразовывать в структуры либо с безопасными счётчиками ссылок, как у Unbounded_String, либо с единственными ссылками, как в контейнерах, и работать потом именно с ними, даже если мне было бы так удобно

сериализовать фрагмент JSON_Value внутрь сгенеренного скрипта.

 

Этот аскетизм делает тщетными попытки защитить JSON_Value даже при помощи protected. procedure имеют исключительный доступ, они могут и читать, и писать без проблем. А вот function могут иметь одновременный доступ с аналогичными последствиями, поэтому в protected точно также приходится переносить всё в другие форматы, с которыми только и может потом работать function. Всё в процедуры не перенесёшь, практически однопоточная программа получится, функции нужны.

 

Более строгая типизация при использовании контейнеров воспринималась как благо, поэтому с этим я ещё хоть как–то мирился.

 

В контейнерах вроде бы как счётчика ссылок нет, потому что контейнер копируется полностью, и там уникальные ссылки, но как недавно

выяснилось, аскеты нашли способ и тут нагадить. Обнаружилось, когда работал с глобальной константой–множеством строк. Возникает не сразу, но после некоторого времени после старта веб–сервера начинает возникать в каждом запросе, так и была обнаружена. Переносишь внутрь function Service — ошибка перестаёт возникать. В норме константе должно быть всё равно, глобальная она или локальная.

 

Возникает в a-cihase.adb:1154 (GNAT GPL 2015 x86_64-linux). Там внутри какая–то другая ошибка, но из Adjust/Finalize она вылетает,

превратившись в Program_Error. И что же мы там рядом видим?

 

B : Natural renames Container'Unrestricted_Access.all.HT.Busy;

...

B := B + 1;

 

Проклятье! Эти аскеты и тут достали. Вот почему–то разработчики Delphi, гораздо более неудобного для многопоточной разработки языка, не

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

 

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

 

А теперь приятная новость: я посмотрел исходники стандартной библиотеки в GNAT GPL 2016 и GNATCOLL.JSON оттуда же и был приятно удивлён, что там как раз это и починили. Об этом нигде не написали в новостях, но это так. Контейнеры в GNAT GPL более ранних версий не пригодны для

разработки многопоточных программ, но теперь ещё одна детская болезнь позади.

 

С уважением,

Левашев Иван,

Барнаул

 

--

If you want to get to the top, you have to start at the bottom

07.10.2016 2:08, Vadim Godunko vgodunko@... [ada_ru] пишет:

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

 

Здравствуйте!

 

Мне пришла в голову такая идея оптимизации:

При создании и уничтожении (если обычным чтением посмотрели и увидели только одну ссылку) барьер не использовать, так как владение

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

Дополнительно к счётчику ссылок добавить специальное поле для номера потока, изначально заполненное. Адский компилятор тяжело будет заставить так работать, но в идеале нужно избегать множественных ссылок (>=2). Для локальных переменных нужно будет реализовать limited read-only ссылку, которая убедится, что идентификатор потока заполнен и совпадает, и тогда не будет ничего делать, либо увеличит счётчик ссылок. Если не совпадает, то надо поставить барьер, стереть идентификатор потока навсегда из этого объекта и увеличить счётчик. На обратном пути каждая такая ссылка отменит своё действие, кроме стирания идентификатора потока. Чтобы не возникало путаницы, ссылки как раз и должны быть limited только для чтения, то есть, чётко на стеке и во время исполнения одного потока отгораживать внутреннюю область от внешней, иначе как понять, сколько обращений к счётчику ссылок было пропущено.

 

Возможно, при помощи дополнительных указателей в каждой ссылке

(например, указатель, при создании указывающий на ссылку, а при

копировании позволяющий что-то сделать с прежней версией ссылки по этому указателю; а также идентификатор потока, в котором была создана ссылка) примерно такое поведение получится сделать для копируемых (private) изменяемых ссылок.

 

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

инкрементировать/декрементировать в любом случае, но ставить барьер только при стёртом номере потока.

 

Сложно оценить корректность и производительность в каждом случае.

С уважением,

Левашев Иван,

Барнаул

 

--

If you want to get to the top, you have to start at the bottom

On 2016-10-06 22:30, Иван Левашев octagram@... [ada_ru] wrote:

 

 

Вот, например, GNATCOLL.JSON делали аскеты. Это значит, что если я считал файл конфигурации в формате JSON в глобальную переменную, я не могу обращаться к ней из других потоков, потому что они там неправильно надёргают счётчик ссылок,

 

От куда такая информация ?

И какую версию GNATCOLL имеете в виду ?

On 2016-10-06 22:30, Иван Левашев octagram@... [ada_ru] wrote:

 

 

Возникает в a-cihase.adb:1154 (GNAT GPL 2015 x86_64-linux).

 

Перейдите для начала на GNAT GPL 2016.

Проблема с многозадачным чтением контейнеров уже решена

наверное с год назад в GNAT pro попала ли эта поправка в GNAT GPL 2016 пока точно сказать не могу.

On 2016-10-06 23:33, Dmitriy Anisimkov wrote:

On 2016-10-06 22:30, Иван Левашев octagram@... [ada_ru] wrote:

 

 

Возникает в a-cihase.adb:1154 (GNAT GPL 2015 x86_64-linux).

 

Перейдите для начала на GNAT GPL 2016.

Проблема с многозадачным чтением контейнеров уже решена

наверное с год назад в GNAT pro попала ли эта поправка в GNAT GPL 2016 пока точно сказать не могу.

Почитал рантайм GNAT GPL 2016. Конкуррентное чтение контейнеров должно быть без проблем.

В GNAT GPL 2015 была такая проблема, когда контейнеры читались

одновременно несколькими потоками,

сбивался внутренний счетчик ссылок.

Кроме того, в 2016 появилась прагма которая может вообще выключить внутренние ссылки в контейнерах,

https://gcc.gnu.org/onlinedocs//gnat_rm/Pragma-Suppress.html

смотреть Container_Checks и Tampering_Check

тогда даже на тех платформах где не поддеживаются атомарные счетчики с конкуррентным чтением

контейнеров проблем не будет.

On 10/06/2016 06:30 PM, Иван Левашев octagram@... [ada_ru] wrote:

 

Хотел бы поделиться своими проблемами. Полезно на будущее иметь в виду

при выборе инструментария и при написании своего.

 

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

 

Вот сделали что бы контейнеры были доступны по чтению из нескольких нитей. Правильно же сделали, да? А в другом месте, в однонитевой программе это привело к тому, что более 30% времени CPU буксовал на атомарных операциях. Так что аккруатнее с желаниями, они могут сбыться ;)

07.10.2016 2:08, Vadim Godunko vgodunko@... [ada_ru] пишет:

On 10/06/2016 06:30 PM, Иван Левашев octagram@... [ada_ru] wrote:

 

Хотел бы поделиться своими проблемами. Полезно на будущее иметь в виду при выборе инструментария и при написании своего.

 

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

 

А откуда у неё цена–то такая? По MESI состояние между M и E меняется. Если состояние было не S, всё как в одном потоке. Ну переупорядочивание ещё не работает со счётчиками. Может быть, просто у LOCK ADD какие–то побочные эффекты, которые можно утрясти со временем в других

архитектурах или других опкодах?

 

Вот сделали что бы контейнеры были доступны по чтению из нескольких нитей. Правильно же сделали, да? А в другом месте, в однонитевой программе это привело к тому, что более 30% времени CPU буксовал на атомарных операциях. Так что аккруатнее с желаниями, они могут сбыться ;)

 

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

 

Eсть 100500 других библиотек контейнеров, написанных аскетами, адаистам на такое «везёт», и там много всякого другого тормозящего подрезано, так что их, наверное, и стоило бы взять, если так нужно.

 

С уважением,

Левашев Иван,

Барнаул

 

--

If you want to get to the top, you have to start at the bottom

On 2016-10-07 01:08, Vadim Godunko vgodunko@... [ada_ru] wrote:

 

 

On 10/06/2016 06:30 PM, Иван Левашев octagram@... [ada_ru]

wrote:

 

Хотел бы поделиться своими проблемами. Полезно на будущее иметь в виду при выборе инструментария и при написании своего.

 

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

 

Вот сделали что бы контейнеры были доступны по чтению из нескольких нитей. Правильно же сделали, да? А в другом месте, в однонитевой программе это привело к тому, что более 30% времени CPU буксовал на атомарных операциях. Так что аккруатнее с желаниями, они могут сбыться ;)

Хочу напомнить, что в текущем сосотоянии GNAT стандартных контейнерах есть возможность убрать счетчики совсем и сохранить конкурентное чтение.

pragma Suppress (Containers_Check);

Новое сообщение:
Страницы: 1

Чтобы оставить новое сообщение необходимо Зарегистрироваться и Войти