Reducing number of threads created

I am looking to reduce the number of threads that Syncthing uses. I want to reduce overhead on smaller devices (routers and NAS devices). I have seen this discussion which speaks about using the GOMAXPROCS environment variable. Before setting GOMAXPROCS Syncthing was creating 2 processes and 18 threads (20 total execution contexts). After setting it to one Syncthing produces 2 processes and 10 threads (total 12 execution contexts).

I am looking to further reduce this. What mechanisms are in place to manage execution contexts?

You can’t really control it as much as you’d like. I think GOMAXPROC controls number of threads used by the runtime, but if I understand correctly Go in its async nature will spawn a thread for any blocking operation.

It’s fine for networking, but storage IO is always blocking, hence always spawns a thread.

You can probably tweak concurrency/buffer size based settings in syncthing in hopes that it does less IO.

Also, I am not sure why you are targeting thread count. They are quite cheap these days, and their count does not necessarily translate to CPU usage.

But yes, syncthing CPU heavy due to cryptographic hashing, encryption and so on, and there is no way around this.

1 Like

The 12 execution contexts I just mentioned were on a new router I just set up. It has only a single connection (to the Syncthing hub). I just looked on my home’s main router one and it has 64 threads. It’s quite a sight to pull up on htop.

Part of the issue is that Syncthing runs on devices that are using kernels which are more than ten years old and execution contexts aren’t as lightweight there as they are with modern kernels. Even on the best of kernels, though, cheap is not free. And when, say, a single file changes and needs to go out to 15 different machines, it’s not one process waking up and working on it, suddenly its 30 threads all waking up at the same time.

If I may coin an analogy, it’s like going to the grocery store with 15 items in your cart, then when it’s time to pay calling up 14 of your friends to all come and each take one item to a different checkout. You personally get through faster, but it’s not playing nicely with others in line while you’re doing it.

[quote=“AudriusButkevicius, post:2, topic:17825”] if I understand correctly Go in its async nature will spawn a thread for any blocking operation.[/quote]

That just makes me sigh. That, of course, isn’t on you, it’s on Go, but threads are objectively the wrong construct to use for for asynchronous IO. Threads are for concurrency, not asynchronicity. One thread and judicious use of select() can handle almost unlimited asynchronous IO.

1 Like

Yes, go uses select for network operations, as nics have the ability to interrupt, but as far as I am aware, nothing like that exists for storage, hence async file io is a myth.

Yes, there are non-blocking reads, but those reads just a fake facade for a blocked thread in the kernel.

Some old, but I think still true (Maybe? Stopped following go advancements), discussion on the subject: https://groups.google.com/g/golang-nuts/c/FI1NIsZK1hA?pli=1

Also, perhaps my general understanding of all of this is wrong.

1 Like

On the other hand, 10 years old, would be linux 3.x era, where thread were already cheap, so perhaps you are talking about 20 years. I guess I’m surprised it even runs on something that old.

We have some max concurrent io knobs, turning down those might prevent the creation of some threads some of the time. That and folder concurrency.

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.