¿Cuál es el impacto real de llamar a socket.recv con un tamaño de archivo que no es una potencia de 2?

Para leer datos de un socket en python, llame a socket.recv , que tiene esta firma:

socket.recv(bufsize[, flags])

Los documentos de python para socket.recv indican vagamente:

Nota: Para una mejor coincidencia con las realidades de hardware y red, el valor de bufsize debería ser una potencia relativamente pequeña de 2, por ejemplo, 4096.

Pregunta : ¿Qué significa ” mejor coincidencia con las realidades de hardware y de red “? ¿Cuál es el impacto real de establecer bufsize en una no-potencia de dos?

He visto muchas otras recomendaciones para hacer que esta lectura tenga una potencia de 2. También soy consciente de las razones por las cuales a menudo es útil tener longitudes de matriz como potencias de dos (operaciones de desplazamiento de bits / enmascaramiento en la longitud, tamaño de matriz FFT óptimo) , etc), pero estos son dependientes de la aplicación. Simplemente no veo la razón general para ello con socket.recv . Ciertamente, no al punto de la recomendación específica en la documentación de python. Tampoco veo ninguna potencia de dos optimizaciones en el código de Python subyacente para que sea una recomendación específica de python

Por ejemplo … si tiene un protocolo donde se conoce exactamente la longitud del paquete entrante, obviamente es preferible leer solo “a lo sumo” lo que se necesita para el paquete con el que está tratando, de lo contrario, podría comerse el siguiente paquete. Y eso sería irritante. Si el paquete que estoy procesando actualmente solo tiene 42 bytes pendientes, solo estableceré bufsize en 42.

¿Qué me estoy perdiendo? Cuando tengo que elegir un tamaño de matriz / búfer arbitrario, generalmente (¿siempre?) Hago que la longitud sea una potencia de dos, por si acaso. Este es solo un hábito desarrollado durante muchos años. ¿Son los documentos de python también una víctima de hábito?

Esto no es exclusivo de python, pero como estoy haciendo referencia específicamente a los documentos de python, lo etiquetaré como tal.


ACTUALIZACIÓN : acabo de verificar el tamaño del búfer en el nivel del kernel en mi sistema (o al menos creo que lo hice … hice cat /proc/sys/net/core/rmem_default ) y fue 124928. No es una potencia de dos. rmem_max fue 131071, claramente tampoco una potencia de dos.

Al analizar esto más, realmente no puedo ver ningún beneficio en el poder de dos recomendaciones aún. Estoy a punto de llamarlo como una recomendación falsa …

También agregué las tags tcp y C ya que también son relevantes.

Estoy bastante seguro de que el consejo de ‘poder de 2’ se basa en un error en la edición y no debe tomarse como un requisito .

Ese consejo específico se agregó a la documentación de Python 2.5 (y se adjuntó a documentos de Python 2.4.3 ), en respuesta al problema de Python # 756104 . El reportero estaba usando un tamaño de búfer excesivamente grande para socket.recv() , que socket.recv() la actualización.

Fue Tim Peters el que introdujo el concepto ‘poder de 2’:

Supongo que usted es la única persona en la historia que intenta pasar un valor tan alto a recv (); incluso si funcionara, casi seguro que se quedaría sin memoria al tratar de asignar espacio de almacenamiento intermedio de 1.9GB. los sockets son una instalación de bajo nivel, y es común pasar una potencia relativamente pequeña de 2 (para una mejor coincidencia con las realidades de hardware y red).

(Énfasis en negrita el mio). He trabajado con Tim y él tiene una gran experiencia en progtwigción y hardware de red, por lo que, en general, le tomaría su palabra al hacer un comentario como ese. Fue particularmente aficionado a la stack de Windows 95, lo llamó su canario en una mina de carbón por su capacidad de fallar bajo estrés. Pero tenga en cuenta que dice que es común , no que se requiere usar una potencia de 2.

Fue esa redacción la que luego condujo a la actualización de la documentación:

Este es un error de documentación; Algo sobre lo que el usuario debería ser “advertido”.

Esto me atrapó una vez, y dos personas diferentes preguntaron sobre esto en #python, así que tal vez deberíamos poner algo como lo siguiente en los documentos recv ().

“”
Para una mejor coincidencia con las realidades de hardware y red, el
El valor de “buffer” debería ser una potencia relativamente pequeña de 2,
por ejemplo, 4096.
“”

Si crees que la redacción es correcta, solo dame el error, yo me encargaré de ello.

Nadie cuestionó la afirmación de ‘poder de 2’ aquí, pero el editor que se movió de ella es común en el espacio de unas pocas respuestas.

Para mí, aquellos que proponen la actualización de la documentación estaban más preocupados por asegurarse de que utilizas un pequeño búfer , y no si es o no una potencia de 2. Eso no quiere decir que no sea un buen consejo ; cualquier búfer de bajo nivel que interactúe con los beneficios del núcleo con la alineación con las estructuras de datos del núcleo.

Pero aunque bien puede haber una stack esotérica donde los buffers con un tamaño que es un poder de 2 son aún más importantes, dudo que Tim Peters haya querido que su experiencia (que es una práctica común ) se emita en términos tan férreos. Simplemente ignórelo si un tamaño de búfer diferente tiene más sentido para sus casos de uso específicos.

Con respecto a: “si tiene un protocolo en el que se conoce exactamente la longitud del paquete entrante, obviamente es preferible leer solo” a lo sumo “lo que se necesita para el paquete con el que está tratando, de lo contrario podría comerse el siguiente paquete. Y eso sería irritante “.

Esto puede ser preferible para el desarrollador de la aplicación, pero probablemente sea ineficiente para la stack de red subyacente. Primero, vincula el espacio del búfer de socket que se puede usar para las E / S de red adicionales. En segundo lugar, cada recv () que realice significa sumergirse en un espacio de kernel / llamada del sistema y hay una penalización de rendimiento para la transición. Siempre es preferible obtener la mayor cantidad de datos posible del espacio del kernel y del espacio del usuario con la menor cantidad de llamadas al sistema posible y hacer el análisis del mensaje allí. Esto agrega más complejidad al código de la aplicación y al manejo de mensajes, pero es probablemente el más eficiente.

Dicho esto, dada la velocidad de los procesadores de hoy y la cantidad de memoria disponible, esto puede no ser un problema para la mayoría de las aplicaciones, pero esta fue una recomendación común para las aplicaciones de red en los “viejos tiempos”.

No estoy seguro del poder de 2 recomendaciones de una aplicación de espacio de usuario. He visto estos requisitos de tipos para los controladores debido a problemas de alineación y tamaño de página, etc., pero no está claro qué efecto tiene esto desde el espacio del usuario a menos que de alguna manera ayude a copiar datos de los buffers del kernel en buffers de usuario. Tal vez alguien con más conocimientos de desarrollo de sistemas operativos podría comentar.