socketry / nio4r

Cross-platform asynchronous I/O primitives for scalable network clients and servers.
Other
968 stars 86 forks source link

Remove Mutex from pure Ruby and native C implementations. #202

Closed ioquatix closed 1 year ago

ioquatix commented 5 years ago

I wondered how much overhead the mutex was introducing.

Async::Wrapper (2.4.0)
Warming up --------------------------------------
Wrapper#wait_readable
                         2.671k i/100ms
    Reactor#register     2.882k i/100ms
Calculating -------------------------------------
Wrapper#wait_readable
                        198.151k (± 2.5%) i/s -    990.941k in   5.004025s
    Reactor#register    263.743k (± 2.5%) i/s -      1.320M in   5.007974s

Comparison:
    Reactor#register:   263743.0 i/s
Wrapper#wait_readable:   198151.4 i/s - 1.33x  slower

Async::Wrapper (2.3.1)
Warming up --------------------------------------
Wrapper#wait_readable
                         2.423k i/100ms
    Reactor#register     2.520k i/100ms
Calculating -------------------------------------
Wrapper#wait_readable
                        185.438k (± 3.1%) i/s -    928.009k in   5.009329s
    Reactor#register    245.313k (± 3.2%) i/s -      1.227M in   5.008085s

Comparison:
    Reactor#register:   245313.3 i/s
Wrapper#wait_readable:   185437.7 i/s - 1.32x  slower

It has about ~7% improved performance.

In real world test, it was inconclusive, which was expected.

ioquatix commented 5 years ago

@tarcieri I'm personally against merging this at this point in time, but I wonder about how you feel about this. For single threaded event reactor, there seems to be no point having mutex. All specs also pass, so clearly we are not checking this behaviour. Do we make any official guarantee about thread safety?

tarcieri commented 5 years ago

The reason the mutex is there to begin with was to provide a pessimistic baseline behavior between MRI and JRuby. Java NIO selectors are absolutely multithreaded and intended to be used from multiple threads simultaneously, and have rather complex semantics to that effect. So I threw a mutex around all implementations as the shortest path to ensuring they behave in a roughly equivalent manner.

To me it's a question of if the mutex is removed, do the semantics still match? That answer, even with the GIL, is almost certainly no, and I'm wondering if the mutex were removed from the C version if it causes memory safety issues arising from potential concurrent modification.

If it were possible to lean on the GIL for some of that thread safety, while still using a mutex on JRuby, and getting roughly equivalent behaviors that way, that sounds ok.

tarcieri commented 5 years ago

A safer option to mitigate the mutex overhead would be to add a #synchronize method that acquires the mutex and calls a block, then skips subsequent mutex acquisitions

ioquatix commented 5 years ago

It took me a while to grok what you said but it’s not a silly idea - locking it to one thread.

ioquatix commented 1 year ago

I don't feel strongly about pushing this across the finish line.