Pull to refresh

Comments 22

Прямо какой то Go-антипатерн.


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


Если обработчик медленнее потока достаточно в Scan просто запускать новую рутину на каждую запись на обработку. Ну или складывать в канал для последующей обработки ограниченным количеством рутин.

Запускать новую рутину на каждую запись нельзя, т.к. накладные расходы на отделение потока будут огромны и больше затормозят весь процесс выборки, чем как-то помогут.
Рутины нужно запускать до процесса/цикла выборки и уже отправлять им данные через буферизированные каналы.

Попробуйте организовать пул воркеров из примера и сравнить производительность

Вы это померяли или просто так пишете?
Рутины так-то не создают потоки, а запускаются в уже готовых. Там затраты на запуск рутины чуть больше чем на вызов функции.
Я это мерял (и не только в этой задаче). В принципе, это известная тема: запуск рутины «дешевый», но если вы будете делать это тысячи раз в цикле, деградация в скорости будет колоссальной.
Кстати, вы, случайно, не путаете поток с каналом?
>у меня появилась идея выбирать данные из таблицы параллельно
Зачем? Прежде чем оптимизировать — нужно померять. И потом померять после — чтобы сравнить. Не мерял, где узкое горлышко — значит не знаешь, что улучшать. Значит идея увеличить число потоков — ни на чем не основана.
Все началось с попытки делать Scan из таблицы в отдельных гоурутинах, чтобы ускорить процесс выборки данных, но этого он делать не позволяет. А дальше я, примерно, так и сделал: замерял без доп. потоков и потом замерял уже с двумя срезами и двумя потоками, получил более быстрый результат.
И дальше уже эксперементировал с большим кол-вом потоков и срезов, чтобы понять на каком этапе дополнительные ядра процессора уже не будут играть роли (т.к. сам Scan распараллелить нельзя).
Я наверное невнятно выразился. Мерять надо не общее время — а загрузку ресурсов. Если вы упираетесь в диски на одном потоке — какой смысл в двух потоках, если диск уже 100%?

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

А у меня был небольшой кластер разработки примерно на 30 узлов по 48 ядер, не то чтобы очень много, но и довольно немало. И я тоже пытался ядра наращивать, пока не спросил админов того сервиса, который я дергал через REST — и они не показали мне график его загрузки в 100% в течение четырех часов.

Ну то есть, у меня-то ресурсов еще дохрена, я могу число потоков удесятерить практически — но упирается все равно все в единственный REST, работающий на одной хилой виртуалке с 4 ядрами и 4 гигами памяти.

Ну т.е. на вашем месте я бы померял например базу — может она уже тоже в диски уперлась?
Сначала написал медленый универсальный код (сохранения в мапу), а потом оптимизировал его через горутины
А вот если б была кодогенерация и использование стандартных бенчмарком доступных в Go из коробки, тогда б стаття была очень крутая
Так я ж сразу предупредил, что сохраняем в map, но можно в структуру или куда угодно, т.к. это не сильно важно, в данном случае, ввиду того, что даже сохранение в map происходит быстрее, чем Scan строки из таблицы.
Т.е. ускоряя сам процесс складывания данных нам ничего не даст, т.к. мы и так ждем Scan постоянно.
Именно поэтому дополнительные рутины и срезы для раскладывания данных не дают дальнейшего прироста скорости выборки.
UFO just landed and posted this here
Есть есть возможность, то можно изначально поделить данные, которые нужно выгрести на части и тогда делать выборку каждой части в своей горутине. К примеру одна выбирает с 1 по 10 строк, другая с 11 по 20 и т.д.
Это, должно быть, какие-то совсем специфичные кейсы. Т.к. с limit и offset придется тянуть order, а это нагрузит еще больше. Если же работать только с условиями к выборке, то это большая редкость, чтобы такие условия попадались в задачах, позволяющие так секционировать данные.
Т.е. Вы пытались написать нечто универсальное? Я предположил, что Вы решали некую проблему с произвотельностью совего приложения. ИМХО универсальные механизмы в большей степени оптимизированы до предела и там особо нечего ловить. А вот частные случаи более интересны.
Time spent: 1m8.022277124s — выборка результата с использованием одного среза
Time spent: 1m7.806109441s

наплели простыню плохо пахнущего кода и выигрыш около 0.2 секунды из минуты.

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

планировщик pg сам прекрасно распараллеливает, когда хочет. Возможно ваша задача решается простыми средствами SQL или небольшим изменением схемы данных или архитектуры базы
Я под оптимизацией, видимо, понимаю немного другое. Для меня оптимизация как раз заключается в более тонком тюнинге, который дает небольшой прирост скорости в одном случае, но при частом использовании этот выигрыш выливается в гораздо большее время. Тем более, я указал в каких случаях разница в скорости увеличивается, а в каких нивелируется.
Если же оптимизация дает выигрыш в 10 раз — это уже не оптимизация, а применение радикально другого подхода, например, переписать «узкий» кусок на C, да и то, если изначально код написан нормально (пусть и не самым оптимальным путем), оптимизация не может дать выигрыш в разы.
>>Если же оптимизация дает выигрыш в 10 раз — это уже не оптимизация, а применение радикально другого подхода

такие оптимизации и стоят того, что высоко-квалифицированный специалист[ы] тратили свое время на них, иначе ваше время никогда не окупится. Прагматичный инженер ПО смотрит на всю систему и оптимизирует там, где выгодно и нужно, а не выдумывает какое-то решение, подбирает проблему под решение и даже не может признать что зря тратил время увидев прирост в 2 сотых доли процента (0.0002) — намного меньше чем погрешность измерения
Надо показать коллегам, что бы запомнили как делать не надо. Это какая-то жесть… особенно бенчмарк про выигрыш в 0.2 сек
Там есть еще бенчмарк с 0.9 сек ) Я понимаю, что для вас это может быть слишком незначительно, но в моих задачах это довольно существенно.
Чуточку не понял, а почему не разрешить pg использовать несколько воркеров?
Самому pg можно разрешить несколько воркеров, но выбрав данные Scan будет последовательно сканировать строки и этот процесс в Go распараллелить, на текущий момент, нельзя.
Sign up to leave a comment.

Articles