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