Por que um pipe nomeado é tão lento quanto gravar em um arquivo?


18

Estou tentando entender como os pipes nomeados funcionam para que eu possa otimizar minha comunicação unidirecional entre processos. Espero alguma sobrecarga devido à cópia de dados em um buffer circular, que eu pensaria estar armazenado na RAM e, portanto, esperava que o canal fosse muito mais rápido do que gravar em um arquivo (porque a RAM é uma ordem de magnitude mais rápida que o disco).

Em vez disso, descobri que o pipe nomeado (ou pipe anônimo) tem aproximadamente a mesma velocidade que um arquivo. Este é um desktop de 3 GHz com uma unidade de disco comum (sem estado sólido), executando o Ubuntu Linux. Aqui está um programa de teste simplificado em 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)))

Tubulação direta para /dev/null:

python test.py > /dev/null

produz 2,1 microssegundos (constante) para cada megabyte.

Piping para um arquivo:

python test.py > /tmp/testout.txt

pula entre 500 microssegundos e 930 microssegundos (o valor maior fica mais comum à medida que o arquivo aumenta - presumivelmente, ele está procurando espaço em disco).

Em seguida, o pipe nomeado:

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

produz 640 microssegundos (constante) e um tubo sem nome:

python test.py | cat > /dev/null

também produz 650 microssegundos (constante).

Alguém pode explicar por que a velocidade do tubo é mais parecida com a velocidade do arquivo do que a velocidade do arquivo /dev/null? Posso ter um comutador em algum lugar que diga "executar canais através de um buffer baseado em arquivo, em vez de um buffer baseado em RAM", e posso mudar esse comutador? Pode ser uma opção de kernel ou uma variável de shell?

Outra interpretação: suponha que a saída do disco salte entre 500 e 930 microssegundos porque o 500 é apenas canalizado e o 930 está realmente gravando. Então os 500 ~ 640 para a tubulação nos dois casos são equivalentes. No entanto, sob essa interpretação, por que há apenas um fator de dois entre a tubulação e a gravação no disco? Os sites que falam sobre discos RAM dizem que os discos RAM são 50 a 200 vezes mais rápidos que os discos rígidos.


11
Escrever /dev/nullé realmente muito barato, enquanto escreve em qualquer outro lugar - seja um arquivo, um FIFO, um cachimbo ou o que for - é muito mais caro, pois exige "muito" esforço de manuseio.
glglgl

Respostas:


31

Você não está obtendo nenhum benefício de desempenho porque na verdade não está atingindo o disco ao usar um arquivo - os dados estão a caminho do disco, mas seu encadeamento de execução não precisa esperar que ele chegue lá, então você está realmente não vendo a penalidade de velocidade de bater no disco.

Se você quiser aguardar a conclusão da operação do disco para ver quanto mais lento fica, chame a sync()(como varia sua versão do python, veja aqui ) - você verá dezenas de milhares de microssegundos apenas para o seu disco procure algumas vezes para que o arquivo seja gravado (supondo que ele não tenha algum tipo de cache de gravação rápida, como em um controlador RAID).


Quando parar de nos preocupar com o tempo de busca dos nossos dispositivos de bloco? :)
EEAA

5
@EEAA Todos os SSDs, o tempo todo.

11
Você está certo: com um sync()tempo de gravação em disco, a média é de 74.000 microssegundos. (O que flush()eu estava fazendo em uma variação do meu teste não o fez.) Então, minha interpretação de que os 500 ~ 640 microssegundos por megabyte é realmente a sobrecarga do tubo faz sentido, obrigado.
Jim Pivarski
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.