Is there a more efficient threading lock?
Michael Speer
knomenet at gmail.com
Sun Feb 26 22:19:55 EST 2023
I wanted to provide an example that your claimed atomicity is simply wrong,
but I found there is something different in the 3.10+ cpython
implementations.
I've tested the code at the bottom of this message using a few docker
python images, and it appears there is a difference starting in 3.10.0
python3.8
EXPECTED 2560000000
ACTUAL 84533137
python:3.9
EXPECTED 2560000000
ACTUAL 95311773
python:3.10 (.8)
EXPECTED 2560000000
ACTUAL 2560000000
just to see if there was a specific sub-version of 3.10 that added it
python:3.10.0
EXPECTED 2560000000
ACTUAL 2560000000
nope, from the start of 3.10 this is happening
the only difference in the bytecode I see is 3.10 adds SETUP_LOOP and
POP_BLOCK around the for loop
I don't see anything different in the long c code that I would expect would
cause this.
AFAICT the inplace add is null for longs and so should revert to the
long_add that always creates a new integer in x_add
another test
python:3.11
EXPECTED 2560000000
ACTUAL 2560000000
I'm not sure where the difference is at the moment. I didn't see anything
in the release notes given a quick glance.
I do agree that you shouldn't depend on this unless you find a written
guarantee of the behavior, as it is likely an implementation quirk of some
kind
--[code]--
import threading
UPDATES = 10000000
THREADS = 256
vv = 0
def update_x_times( xx ):
for _ in range( xx ):
global vv
vv += 1
def main():
tts = []
for _ in range( THREADS ):
tts.append( threading.Thread( target = update_x_times, args =
(UPDATES,) ) )
for tt in tts:
tt.start()
for tt in tts:
tt.join()
print( 'EXPECTED', UPDATES * THREADS )
print( 'ACTUAL ', vv )
if __name__ == '__main__':
main()
On Sun, Feb 26, 2023 at 6:35 PM Jon Ribbens via Python-list <
python-list at python.org> wrote:
> On 2023-02-26, Barry Scott <barry at barrys-emacs.org> wrote:
> > On 25/02/2023 23:45, Jon Ribbens via Python-list wrote:
> >> I think it is the case that x += 1 is atomic but foo.x += 1 is not.
> >
> > No that is not true, and has never been true.
> >
> >:>>> def x(a):
> >:... a += 1
> >:...
> >:>>>
> >:>>> dis.dis(x)
> > 1 0 RESUME 0
> >
> > 2 2 LOAD_FAST 0 (a)
> > 4 LOAD_CONST 1 (1)
> > 6 BINARY_OP 13 (+=)
> > 10 STORE_FAST 0 (a)
> > 12 LOAD_CONST 0 (None)
> > 14 RETURN_VALUE
> >:>>>
> >
> > As you can see there are 4 byte code ops executed.
> >
> > Python's eval loop can switch to another thread between any of them.
> >
> > Its is not true that the GIL provides atomic operations in python.
>
> That's oversimplifying to the point of falsehood (just as the opposite
> would be too). And: see my other reply in this thread just now - if the
> GIL isn't making "x += 1" atomic, something else is.
> --
> https://mail.python.org/mailman/listinfo/python-list
>
More information about the Python-list
mailing list