Fernando Simões в разборе эксперимента показал, как swap, область на диске для выгрузки страниц памяти, задержал программу на Go в тесте на Linux 6.8. Медианная пауза сборщика мусора, механизма освобождения ненужной памяти, составила около 51 мкс, а худшая при хранении его служебных данных на диске — 40 мс.
Причиной стала загрузка служебных данных сборщика с диска в момент, когда Go приостанавливал выполнение программы. В худшем замере 39 из 40 мс ушли на обработку 228 обращений к отсутствующим в оперативной памяти страницам.
Во время такой паузы вся программа на Go стоит и не может обработать даже результаты завершённого ввода-вывода.
Проверка утверждений:
- Fernando Simões описал эксперимент на Linux 6.8, в котором чтение выгруженных в swap служебных данных сборщика мусора Go увеличило худшую паузу до 40 мс. (подтверждено самой публикацией: доказательство; «I was wrong. While experimenting with this, I found a problem that could’ve hurt me: Go’s garbage collector reads its metadata (outside the heap, in a region that is not freed ) in a stop-the-world pause, and that metadata can be in swap. I did a mock run on a Hetzner box using kernel 6.8 with MGLRU enabled 1 1 You can find everything about these experiments: plots, the mock allocator, bpf scripts, python scripts, etc., here: https://github.com/frnsimoes/go-gc-swap-cost . The median pause was around 51 us. With the metadata on the NVMe, the worst pause was 40ms.»)
- В эксперименте медианная пауза сборщика мусора Go составила около 51 мкс. (подтверждено самой публикацией: доказательство; «The median pause was around 51 us.»)
- В худшем замере 39 из 40 мс ушли на обработку 228 обращений к отсутствующим в оперативной памяти страницам. (подтверждено самой публикацией: доказательство; «This was the worst one: 39902 us, faults during it 228, 39013 us in faults . 39 of those 40ms were spent in 228 page faults.»)
- На время такой паузы останавливается вся программа на Go, поэтому она не может обработать даже уже завершившийся ввод-вывод. (подтверждено самой публикацией: доказательство; «Those 40ms mean everything has stopped - in Go’s terminology, every P has stopped , so, for example, if a goroutine was waiting for I/O, during that pause the I/O might return and there would be no one to handle it.»)
Первоисточники:
- https://frn.sh/go-gc
- https://github.com/frnsimoes/go-gc-swap-cost/blob/main/bpf/stw.bt
- https://cs.opensource.google/go/go/+/release-branch.go1.23:src/runtime/HACKING.md;l=214
- https://cs.opensource.google/go/go/+/release-branch.go1.23:src/runtime/mgc.go;l=24
- https://cs.opensource.google/go/go/+/release-branch.go1.23:src/runtime/mgc.go;l=61
- https://cs.opensource.google/go/go/+/release-branch.go1.23:src/runtime/proc.go;l=1586
- https://elixir.bootlin.com/linux/v6.8/source/include/linux/mmzone.h
- https://elixir.bootlin.com/linux/v6.8/source/mm/memory.c
- https://elixir.bootlin.com/linux/v6.8/source/mm/page_io.c
- https://elixir.bootlin.com/linux/v6.8/source/mm/swap_state.c
- https://go.dev/blog/greenteagc
оценка 51,3 из 100 · тип: руководство