О преимуществах использования socket.sendfile () при отправке байтов в удаленный сокет

В статье объясняется, почему в задачах, связанных с отправкой байтов в удаленный сокет, выгодно использовать socket.sendfile() вместо чтения с помощью read() и отправки с использованием send().

Процесс отправки байтов в удаленный сокет

Статья об Apache Kafka подсказывает, что типичный подход к отправке байтов из локального файла в удаленный сокет состоит из следующих этапов:

  1. Прочитать данные с носителя данных в кеш страницы в ОС.
  2. Скопировать данные из кеша страницы в буфер приложения.
  3. Скопировать буфер приложения в другой буфер ядра.
  4. Отправить буфер ядра в сокет.

Все это подразумевает 4 операции копирования данных и 2 системных вызова.

Реализация в Python

Если мы будем просто читать данные с помощью метода read() и потом отправлять их в сокет с использованием send(), то обеспечим 4 операции копирования и 2 системных вызова. Но в Linux и других операционных системах Unix существует API-интерфейс sendfile(), который может напрямую передавать байты из файлового канала в канал сокета. Это выгодно: можно избежать 2 операций копирования и 1 системного вызова (фактически исключить этапы 2 и 3).

Python 3.5 предоставляет высокоуровневую оболочку на основе сокетов — socket.socket.sendfile():

import socket

server_socket = socket.socket()
server_socket.bind(('localhost', 12345))
server_socket.listen(5)
while True:
    client_socket, addr = server_socket.accept()
    with open('4GB.bin', 'rb') as f:
        client_socket.sendfile(f, 0)
    client_socket.close()

Используя sendfile(), можно получить практически 2-кратный выигрыш в производительности по сравнению с методом send().


По материалам Майкла Дула.