Python 3.15+ requires C-contiguous buffers for int.from_bytes()#1706
Python 3.15+ requires C-contiguous buffers for int.from_bytes()#1706aaugustin merged 1 commit intopython-websockets:mainfrom
Conversation
|
I've realized I can add a check for sys.version to the if, so it makes the copy only for Python 3.15. Let me know what you prefer. |
|
If IMO the comment is not needed: the code is self-explanatory and a test will fail if it is removed. |
|
If you want to provide the benchmark, that will help confirm that there's no point making it version-conditional. Else, I can take it from there. Thank for you spotting the problem and proposing the solution. |
|
I'm not sure what you mean. The potential performance issue is that on Python 3.14 and older the copy is now done twice - first here in this new code and second in Python on C level. The point of version check would be in doing the copy only for Python 3.15+ when it's necessary. |
|
I benchmarked with a 1MB non-contiguous memoryview. Python 3.14 and earlier Unexpectedly, So it's actually a performance improvement to cast to bytes on Python < 3.15 too. Details>>> import timeit
...
... # 1MB of non-contiguous random data
... non_contiguous = memoryview(open('/dev/urandom', 'rb').read(2 * 1024 * 1024))[::2]
...
... t = timeit.timeit(lambda: int.from_bytes(non_contiguous), number=10)
... print(f"int.from_bytes(): {t:.3f}s")
...
... t = timeit.timeit(lambda: bytes(non_contiguous), number=10)
... print(f"bytes(): {t:.3f}s")
...
... t = timeit.timeit(lambda: int.from_bytes(bytes(non_contiguous)), number=10)
... print(f"int.from_bytes(bytes()): {t:.3f}s")
...
int.from_bytes(): 54ms
bytes(): 34ms
int.from_bytes(bytes()): 40msI'm getting the same results on Python 3.13, 3.12, and 3.11. Python 3.15 Even more unexpectedly, casting to Details>>> import timeit
...
... # 1MB of non-contiguous random data
... non_contiguous = memoryview(open('/dev/urandom', 'rb').read(2 * 1024 * 1024))[::2]
...
... # t = timeit.timeit(lambda: int.from_bytes(non_contiguous), number=10)
... # print(f"int.from_bytes(): {t * 1000:.1f}ms")
...
... t = timeit.timeit(lambda: bytes(non_contiguous), number=10)
... print(f"bytes(): {t * 1000:.1f}ms")
...
... t = timeit.timeit(lambda: int.from_bytes(bytes(non_contiguous)), number=10)
... print(f"int.from_bytes(bytes()): {t * 1000:.1f}ms")
...
bytes(): 49.2ms
int.from_bytes(bytes()): 40.3msFor completeness, I tested contiguous memoryviews and there's no significant perf change — expectedly 😅 |
|
I just realized that performance-sensitive users must be using the C implementation anyway so no big deal. |
|
Thank you for your contribution! |
As you can see here: python/cpython@510ab7d
The conversion is no longer done on CPython side. Without this change,
test_apply_mask_non_contiguous_memoryviewfails with Python 3.15.