From b9032ac7d6b2cebc8c403b35ce3f7bd0d97d79f6 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Fri, 20 Jun 2025 16:53:47 +0530 Subject: [PATCH 1/2] optimize perf critical socket functions with AC --- Modules/clinic/socketmodule.c.h | 172 +++++++++++++++++++++++++++++++- Modules/socketmodule.c | 149 ++++++++++++++------------- 2 files changed, 244 insertions(+), 77 deletions(-) diff --git a/Modules/clinic/socketmodule.c.h b/Modules/clinic/socketmodule.c.h index 573903be87e6ea..e21ba96ba2e5fe 100644 --- a/Modules/clinic/socketmodule.c.h +++ b/Modules/clinic/socketmodule.c.h @@ -7,7 +7,7 @@ preserve # include "pycore_runtime.h" // _Py_ID() #endif #include "pycore_long.h" // _PyLong_UInt16_Converter() -#include "pycore_modsupport.h" // _PyArg_UnpackKeywords() +#include "pycore_modsupport.h" // _PyArg_CheckPositional() PyDoc_STRVAR(_socket_socket_close__doc__, "close($self, /)\n" @@ -29,6 +29,170 @@ _socket_socket_close(PyObject *s, PyObject *Py_UNUSED(ignored)) return _socket_socket_close_impl((PySocketSockObject *)s); } +PyDoc_STRVAR(_socket_socket_send__doc__, +"send($self, data, flags=0, /)\n" +"--\n" +"\n" +"Send a data string to the socket.\n" +"\n" +"For the optional flags argument, see the Unix manual.\n" +"Return the number of bytes sent; this may be less than len(data) if the network is busy."); + +#define _SOCKET_SOCKET_SEND_METHODDEF \ + {"send", _PyCFunction_CAST(_socket_socket_send), METH_FASTCALL, _socket_socket_send__doc__}, + +static PyObject * +_socket_socket_send_impl(PySocketSockObject *s, Py_buffer *pbuf, int flags); + +static PyObject * +_socket_socket_send(PyObject *s, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + Py_buffer pbuf = {NULL, NULL}; + int flags = 0; + + if (!_PyArg_CheckPositional("send", nargs, 1, 2)) { + goto exit; + } + if (PyObject_GetBuffer(args[0], &pbuf, PyBUF_SIMPLE) != 0) { + goto exit; + } + if (nargs < 2) { + goto skip_optional; + } + flags = PyLong_AsInt(args[1]); + if (flags == -1 && PyErr_Occurred()) { + goto exit; + } +skip_optional: + return_value = _socket_socket_send_impl((PySocketSockObject *)s, &pbuf, flags); + +exit: + /* Cleanup for pbuf */ + if (pbuf.obj) { + PyBuffer_Release(&pbuf); + } + + return return_value; +} + +PyDoc_STRVAR(_socket_socket_sendall__doc__, +"sendall($self, data, flags=0, /)\n" +"--\n" +"\n" +"Send a data string to the socket.\n" +"\n" +"For the optional flags argument, see the Unix manual.\n" +"This calls send() repeatedly until all data is sent.\n" +"If an error occurs, it\'s impossible to tell how much data has been sent.\""); + +#define _SOCKET_SOCKET_SENDALL_METHODDEF \ + {"sendall", _PyCFunction_CAST(_socket_socket_sendall), METH_FASTCALL, _socket_socket_sendall__doc__}, + +static PyObject * +_socket_socket_sendall_impl(PySocketSockObject *s, Py_buffer *pbuf, + int flags); + +static PyObject * +_socket_socket_sendall(PyObject *s, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + Py_buffer pbuf = {NULL, NULL}; + int flags = 0; + + if (!_PyArg_CheckPositional("sendall", nargs, 1, 2)) { + goto exit; + } + if (PyObject_GetBuffer(args[0], &pbuf, PyBUF_SIMPLE) != 0) { + goto exit; + } + if (nargs < 2) { + goto skip_optional; + } + flags = PyLong_AsInt(args[1]); + if (flags == -1 && PyErr_Occurred()) { + goto exit; + } +skip_optional: + return_value = _socket_socket_sendall_impl((PySocketSockObject *)s, &pbuf, flags); + +exit: + /* Cleanup for pbuf */ + if (pbuf.obj) { + PyBuffer_Release(&pbuf); + } + + return return_value; +} + +#if defined(CMSG_LEN) + +PyDoc_STRVAR(_socket_socket_sendmsg__doc__, +"sendmsg($self, buffers, ancdata=, flags=0,\n" +" address=, /)\n" +"--\n" +"\n" +"Send normal and ancillary data to the socket.\n" +"\n" +"It gathering the non-ancillary data from a series of buffers\n" +"and concatenating it into a single message.\n" +"The buffers argument specifies the non-ancillary\n" +"data as an iterable of bytes-like objects (e.g. bytes objects).\n" +"The ancdata argument specifies the ancillary data (control messages)\n" +"as an iterable of zero or more tuples (cmsg_level, cmsg_type,\n" +"cmsg_data), where cmsg_level and cmsg_type are integers specifying the\n" +"protocol level and protocol-specific type respectively, and cmsg_data\n" +"is a bytes-like object holding the associated data. The flags\n" +"argument defaults to 0 and has the same meaning as for send(). If\n" +"address is supplied and not None, it sets a destination address for\n" +"the message. The return value is the number of bytes of non-ancillary\n" +"data sent."); + +#define _SOCKET_SOCKET_SENDMSG_METHODDEF \ + {"sendmsg", _PyCFunction_CAST(_socket_socket_sendmsg), METH_FASTCALL, _socket_socket_sendmsg__doc__}, + +static PyObject * +_socket_socket_sendmsg_impl(PySocketSockObject *s, PyObject *data_arg, + PyObject *cmsg_arg, int flags, + PyObject *addr_arg); + +static PyObject * +_socket_socket_sendmsg(PyObject *s, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *data_arg; + PyObject *cmsg_arg = NULL; + int flags = 0; + PyObject *addr_arg = NULL; + + if (!_PyArg_CheckPositional("sendmsg", nargs, 1, 4)) { + goto exit; + } + data_arg = args[0]; + if (nargs < 2) { + goto skip_optional; + } + cmsg_arg = args[1]; + if (nargs < 3) { + goto skip_optional; + } + flags = PyLong_AsInt(args[2]); + if (flags == -1 && PyErr_Occurred()) { + goto exit; + } + if (nargs < 4) { + goto skip_optional; + } + addr_arg = args[3]; +skip_optional: + return_value = _socket_socket_sendmsg_impl((PySocketSockObject *)s, data_arg, cmsg_arg, flags, addr_arg); + +exit: + return return_value; +} + +#endif /* defined(CMSG_LEN) */ + static int sock_initobj_impl(PySocketSockObject *self, int family, int type, int proto, PyObject *fdobj); @@ -359,6 +523,10 @@ _socket_if_indextoname(PyObject *module, PyObject *arg) #endif /* (defined(HAVE_IF_NAMEINDEX) || defined(MS_WINDOWS)) */ +#ifndef _SOCKET_SOCKET_SENDMSG_METHODDEF + #define _SOCKET_SOCKET_SENDMSG_METHODDEF +#endif /* !defined(_SOCKET_SOCKET_SENDMSG_METHODDEF) */ + #ifndef _SOCKET_INET_NTOA_METHODDEF #define _SOCKET_INET_NTOA_METHODDEF #endif /* !defined(_SOCKET_INET_NTOA_METHODDEF) */ @@ -370,4 +538,4 @@ _socket_if_indextoname(PyObject *module, PyObject *arg) #ifndef _SOCKET_IF_INDEXTONAME_METHODDEF #define _SOCKET_IF_INDEXTONAME_METHODDEF #endif /* !defined(_SOCKET_IF_INDEXTONAME_METHODDEF) */ -/*[clinic end generated code: output=07776dd21d1e3b56 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=ffebef95a4f50ef2 input=a9049054013a1b77]*/ diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 85c72779bac607..77bae27560f700 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -4592,55 +4592,63 @@ sock_send_impl(PySocketSockObject *s, void *data) return (ctx->result >= 0); } -/* s.send(data [,flags]) method */ +/*[clinic input] +_socket.socket.send + self as s: self(type="PySocketSockObject *") + data as pbuf: Py_buffer + flags: int = 0 + / + +Send a data string to the socket. + +For the optional flags argument, see the Unix manual. +Return the number of bytes sent; this may be less than len(data) if the network is busy. +[clinic start generated code]*/ static PyObject * -sock_send(PyObject *self, PyObject *args) -{ - PySocketSockObject *s = _PySocketSockObject_CAST(self); +_socket_socket_send_impl(PySocketSockObject *s, Py_buffer *pbuf, int flags) +/*[clinic end generated code: output=3ddf83f17d0c875b input=befe7d7790ccb035]*/ - int flags = 0; - Py_buffer pbuf; +{ struct sock_send ctx; - if (!PyArg_ParseTuple(args, "y*|i:send", &pbuf, &flags)) - return NULL; - if (!IS_SELECTABLE(s)) { - PyBuffer_Release(&pbuf); return select_error(); } - ctx.buf = pbuf.buf; - ctx.len = pbuf.len; + ctx.buf = pbuf->buf; + ctx.len = pbuf->len; ctx.flags = flags; if (sock_call(s, 1, sock_send_impl, &ctx) < 0) { - PyBuffer_Release(&pbuf); return NULL; } - PyBuffer_Release(&pbuf); return PyLong_FromSsize_t(ctx.result); } -PyDoc_STRVAR(send_doc, -"send(data[, flags]) -> count\n\ -\n\ -Send a data string to the socket. For the optional flags\n\ -argument, see the Unix manual. Return the number of bytes\n\ -sent; this may be less than len(data) if the network is busy."); - /* s.sendall(data [,flags]) method */ +/*[clinic input] +_socket.socket.sendall + self as s: self(type="PySocketSockObject *") + data as pbuf: Py_buffer + flags: int = 0 + / + +Send a data string to the socket. + +For the optional flags argument, see the Unix manual. +This calls send() repeatedly until all data is sent. +If an error occurs, it's impossible to tell how much data has been sent." +[clinic start generated code]*/ static PyObject * -sock_sendall(PyObject *self, PyObject *args) -{ - PySocketSockObject *s = _PySocketSockObject_CAST(self); +_socket_socket_sendall_impl(PySocketSockObject *s, Py_buffer *pbuf, + int flags) +/*[clinic end generated code: output=ec92861424d3faa8 input=47fb8b126a4f16b3]*/ +{ char *buf; Py_ssize_t len, n; - int flags = 0; - Py_buffer pbuf; struct sock_send ctx; int has_timeout = (s->sock_timeout > 0); PyTime_t timeout = s->sock_timeout; @@ -4648,13 +4656,10 @@ sock_sendall(PyObject *self, PyObject *args) int deadline_initialized = 0; PyObject *res = NULL; - if (!PyArg_ParseTuple(args, "y*|i:sendall", &pbuf, &flags)) - return NULL; - buf = pbuf.buf; - len = pbuf.len; + buf = pbuf->buf; + len = pbuf->len; if (!IS_SELECTABLE(s)) { - PyBuffer_Release(&pbuf); return select_error(); } @@ -4692,23 +4697,13 @@ sock_sendall(PyObject *self, PyObject *args) if (PyErr_CheckSignals()) goto done; } while (len > 0); - PyBuffer_Release(&pbuf); res = Py_NewRef(Py_None); done: - PyBuffer_Release(&pbuf); return res; } -PyDoc_STRVAR(sendall_doc, -"sendall(data[, flags])\n\ -\n\ -Send a data string to the socket. For the optional flags\n\ -argument, see the Unix manual. This calls send() repeatedly\n\ -until all data is sent. If an error occurs, it's impossible\n\ -to tell how much data has been sent."); - #ifdef HAVE_SENDTO struct sock_sendto { @@ -4858,10 +4853,8 @@ sock_sendmsg_iovec(PySocketSockObject *s, PyObject *data_arg, } } for (; ndatabufs < ndataparts; ndatabufs++) { - if (!PyArg_Parse(PySequence_Fast_GET_ITEM(data_fast, ndatabufs), - "y*;sendmsg() argument 1 must be an iterable of " - "bytes-like objects", - &databufs[ndatabufs])) + if (PyObject_GetBuffer(PySequence_Fast_GET_ITEM(data_fast, ndatabufs), + &databufs[ndatabufs], PyBUF_SIMPLE) < 0) goto finally; iovs[ndatabufs].iov_base = databufs[ndatabufs].buf; iovs[ndatabufs].iov_len = databufs[ndatabufs].len; @@ -4885,11 +4878,39 @@ sock_sendmsg_impl(PySocketSockObject *s, void *data) /* s.sendmsg(buffers[, ancdata[, flags[, address]]]) method */ +/*[clinic input] +_socket.socket.sendmsg + self as s: self(type="PySocketSockObject *") + buffers as data_arg: object + ancdata as cmsg_arg: object = NULL + flags: int = 0 + address as addr_arg: object = NULL + / + +Send normal and ancillary data to the socket. + +It gathering the non-ancillary data from a series of buffers +and concatenating it into a single message. +The buffers argument specifies the non-ancillary +data as an iterable of bytes-like objects (e.g. bytes objects). +The ancdata argument specifies the ancillary data (control messages) +as an iterable of zero or more tuples (cmsg_level, cmsg_type, +cmsg_data), where cmsg_level and cmsg_type are integers specifying the +protocol level and protocol-specific type respectively, and cmsg_data +is a bytes-like object holding the associated data. The flags +argument defaults to 0 and has the same meaning as for send(). If +address is supplied and not None, it sets a destination address for +the message. The return value is the number of bytes of non-ancillary +data sent. +[clinic start generated code]*/ + static PyObject * -sock_sendmsg(PyObject *self, PyObject *args) -{ - PySocketSockObject *s = _PySocketSockObject_CAST(self); +_socket_socket_sendmsg_impl(PySocketSockObject *s, PyObject *data_arg, + PyObject *cmsg_arg, int flags, + PyObject *addr_arg) +/*[clinic end generated code: output=3b4cb1110644ce39 input=479c13d90bd2f88b]*/ +{ Py_ssize_t i, ndatabufs = 0, ncmsgs, ncmsgbufs = 0; Py_buffer *databufs = NULL; sock_addr_t addrbuf; @@ -4901,16 +4922,10 @@ sock_sendmsg(PyObject *self, PyObject *args) } *cmsgs = NULL; void *controlbuf = NULL; size_t controllen, controllen_last; - int addrlen, flags = 0; - PyObject *data_arg, *cmsg_arg = NULL, *addr_arg = NULL, - *cmsg_fast = NULL, *retval = NULL; + int addrlen; + PyObject *cmsg_fast = NULL, *retval = NULL; struct sock_sendmsg ctx; - if (!PyArg_ParseTuple(args, "O|OiO:sendmsg", - &data_arg, &cmsg_arg, &flags, &addr_arg)) { - return NULL; - } - memset(&msg, 0, sizeof(msg)); /* Parse destination address. */ @@ -5072,22 +5087,6 @@ sock_sendmsg(PyObject *self, PyObject *args) return retval; } -PyDoc_STRVAR(sendmsg_doc, -"sendmsg(buffers[, ancdata[, flags[, address]]]) -> count\n\ -\n\ -Send normal and ancillary data to the socket, gathering the\n\ -non-ancillary data from a series of buffers and concatenating it into\n\ -a single message. The buffers argument specifies the non-ancillary\n\ -data as an iterable of bytes-like objects (e.g. bytes objects).\n\ -The ancdata argument specifies the ancillary data (control messages)\n\ -as an iterable of zero or more tuples (cmsg_level, cmsg_type,\n\ -cmsg_data), where cmsg_level and cmsg_type are integers specifying the\n\ -protocol level and protocol-specific type respectively, and cmsg_data\n\ -is a bytes-like object holding the associated data. The flags\n\ -argument defaults to 0 and has the same meaning as for send(). If\n\ -address is supplied and not None, it sets a destination address for\n\ -the message. The return value is the number of bytes of non-ancillary\n\ -data sent."); #endif /* CMSG_LEN */ #ifdef HAVE_SOCKADDR_ALG @@ -5424,8 +5423,8 @@ static PyMethodDef sock_methods[] = { recvfrom_into_doc }, #endif - {"send", sock_send, METH_VARARGS, send_doc}, - {"sendall", sock_sendall, METH_VARARGS, sendall_doc}, + _SOCKET_SOCKET_SEND_METHODDEF + _SOCKET_SOCKET_SENDALL_METHODDEF #ifdef HAVE_SENDTO {"sendto", sock_sendto, METH_VARARGS, sendto_doc}, #endif @@ -5445,7 +5444,7 @@ static PyMethodDef sock_methods[] = { #ifdef CMSG_LEN {"recvmsg", sock_recvmsg, METH_VARARGS, recvmsg_doc}, {"recvmsg_into", sock_recvmsg_into, METH_VARARGS, recvmsg_into_doc}, - {"sendmsg", sock_sendmsg, METH_VARARGS, sendmsg_doc}, + _SOCKET_SOCKET_SENDMSG_METHODDEF #endif #ifdef HAVE_SOCKADDR_ALG { From 53c6f8bb9b02fe6f28abf8c62b7ee6dff27a3915 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Fri, 20 Jun 2025 17:05:49 +0530 Subject: [PATCH 2/2] fix comments --- Modules/clinic/socketmodule.c.h | 4 ++-- Modules/socketmodule.c | 7 ++----- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/Modules/clinic/socketmodule.c.h b/Modules/clinic/socketmodule.c.h index e21ba96ba2e5fe..0cedab597db714 100644 --- a/Modules/clinic/socketmodule.c.h +++ b/Modules/clinic/socketmodule.c.h @@ -84,7 +84,7 @@ PyDoc_STRVAR(_socket_socket_sendall__doc__, "\n" "For the optional flags argument, see the Unix manual.\n" "This calls send() repeatedly until all data is sent.\n" -"If an error occurs, it\'s impossible to tell how much data has been sent.\""); +"If an error occurs, it\'s impossible to tell how much data has been sent."); #define _SOCKET_SOCKET_SENDALL_METHODDEF \ {"sendall", _PyCFunction_CAST(_socket_socket_sendall), METH_FASTCALL, _socket_socket_sendall__doc__}, @@ -538,4 +538,4 @@ _socket_if_indextoname(PyObject *module, PyObject *arg) #ifndef _SOCKET_IF_INDEXTONAME_METHODDEF #define _SOCKET_IF_INDEXTONAME_METHODDEF #endif /* !defined(_SOCKET_IF_INDEXTONAME_METHODDEF) */ -/*[clinic end generated code: output=ffebef95a4f50ef2 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=0376c46b76ae2bce input=a9049054013a1b77]*/ diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 77bae27560f700..f3ad01854de93b 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -4626,7 +4626,6 @@ _socket_socket_send_impl(PySocketSockObject *s, Py_buffer *pbuf, int flags) } -/* s.sendall(data [,flags]) method */ /*[clinic input] _socket.socket.sendall self as s: self(type="PySocketSockObject *") @@ -4638,13 +4637,13 @@ Send a data string to the socket. For the optional flags argument, see the Unix manual. This calls send() repeatedly until all data is sent. -If an error occurs, it's impossible to tell how much data has been sent." +If an error occurs, it's impossible to tell how much data has been sent. [clinic start generated code]*/ static PyObject * _socket_socket_sendall_impl(PySocketSockObject *s, Py_buffer *pbuf, int flags) -/*[clinic end generated code: output=ec92861424d3faa8 input=47fb8b126a4f16b3]*/ +/*[clinic end generated code: output=ec92861424d3faa8 input=732b15b9ca64dce6]*/ { char *buf; @@ -4876,8 +4875,6 @@ sock_sendmsg_impl(PySocketSockObject *s, void *data) return (ctx->result >= 0); } -/* s.sendmsg(buffers[, ancdata[, flags[, address]]]) method */ - /*[clinic input] _socket.socket.sendmsg self as s: self(type="PySocketSockObject *")