So I've noticed that sometimes go programs run very slowly on OpenBSD. This
relates to garbage collection being exceptionally slow. The odd part is that
the CPU is idle. The problem isn't too many threads spinning around, but
rather all the threads sleeping.

If I'm not mistaken, the root cause is the loop in runtime/proc.go:sysmon().
It starts off with a short delay of 20us, slowly working up to a max of 10ms.
Alas, our kernel generally tends to give up a minimum of 20ms. Mind the us and
ms.

The effect is definitely most noticeable during garbage collection, but I
imagine there can be other circumstances in which it happens.

Some workarounds:

Reduce collections with the ballast approach.
https://blog.twitch.tv/en/2019/04/10/go-memory-ballast-how-i-learnt-to-stop-worrying-and-love-the-heap-26c2462549a2/

Either env GOMAXPROCS=1 or runtime.GOMAXPROCS(1) will eliminate the sleeps as
well, at the cost of parallelism. (The normal goroutine i/o multiplexer still
works as normal, so this may not even be noticeable.)

Running a kernel with HZ=1000 substantially reduces the length of extra
sleeps, but doesn't eliminate them entirely.

Fixes:

Somebody could rework the go scheduler to not use a wait loop. I'm not sure
why the sysmon thread doesn't or can't use normal wakeup techniques.

Somebody could rework the nanosleep syscall to permit sub tick precision.

(I'm sending this email mostly just to inform people of the workarounds. I've
looked into this issue before, thought it was specific to one situation, but I
think it's more common than that. But you may not notice, especially since it
doesn't present as 100% CPU load.)

Reply via email to