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.
/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.Respostas:
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).fonte
sync()
tempo de gravação em disco, a média é de 74.000 microssegundos. (O queflush()
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.