Skip to content

Optimize non buffered protocol reads in SSLProtocol#726

Open
tarasko wants to merge 10 commits intoMagicStack:masterfrom
tarasko:optimize_ssl_nonbuffered_read
Open

Optimize non buffered protocol reads in SSLProtocol#726
tarasko wants to merge 10 commits intoMagicStack:masterfrom
tarasko:optimize_ssl_nonbuffered_read

Conversation

@tarasko
Copy link
Contributor

@tarasko tarasko commented Jan 31, 2026

Calling SSLObject.read(SSL_READ_MAX_SIZE) has a big performance issue. Internally it first allocates bytes object of SSL_READ_MAX_SIZE bytes, then reads into it, then shrinks it down to the actual number of bytes read.
Given that SSL_READ_MAX_SIZE = 256 * 1024,
we allocate 256K every time we call SSLObject.read from SSLProtocol._do_read__copied.
I've fixed it by allocating our own buffer and passing it to SSLObject.read.

I have attached perf output before and after this change.

Other changes:

  • fix cython warning: uvloop/handles/pipe.pyx:159:4: Overriding a c(p)def method with a def method
  • Use bytearray instead of raw memory allocations for the buffer that is provided to UVStream for simplicity
  • Reduce default size of read buffer from 256K to 64K. 64K is actually a default suggested size from libuv. Read buffer can still grow up to 256K if libuv wants more memory.
  • Cache _app_protocol.data_received for performance
  • Cache SSL_READ_MAX_SIZE as a python object, so that cython don't have to create a new object every time we call SSLObject.read.
  • It is ok to call SSLObject.read and pass a buffer that is smaller than the len argument. SSLObject.read will correctly read up to the buffer size, not up to len argument

Before:

30.79%     0.14%            14  python          _ssl.cpython-313-x86_64-linux-gnu.so                  [.] _ssl__SSLSocket_read
        |          
         --30.65%--_ssl__SSLSocket_read
                   |          
                   |--16.98%--PyBytes_FromStringAndSize
                   |          |          
                   |           --13.91%--PyObject_Malloc
                   |                     |          
                   |                      --13.87%--malloc
                   |                                |          
                   |                                 --13.79%--_int_malloc
                   |                                           |          
                   |                                            --13.48%--sysmalloc_mmap.isra.0
                   |                                                      |          
                   |                                                       --7.11%--__mmap
                   |          
                   |--8.66%--_PyBytes_Resize
                   |          |          
                   |           --8.50%--realloc
                   |                     |          
                   |                      --8.43%--__GI___mremap
                   |          
                    --4.37%--SSL_read_ex
                              |          
                               --4.19%--ssl3_read_internal
                                         |          
                                          --4.18%--ssl3_read_bytes
                                                    |          
                                                     --3.24%--ssl3_get_record
                                                               |          
                                                                --2.24%--tls1_enc
                                                                          |          
                                                                          |--1.12%--EVP_DecryptUpdate
                                                                          |          ossl_gcm_stream_update
                                                                          |          |          
                                                                          |           --1.02%--gcm_cipher_internal
                                                                          |                     |          
                                                                          |                      --0.81%--ossl_gcm_one_shot
                                                                          |                                |          
                                                                          |                                 --0.67%--generic_aes_gcm_cipher_update
                                                                          |          
                                                                           --0.78%--EVP_CIPHER_CTX_ctrl

After:

 8.47%     0.21%            19  python          _ssl.cpython-313-x86_64-linux-gnu.so                  [.] _ssl__SSLSocket_read
        |          
         --8.25%--_ssl__SSLSocket_read
                   |          
                   |--6.93%--SSL_read_ex
                   |          |          
                   |           --6.77%--ssl3_read_internal
                   |                     |          
                   |                      --6.68%--ssl3_read_bytes
                   |                                |          
                   |                                |--5.28%--ssl3_get_record
                   |                                |          |          
                   |                                |           --3.81%--tls1_enc
                   |                                |                     |          
                   |                                |                     |--1.92%--EVP_DecryptUpdate
                   |                                |                     |          ossl_gcm_stream_update
                   |                                |                     |          |          
                   |                                |                     |           --1.91%--gcm_cipher_internal
                   |                                |                     |                     |          
                   |                                |                     |                      --1.56%--ossl_gcm_one_shot
                   |                                |                     |                                |          
                   |                                |                     |                                 --1.26%--generic_aes_gcm_cipher_update
                   |                                |                     |                                           |          
                   |                                |                     |                                            --0.50%--CRYPTO_gcm128_decrypt_ctr32
                   |                                |                     |          
                   |                                |                      --1.24%--EVP_CIPHER_CTX_ctrl
                   |                                |          
                   |                                 --0.59%--ssl3_setup_read_buffer
                   |          
                    --0.69%--PyArg_ParseTuple
                              |          
                               --0.60%--vgetargs1_impl
@tarasko tarasko changed the title Optimize non buffered reads in SSLProtocol Jan 31, 2026
@tarasko
Copy link
Contributor Author

tarasko commented Feb 1, 2026

Hi @fantix, would you mind taking a look at this PR as well? It significantly improves SSL read performance in case of non-buffered user protocol

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

1 participant