Присоединяйтесь к моему каналу в Telegram: t.me/vladimir_balun_programming Делюсь там интересными активностями, мыслями, новостями и материалами по программированию, а также рассказываю о том, что читаю и изучаю по программированию и не только!
Владимир, как обычно, топ-темы рассматриваешь. Очень хочется увидеть и поработать с такими необычными темами и на твоем курсе по concurreny в go. За данный видос огромный респект!
Какие вы ресурсы изучали чтобы знать тонкости всего процессора и библиотек языка? Вы настолько детально все изучили, что как будто бы вы с другой планеты) Было очень интересно!
Очень известная штука в профессиональных c++ кругах. Если вместо sequential consistency бахнуть acquire-release семантику, вы ускорите ещё раза в 3-4) Но в го отказались от такого для простоты (см. go memory model). Очень подойдёт тем, кто занимается разработкой чего-то низкоуровнего, я сам был в шоке когда увидел в первый раз такие оптимизации)
Спасибо! Правда нет комментария, почему переход на атомики дал прирост в два раза. Просто потому что мьютексы медленнее атомиков? И по итогу нужно ли проводить шардирование для такой оптимизации, или достаточно просто выравнивания?
Это решение будет ускорять в 10 раз и при наличии другой работы в коде? Помимо инкрементирования счетчика, приложение делает ещё что-то и переменные тоже могут попадать в кэш. Возможно не очень понял, но мы как будто затрем весь кэш при инкрементироварии счётчика таким способом и это замедлит код вокруг
Это если счётчик будет разбит на 1024 shard'а для 64K cache'а L1 или на 4096 shard'а для 256К. Пока на shard'ы тратится малый процент cache'а, такой опасности нет.
А какие минусы у такой оптимизации? Получается, что мы резервируем себе участок памяти кэша в котором полезных данных 4 байта, а всё остальное не используется никак? Как будто это оптимизация будет сильно отъедать память, когда количество горутин будет значительно больше, чем число ядер, и тогда относительно не быстрая зашаренные данные, будут эффективнее по памяти, но медленнее исполняться. Вопрос какой баланс в итоге лучший, всегда по ситуации?
Нуу, 64 байта это очень немного. L1 кеш на большинстве современных процессоров имеет объем 32kb, соответственно туда поместится 512 таких переменных на одно ядро, но насолько я понимаю при работе в одном потоке процессор не будет загружать в себя данные всех переменных, хотя это под вопросом. Я думаю если протестировать данный лайфхак на большом количестве корутин, то получится что он все равно будет значительно быстрее, чем без этого приема. Хотя не уверен, надо бы протестировать.
Так выравнивание получается как бы "растягивает" счётчик на какое-то кол-во байт, хотя по факту их не использует, чтоб другой счётчик гарантировано попал в другую кэш линию ?
А если у нас куча других задач помимо счетчика, ? Могу быть не прав, но это звучит так как будто в вакууме это финт ушами уровня магистра, но если взять реальный сервис, который, условно, съедает 70% ресурсов машины, ? Или еще лучше рассмотреть ситуацию , когда запросов настолько много, что условный сервис должен ?
В самом начале у нас вроде как у каждого ядра свой кэш, а после последней версии у всех ядер один кэш(-линия)? Или это разные кэши? В целом прикольная оптимизации, но насколько понял, ее эффективность зависит от конкретной архитектуры/модели процоессора, т.е. где-то можно не сработать. Хотя, если дело доходит до такой низкоуровневой ерунды, то наверное заранее известно, на каких процессорах будет запускаться код в проде )
добрый вечер, а есть ли публичный репозиторий с примером данного кода для более внимательного изучения? в записанном видео быстрое перемещение по коду и тем речи высокий, не всегда удобно для моментального восприятия
Я так понял у Вас intel. Вот не могу, сколько не пробую, на M1 хак повторить, максимальной производительности добиваюсь при атомиках и дальше никак. Не получилось нагуглить как с кэш линией работает М1 Pro. Если у кого-то есть инфа, буду благодарен, потому что интересно повторить выравнивание на своем компе.
Как ты вообще додумался до этого? Мозг капитальный. Постфактум, конечно, кажется очевидным, но изначально к этой мысли бы никогда в жизни не пришел самостоятельно
Забавно, возможно я что-то делаю ни так, но у меня все варианты не сильно друг от друга отличаются по производительности: BenchmarkAtomicCounter-10 10 1538 ns/op BenchmarkMutexCounter-10 10 412.5 ns/op BenchmarkRWMutexCounter-10 10 658.3 ns/op BenchmarkShardedAtomicCounter-10 10 570.9 ns/op BenchmarkAtomicCounterOptimize-10 10 775.0 ns/op
я тоже попробовал проделать всё тоже самое. ShardedAtomic c alignment не сильно ушёл от Atomic и ShardedAtomic. 349.7 ns/op, 455.7 ns/op, 385.9 ns/op соответственно.
Присоединяйтесь к моему каналу в Telegram: t.me/vladimir_balun_programming
Делюсь там интересными активностями, мыслями, новостями и материалами по программированию, а также рассказываю о том, что читаю и изучаю по программированию и не только!
Данная логика с оптимизацией хорошо рассписана в книжке «100 ошибок Go и как их избежать» в последней главе.
Спасибо, я не дочитал
Ну брат ну ты даёшь это всё очень круто. Думаю всем будет уроком немножко процессор и память изучить.
Спасибо!
Крайне интересная информация, спасибо! Коротко и очень необычно) Спасибо!
Спасибо!
Владимир, как обычно, топ-темы рассматриваешь. Очень хочется увидеть и поработать с такими необычными темами и на твоем курсе по concurreny в go. За данный видос огромный респект!
Спасибо, курс как раз будет 2 мая - там в том числе и это разбирается)
Вова, ты крут, очень понравилась данная рубрика, спасибо!)
Спасибо!
Супер! Респект и удачи тебе!
Спасибо!
Млин, Вов, как всегда - огонь!!!
Спасибо!
Спасибо за крутой лайфхак! Полезно 😊
Спасибо!
Приятненько. Это похоже как и на порядок полей в структурах
Круто! Вы фокусник! ))
Да, Балун реально шарит, мощь. Нетривиальный ролик. И классный пример того как понимание подкапотной работы помогает реально оптимизировать код...
Пушка. Наконец-то я понял зачем нужно знать устройство памяти и всего прочего, прям конкретный кейс, спасибо)
Нужно не всегда, но иногда эти знания бывают очень полезными)
Очень интересно! спасибо
Спасибо!
Спасибо, очень интересное видео!
Спасибо!
Очень круто, спасибо
чудеса на виражах прям какие-то)
Магия)
alignment это выравнивание , offset смещение)
годный контент
Все так)
Если уж придираться к неймингу, то такой заполнитель обычно называют padding.
это называется wet blanket) а если серьезно, то везде используется именно field alignment. учите матчасть!)
Какие вы ресурсы изучали чтобы знать тонкости всего процессора и библиотек языка? Вы настолько детально все изучили, что как будто бы вы с другой планеты) Было очень интересно!
Я не знаю очень много всего все еще - а то, что знаю, просто последовательный путь изучения тем, которые постепенно друг с другом переплетаются)
Очень известная штука в профессиональных c++ кругах. Если вместо sequential consistency бахнуть acquire-release семантику, вы ускорите ещё раза в 3-4) Но в го отказались от такого для простоты (см. go memory model).
Очень подойдёт тем, кто занимается разработкой чего-то низкоуровнего, я сам был в шоке когда увидел в первый раз такие оптимизации)
Выравниванием мы увеличиваем размер счетчика чтобы он не влезал в одну линию шарда? А если таких счетчиков множество на памяти же отразится?
Спасибо! Правда нет комментария, почему переход на атомики дал прирост в два раза. Просто потому что мьютексы медленнее атомиков? И по итогу нужно ли проводить шардирование для такой оптимизации, или достаточно просто выравнивания?
На превьюшку поставить:
_ [60]byte
↖
Это увеличит скорость кода в 10 раз
Круто! Не думал попробовать решить 1 billion row challenge на golang?
Это решение будет ускорять в 10 раз и при наличии другой работы в коде? Помимо инкрементирования счетчика, приложение делает ещё что-то и переменные тоже могут попадать в кэш. Возможно не очень понял, но мы как будто затрем весь кэш при инкрементироварии счётчика таким способом и это замедлит код вокруг
Это если счётчик будет разбит на 1024 shard'а для 64K cache'а L1 или на 4096 shard'а для 256К.
Пока на shard'ы тратится малый процент cache'а, такой опасности нет.
А какие минусы у такой оптимизации? Получается, что мы резервируем себе участок памяти кэша в котором полезных данных 4 байта, а всё остальное не используется никак? Как будто это оптимизация будет сильно отъедать память, когда количество горутин будет значительно больше, чем число ядер, и тогда относительно не быстрая зашаренные данные, будут эффективнее по памяти, но медленнее исполняться. Вопрос какой баланс в итоге лучший, всегда по ситуации?
Нуу, 64 байта это очень немного. L1 кеш на большинстве современных процессоров имеет объем 32kb, соответственно туда поместится 512 таких переменных на одно ядро, но насолько я понимаю при работе в одном потоке процессор не будет загружать в себя данные всех переменных, хотя это под вопросом. Я думаю если протестировать данный лайфхак на большом количестве корутин, то получится что он все равно будет значительно быстрее, чем без этого приема. Хотя не уверен, надо бы протестировать.
Так выравнивание получается как бы "растягивает" счётчик на какое-то кол-во байт, хотя по факту их не использует, чтоб другой счётчик гарантировано попал в другую кэш линию ?
Привет. Не подскажешь какой у тебя доп. монитор стоит?
ШИКАААРРРРРРНООО!!! брат давай такие видео по всем темам и ты ТОП! такой шикраный контент и на русском
Прикольно ) Красавчик! ;)
Осталось только коммент в коде написать, чтобы другой программист не грохнул неиспользуемую память)
Хорошее замечание)
так регресс и появляется
Желательно выравнивать к длине кешлинии архитектуры на которой запускается код, иначе оптимизации не будет, т.к значения будут затирать друг друга.
почему цикл не в самом начале функции benchmark?
for j := 0; j < b.N; j++ {
А если у нас куча других задач помимо счетчика, ? Могу быть не прав, но это звучит так как будто в вакууме это финт ушами уровня магистра, но если взять реальный сервис, который, условно, съедает 70% ресурсов машины, ? Или еще лучше рассмотреть ситуацию , когда запросов настолько много, что условный сервис должен ?
Можно исходники?
В самом начале у нас вроде как у каждого ядра свой кэш, а после последней версии у всех ядер один кэш(-линия)? Или это разные кэши?
В целом прикольная оптимизации, но насколько понял, ее эффективность зависит от конкретной архитектуры/модели процоессора, т.е. где-то можно не сработать. Хотя, если дело доходит до такой низкоуровневой ерунды, то наверное заранее известно, на каких процессорах будет запускаться код в проде )
ниче не понятно, но очень интересно
Попробуйте почитать мой большой комментарий-ответ другому собеседнику ниже про 64-байтные блоки адресного пространства.
Возможно, что-то прояснится.
добрый вечер, а есть ли публичный репозиторий с примером данного кода для более внимательного изучения? в записанном видео быстрое перемещение по коду и тем речи высокий, не всегда удобно для моментального восприятия
Есть курс по concurrency МФТИ , можешь посмотреть его , там 20+ часов :)
@@КонстантинСердюк-ь5ю а ссыль можно, пожалуйста?
Я так понял у Вас intel. Вот не могу, сколько не пробую, на M1 хак повторить, максимальной производительности добиваюсь при атомиках и дальше никак.
Не получилось нагуглить как с кэш линией работает М1 Pro. Если у кого-то есть инфа, буду благодарен, потому что интересно повторить выравнивание на своем компе.
Ты, наверно, просто тест с атомиками запускал. А надо шардированный тест со смещением структуры AtomicCounter
Коротко о том как увеличить использование памяти приложения в 16 раз )
@@doingwell5629 я имел ввиду в структуре 🙂
@@НиколайВикторович-х3г это почти всегда компромисс - что-то получаем, чем-то жертвуя при этом
Подскажите , а на плюсах такие проблемы возникают?
Это актуально для всех языков, так как проблема завязана на принципах работы cpu
@@dmikoss плюсую)
Жаль что go не оптимизирует это сам на этапе компиляции
Как ты вообще додумался до этого? Мозг капитальный. Постфактум, конечно, кажется очевидным, но изначально к этой мысли бы никогда в жизни не пришел самостоятельно
зачем мьютекс на чтение? Какаю то ересь прочитать невозможно, либо текущее значение, либо текущее + 1
Цыганские фокусы
Забавно, возможно я что-то делаю ни так, но у меня все варианты не сильно друг от друга отличаются по производительности:
BenchmarkAtomicCounter-10 10 1538 ns/op
BenchmarkMutexCounter-10 10 412.5 ns/op
BenchmarkRWMutexCounter-10 10 658.3 ns/op
BenchmarkShardedAtomicCounter-10 10 570.9 ns/op
BenchmarkAtomicCounterOptimize-10 10 775.0 ns/op
я тоже попробовал проделать всё тоже самое. ShardedAtomic c alignment не сильно ушёл от Atomic и ShardedAtomic. 349.7 ns/op, 455.7 ns/op, 385.9 ns/op соответственно.
А может различаются размеры этих кэш-линий на разных процах? Но это я просто предположил, если так то смысла мало от этой оптимизации