php / php/php-src

Increase FILLUNIT (currently 5kB) in main/rfc1867.c otherwise large uploads are inefficients and slow

Abierto
#13,189 1 comentario 2 reacciones 0 asignados Ver en GitHub

Nadie ha tomado este issue todavía.

Feature Status: Needs Triage
Lenguaje dominante
C
Estrellas
40.4k
Forks
8.2k
Merge medio
2 d 13 h
PR fusionados (30 d)
96

Descripción

Description

I recently stumbled upon a very simple file upload code which was quite slow. It turned out that it was IO-limited while writing the temporary file to the disk. A strace showed that PHP would spoonfeed the incoming network data to the disk :

16:51:04 read(6, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 4142) = 4142
16:51:04 read(6, "\1\5\0\1 \0\0\0", 8)  = 8
16:51:04 read(6, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 977) = 977
16:51:04 write(8, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 5119) = 5119
16:51:04 read(6, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 5119) = 5119
16:51:04 write(8, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 5119) = 5119
16:51:04 read(6, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 2096) = 2096
16:51:04 read(6, "\1\5\0\1 \0\0\0", 8)  = 8
16:51:04 read(6, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 3023) = 3023
16:51:04 write(8, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 5119) = 5119

On the network/read side this is not very efficient, it uses a lot of syscalls (especially since there is a recurring 8-byte read() call). On my experiment (a n1-standard-8 GCE VM), a single process would use 50% CPU (on the system side, not the user one) with an upload proceeding at 15 MB/s.

On the disk/write side this is also inefficient, and runs quickly in real cloud limits : in my case a 100G "SSD" disk which GCE advertises as 48 MB/s and 3000 IO/s max; my benchmarks with fio confirmed those figures.

The problem with such a small FILLUNIT=5kB buffer, is that you can't get a better throughput than 3000*5 kB/s =15 MB/s. So you don't get the max througput of your disk, you top on IOPS first.

Of course that is amplified by the fact that is a VM and uses - as recommended by Google - the 'noop' IO scheduler and there is no write coalescing. I guess an IO scheduler could mitigate this on a bare-metal local storage. But I think running PHP on such a cloud VM (8 vCPUs/ 32GB RAM/ 100G "SSD") is very common. And the performance drop (and global pressure on the server storage) is very noticeable.

According to https://github.com/php/php-src/commit/44b68122c2ba0cf07cd837af160b3bd01527d081 FILLUNIT has been 5 kB for 23 years.

I would suggest it is time to bump it up. I would suggest at least 64 kB, GCE suggests 256 kB (https://cloud.google.com/compute/docs/disks/optimizing-pd-performance). Only one instance of this buffer is used at once, during the file upload loop (thus per-process), and can be released as soon as the loop is done.

Note that the value is used to allocate a second transient buffer on the stack in multipart_buffer_read_body() - and that might abuse the stack (which defaults to 1MB on Windows and 8MB on Linux if I'm not mistaken).

PHP Version

PHP 8.1.27

Operating System

Debian 10

Guía de contribución

Abrir la guía de contribución

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Línea de trabajo

Comienza en main/rfc1867.c localizando FILLUNIT y multipart_buffer_read_body(), y después revisa cómo se asignan los tamaños de búfer durante las subidas. Usa las mediciones reportadas de strace y fio como contexto de rendimiento, y ten en cuenta el búfer transitorio de la pila. Se considera terminado cuando un valor mayor acordado mejora el rendimiento de las subidas sin comprometer el comportamiento de las subidas ni el uso de la pila.

Escrito por el modelo de indexación a partir del texto del issue.

Evaluación

Stack tecnológico
c
Área
backend, performance
Tipo de issue
Nueva funcionalidad
Dificultad
4/5
Tiempo estimado
3-5 días
Estado de actividad
Estancado
Claridad
Bastante claro
Aptitud para principiantes
35/100

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.