Pourquoi un canal nommé est-il aussi lent que d'écrire dans un fichier?

18

J'essaie de comprendre le fonctionnement des canaux nommés afin de pouvoir rationaliser ma communication interprocessus unidirectionnelle. Je m'attends à des frais généraux en raison de la copie de données dans un tampon circulaire, que j'aurais pensé être stocké dans la RAM, et je m'attendais donc à ce que le canal soit beaucoup plus rapide que d'écrire dans un fichier (car la RAM est plus rapide que le disque).

Au lieu de cela, j'ai constaté que le canal nommé (ou canal anonyme) est à peu près à la même vitesse qu'un fichier. Il s'agit d'un bureau à 3 GHz avec un lecteur de disque ordinaire (pas un état solide), exécutant Ubuntu Linux. Voici un programme de test simplifié en Python:

import sys
import time
import random

megabyte = "".join(random.choice("abcdefghijklmnopqrstuvwxyz") for x in range(1024**2))

while True:
    before = time.time()
    sys.stdout.write(megabyte)
    after = time.time()
    sys.stderr.write("{} microseconds\n".format(1e6 * (after - before)))

Tuyauterie directement vers /dev/null:

python test.py > /dev/null

donne 2,1 microsecondes (constant) pour chaque mégaoctet.

Tuyauterie vers un fichier:

python test.py > /tmp/testout.txt

saute entre 500 microsecondes et 930 microsecondes (la plus grande valeur devient plus courante au fur et à mesure que le fichier grossit --- probablement, il cherche de l'espace disque).

Puis le tube nommé:

mkfifo testpipe
cat testpipe > /dev/null &
python test.py > testpipe

donne 640 microsecondes (constant) et un tube sans nom:

python test.py | cat > /dev/null

donne également 650 microsecondes (constant).

Quelqu'un peut-il expliquer pourquoi la vitesse du tuyau ressemble plus à la vitesse du fichier qu'à celle du fichier /dev/null? Puis-je avoir un commutateur quelque part qui dit "exécuter des canaux via un tampon basé sur des fichiers plutôt qu'un tampon basé sur la RAM" et puis-je changer ce commutateur? Serait-ce une option du noyau ou une variable shell?

Autre interprétation: supposons que la sortie du disque passe entre 500 et 930 microsecondes car le 500 est juste du piping et le 930 est en train d'écrire. Ensuite, le 500 ~ 640 pour la tuyauterie dans les deux cas est équivalent. Cependant, selon cette interprétation, pourquoi n'y a-t-il qu'un facteur deux entre la tuyauterie et l'écriture sur le disque? Les sites Web qui parlent de disques RAM disent que les disques RAM sont 50-200 fois plus rapides que les disques durs.

Jim Pivarski
la source
1
Écrire sur /dev/nullest en fait assez bon marché, alors qu'écrire n'importe où ailleurs - que ce soit un fichier, un FIFO, un tube ou autre - est beaucoup plus cher car il nécessite "beaucoup" d'efforts de manipulation.
glglgl

Réponses:

31

Vous ne voyez aucun avantage en termes de performances car vous ne frappez pas réellement le disque lorsque vous utilisez un fichier - les données sont en route vers le disque, mais votre thread d'exécution n'a pas besoin d'attendre qu'il atterrisse là-bas, vous êtes donc ne voyant pas réellement la pénalité de vitesse de frapper le disque.

Si vous voulez attendre la fin de l'opération sur le disque pour voir combien de temps plus lent, appelez un sync()(comment varie selon votre version de python, voir ici ) - vous regarderez des dizaines de milliers de microsecondes juste pour que votre disque cherchez plusieurs fois pour obtenir le fichier écrit (en supposant qu'il n'a pas une sorte de cache d'écriture rapide comme dans un contrôleur RAID).

Shane Madden
la source
Quand pouvons-nous cesser de nous inquiéter du temps de recherche de nos appareils en mode bloc? :)
EEAA
5
@EEAA Tous les SSD, tout le temps.
1
Vous avez raison: avec un, sync()le temps d'écriture sur disque devient 74 000 microsecondes en moyenne. (Le que flush()je faisais dans une variante de mon test ne l'a pas fait.) Alors, mon interprétation selon laquelle les 500 ~ 640 microsecondes par mégaoctet sont vraiment la surcharge du tuyau est logique, merci.
Jim Pivarski