Pull to refresh

Comments 13

Зачем в этой статье логотипы VK?

В основном потому что мне захотелось его туда добавить.


Новое место работы, если оно нравится, обычно вызывает у людей какие-то эмоции.
Мне кажется забавный гофер в толстовке получился.


Секцию "о себе" в первый раз использовал. Возможно слегка переборщил и можно было без самого лого, одного лишь талисмана оставить.

Правильная идея. Поддерживаю.

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

И спасибо за хорошие инструменты!

Про benchrun — не слышал — но идея интересная — надо будет попробовать…

Отличное исследование и подход! За issue отдельное спасибо.

Не хватает описания решения для улучшения. Просто для полноты статьи, и потому, что это самое вкусное.


Спасибо за улучшения:)

О, об этом я как-то не подумал. Хорошее дополнение, спасибо!

Я тогда допишу в скором времени в этой же статье.

Спасибо! Примерно так и ожидал, учитывая основной юзкейс :) но, кстати, позволяет и расширять потом буде нуна.

Готово. А ещё приглашаю в канал #gocontributing в слаке, если вам там ещё нет. :)

Не могли бы вы пояснить такой момент.
Вот функция: https://github.com/recoilme/pudge/blob/master/pudge.go#L206
Там есть свитч и нелепый момент. Внутри части кейсов, создаётся переменная buf. Выглядит как будто я упорот и люблю писать одну и ту же строку 10 раз. Но дело в том, что если вынести создание buf до свича, код замедлится, даже если я не хожу в кейс, в котором юзается буф. Те компилятор не оптимизирует код разворачивая свич на несколько веток, а создаёт буфер на куче независимо от того юзал я его или нет. Я совсем не силен в компиляторах и возможно так и должно быть или это недостаток? Замедление нескщественное точно не помню, с десяток наносекунд или сотня, и обычно не стоит упарываться, дублируя код ради них, тут просто база данных, и этот код повсюду вызывался.

Лучше запускать компиляцию с флагом -gcflags='-m=2', тогда будет понятно, что происходит с аллокациями, что идёт в кучу, а что на стек:


foo.go:16:13: new(bytes.Buffer) escapes to heap
foo.go:16:13:   from buf (assigned) at foo.go:16:7
foo.go:16:13:   from buf (interface-converted) at foo.go:17:21
foo.go:16:13:   from buf (passed to call[argument escapes]) at foo.go:17:21

Даже если у вас new(bytes.Buffer) внутри case, он будет размещён на куче. Когда buf передаётся в качестве io.Writer, будет безусловная аллокация.


Вообще interface{} с производительностью слабо совместим.
Множественные case в type switch плохи как минимум тем, что туда значение пробрасывается как interface{}.


Если нужна скорость, сделайте числовые типы первыми и используйте для (u)int16/32/64 методы binary.BigEndian.PutUint16/32/64 напрямую.
Это может быть эффективнее.


Учтите, что type switch выполняет ветки последовательно, поэтому более частые типы стоит ставить первыми.
Для числовых типов вы вполне можете сделать пути выполнения без лишних аллокаций.
Слайсы, выделяемые в "buf := make([]byte, 4)", где размер заранее известен, Go спокойно размещает на стеке (но в этой функции буфер убегает через return, так что всё равно ему место в куче).


Вот пример для специализации ветки uint64: https://play.golang.org/p/1srtJDsIFIJ.
Разница в производительности:


$ go-benchrun Old New -benchmem -count=10
  Running old benchmarks:
goos: linux
goarch: amd64
BenchmarkOld-8       1000000          1402 ns/op         816 B/op         11 allocs/op
BenchmarkNew-8      50000000            24.3 ns/op         8 B/op          1 allocs/op
PASS
ok      _/<ВЦ>  12.802s
  Benchstat results:
name   old time/op    new time/op    delta
Old-8    1.42µs ± 5%    0.03µs ± 3%  -98.24%  (p=0.000 n=10+10)

name   old alloc/op   new alloc/op   delta
Old-8      816B ± 0%        8B ± 0%  -99.02%  (p=0.000 n=10+10)

name   old allocs/op  new allocs/op  delta
Old-8      11.0 ± 0%       1.0 ± 0%  -90.91%  (p=0.000 n=10+10)

Надеюсь, это является ответом на ваш вопрос.

Спасибо, за подробный ответ. Для типа []byte, сделано как вы советуете. В целом это грустно. Получается избежать кучи практически невозможно. Пойду проголосую за ваш ишью (мне нравится пользоваться функциями)

Подскажите, плиз, а чем выделение памяти на стеке лучше, чем в куче — намного быстрее?

Быстрее выделение, быстрее очистка, не увеличивает объём работы для GC.
А ещё по времени выполнения более детерменированно.


То, что в Go есть типы-значения (то, что можно использовать не по указателю), позволяет больше всего на стеке размещать, что может иногда сильно помогать.


Escape analysis правда до сих пор многие вещи не распознаёт, но у Matthew Dempsky как-то делился своим полным переписыванием этой части, может, её дорабатывать будет проще. :)

Sign up to leave a comment.

Articles