présentation
Un service de fichiers a connu une baisse de performances après une modification de middleware. L'ajout d'un lecteur de journalisation pour compter les octets a désactivé la fonctionnalité sendfile(2) de Go.
fonctionnement de sendfile
Sendfile(2) permet de transférer des données directement du disque vers le tampon de socket sans passer par l'espace utilisateur. Cela réduit les copies de données et améliore les performances. La fonctionnalité équivalente pour le transfert de socket à socket est splice(2).
disk -> page cache --(sendfile)--> socket buffer -> NICimplémentation en go
La bibliothèque standard de Go utilise sendfile(2) automatiquement lors de l'utilisation de io.Copy avec un *os.File. Cependant, si le fichier est enveloppé dans un io.Reader, la fonctionnalité sendfile(2) est désactivée.
Il existe une exception pour *io.LimitedReader, qui est explicitement vérifié par le runtime et permet de conserver la fonctionnalité sendfile(2).
analyse des performances
Les tests ont montré que l'utilisation de sendfile(2) réduit considérablement le nombre de syscalls et améliore les performances. L'utilisation de io.LimitedReader permet de conserver cette fonctionnalité, tandis que l'enveloppe d'un fichier dans un io.Reader simple la désactive.
Les résultats des tests sont les suivants :
- raw (io.Copy(conn, f)) : 2 958 appels à sendfile, 7 lectures, 7 écritures
- wrapped (io.Copy(conn, justReader{f})) : 0 appel à sendfile, 65 546 écritures, 65 547 lectures
- limit (io.Copy(conn, io.LimitReader(f, n))) : 2 896 appels à sendfile, 7 écritures, 7 lectures
La désactivation de sendfile(2) peut entraîner une augmentation de 3,4 fois du temps CPU par octet transféré.