Bug description
Sometimes, when receiving data over a TCP socket, there is a delay between dataAvailable starting to return true, and the readSemaphore actually being signaled. If the wait is removed, CPU usage jumps to 100% because we are constantly querying data available, but the read actually completes faster.
To Reproduce
I don't have a clean low-level reproduction, using only Socket—I haven't really tried—but there is a simple, realistic use-case that reproduces it.
- Install Pharo-MySQL
- Connect to a MySQL server
- Time execution of a simple query—even just
[conn query: 'select "foo";'] bench "=> 6,917 iterations in 5 seconds. 1383.400 per second".
- Edit
Socket>>waitForDataFor:ifClosed:ifTimedOut: to remove the self readSemaphore waitTimeoutMSecs: (1000 min: msecsDelta - msecsElapsed) line entirely.
- Benchmark again and note a significant improvement—
8,620 iterations in 5 seconds. 1724.000 per second. Of course CPU usage will be 100% so this is not a viable workaround.
If you instead profile the execution of a large number of queries, you'll see a big chunk of time spent in that waitTimeoutMSecs: call as well.
Expected behavior
Similar or faster performance with the wait as without—the semaphore to be signaled immediately when dataAvailable would return true.
Version information:
- OS: Windows for sure, I forget if I tested in Linux as well but I think so.
- VM Version: tested in
v10.0.5 and latest v12.0.3-beta. Above results are from 12.
- Pharo Version: Tested in Pharo 11 and 14.
Expected development cost
Honestly, I'm not sure it's worth chasing down bugs like this in the plugin-based sockets implementation—this isn't the only problem, and it's much harder to maintain a plugin than code in the image. Dealing with the plugin feels likely to be a big job, and it's not something I can contribute to. I would be more in favor of replacing the plugin implementation with a pure-FFI implementation, and honestly I wouldn't be surprised if this just goes away at that point.
Bug description
Sometimes, when receiving data over a TCP socket, there is a delay between
dataAvailablestarting to returntrue, and thereadSemaphoreactually being signaled. If the wait is removed, CPU usage jumps to 100% because we are constantly querying data available, but the read actually completes faster.To Reproduce
I don't have a clean low-level reproduction, using only Socket—I haven't really tried—but there is a simple, realistic use-case that reproduces it.
[conn query: 'select "foo";'] bench "=> 6,917 iterations in 5 seconds. 1383.400 per second".Socket>>waitForDataFor:ifClosed:ifTimedOut:to remove theself readSemaphore waitTimeoutMSecs: (1000 min: msecsDelta - msecsElapsed)line entirely.8,620 iterations in 5 seconds. 1724.000 per second. Of course CPU usage will be 100% so this is not a viable workaround.If you instead profile the execution of a large number of queries, you'll see a big chunk of time spent in that
waitTimeoutMSecs:call as well.Expected behavior
Similar or faster performance with the wait as without—the semaphore to be signaled immediately when
dataAvailablewould returntrue.Version information:
v10.0.5and latestv12.0.3-beta. Above results are from 12.Expected development cost
Honestly, I'm not sure it's worth chasing down bugs like this in the plugin-based sockets implementation—this isn't the only problem, and it's much harder to maintain a plugin than code in the image. Dealing with the plugin feels likely to be a big job, and it's not something I can contribute to. I would be more in favor of replacing the plugin implementation with a pure-FFI implementation, and honestly I wouldn't be surprised if this just goes away at that point.