パイプバッファーがいっぱいであるかどうかを確認するにはどうすればよいですか?


11

1つのプログラムからの出力を、私が書いたいくつかのPerlにパイプしています。これは長時間実行されるプロセスであり、場合によっては数日かかるため、ボトルネックがどこにあるかを調べて、それらを開こうとする必要があります。スクリプトが処理できるよりも速くデータがスクリプトにパイプ処理されているかどうかを知りたいのですが。これが当てはまる場合は、スクリプトを調整しようとしますが、必要がない場合は調整しません。バッファがいっぱいになるとフラグが設定され、それ以上の書き込みができなくなるという話がありましたが、そのフラグが設定されているかどうか、またはどのくらいの頻度で設定されているかを確認するにはどうすればよいですか?何か案は?


3
筆記プロセスだけが知っていると思います。
enzotib 2009

5
pvパイプチェーンのどこかで使用することを検討できます。
amphetamachine

回答:


9

strace(Linux)、dtruss(OS X)、ktrace(FreeBSD)、truss(Solaris)などのシステムコールトレースツールを使用してPerlスクリプトを追跡します。目標は、Perlスクリプトがその読み取りにどれだけの時間を費やしているかを確認することです。 stdin、および他のプログラムがそのstdoutへの書き込みを待機するのに費やす時間。

ここでは、筆者をボトルネックとしてこれをテストしています。

terminal 1$ gzip -c < /dev/urandom | cat > /dev/null

terminal 2$ ps auxw | egrep 'gzip|cat'
slamb    25311 96.0  0.0  2852  516 pts/0    R+   23:35   3:40 gzip -c
slamb    25312  0.8  0.0  2624  336 pts/0    S+   23:35   0:01 cat

terminal 2$ strace -p 25312 -s 0 -rT -e trace=read
Process 25312 attached - interrupt to quit
     0.000000 read(0, ""..., 4096) = 4096 <0.005207>
     0.005531 read(0, ""..., 4096) = 4096 <0.000051>

ここの最初の数値は、前のシステムコールの開始以降の時間で、最後の数値は、システムコールで費やされた時間です。そのため、Perlで少し後処理して、それを集約することができます... [*]

terminal 2$ strace -p 25312 -s 0 -rT -e trace=read 2>&1 | perl -nle 'm{^\s*([\d.]+) read\(0, .*<([\d.]+)>} or next; $total_work += $1 - $last_wait; $total_wait += $2; $last_wait = $2; print "working for $total_work sec, waiting for $total_wait sec"; $last_wait = $2;'
working for 0 sec, waiting for 0.005592 sec
...
working for 0.305356 sec, waiting for 2.28624900000002 sec
...

terminal 2$ strace -p 25311 -s 0 -rT -e trace=write 2>&1 | perl -nle 'm{^\s*([\d.]+) write\(1, .*<([\d.]+)>} or next; $total_work += $1 - $last_wait; $total_wait += $2; $last_wait = $2; print "working for $total_work sec, waiting for $total_wait sec"; $last_wait = $2;'
...
working for 5.15862000000001 sec, waiting for 0.0555740000000007 sec
...

より洗練された方法で、両側を一度にトレースし、正しいファイル記述子のみを追跡し、毎秒程度の適切なステータス更新を出力して、それぞれが他を待機している時間の割合を表示するSystemTapまたはDTraceスクリプトを作成できます。

[*]-警告:他のファイル記述子で読み取り/書き込みが呼び出されている場合、粗雑な集計は完全に正しくありません。その場合、作業時間を過小評価します。


dtraceのバージョンは、実際にはかなりきれいです。

terminal 1$ gzip -c < /dev/urandom | cat > /dev/null

terminal 2$ ps aux | egrep 'gzip| cat'
slamb    54189  95.8  0.0   591796    584 s006  R+   12:49AM  22:49.55 gzip -c
slamb    54190   0.4  0.0   599828    392 s006  S+   12:49AM   0:06.08 cat

terminal 2$ cat > pipe.d <<'EOF'
#!/usr/sbin/dtrace -qs

BEGIN
{
  start = timestamp;
  writer_blocked = 0;
  reader_blocked = 0;
}

tick-1s, END
{
  this->elapsed = timestamp - start;
  printf("since startup, writer blocked %3d%% of time, reader %3d%% of time\n",
         100 * writer_blocked / this->elapsed,
         100 * reader_blocked / this->elapsed);
}

syscall::write:entry
/pid == $1 && arg0 == 1/
{
  self->entry = timestamp;
}

syscall::write:return
/pid == $1 && self->entry != 0/
{
  writer_blocked += timestamp - self->entry;
  self->entry = 0;
}

syscall::read:entry
/pid == $2 && arg0 == 0/
{
  self->entry = timestamp;
}

syscall::read:return
/pid == $2 && self->entry != 0/
{
  reader_blocked += timestamp - self->entry;
  self->entry = 0;
}
EOF

terminal 2$ chmod u+x pipe.d
terminal 2$ sudo ./pipe.d 54189 54190
since startup, writer blocked   0% of time, reader  98% of time
since startup, writer blocked   0% of time, reader  99% of time
since startup, writer blocked   0% of time, reader  99% of time
since startup, writer blocked   0% of time, reader  99% of time
since startup, writer blocked   0% of time, reader  99% of time
^C
since startup, writer blocked   0% of time, reader  99% of time

そしてSystemTapバージョン:

terminal 1$ gzip -c /dev/urandom | cat > /dev/null

terminal 2$ ps auxw | egrep 'gzip| cat'
slamb     3405  109  0.0   4356   584 pts/1    R+   02:57   0:04 gzip -c /dev/urandom
slamb     3406  0.2  0.0  10848   588 pts/1    S+   02:57   0:00 cat

terminal 2$ cat > probes.stp <<'EOF'
#!/usr/bin/env stap

global start
global writer_pid
global writes
global reader_pid
global reads

probe begin {
  start = gettimeofday_us()
  writer_pid = strtol(argv[1], 10)
  reader_pid = strtol(argv[2], 10)
}

probe timer.s(1), end {
  elapsed = gettimeofday_us() - start
  printf("since startup, writer blocked %3d%% of time, reader %3d%% of time\n",
         100 * @sum(writes) / elapsed,
         100 * @sum(reads) / elapsed)
}

probe syscall.write.return {
  if (pid() == writer_pid && $fd == 1)
    writes <<< gettimeofday_us() - @entry(gettimeofday_us())
}

probe syscall.read.return {
  if (pid() == reader_pid && $fd == 0)
    reads <<< gettimeofday_us() - @entry(gettimeofday_us())
}
EOF

terminal 2$ chmod a+x probes.stp
terminal 2$ sudo ./pipe.stp 3405 3406
since startup, writer blocked   0% of time, reader  99% of time
...

6

pv -TCパイプラインにコマンドを挿入できます:

cmd1 | pv -TC | cmd2

pv独自のバッファを使用し、-T平均で1秒間にどれだけいっぱいかを報告します(デフォルト)。

常に100%の場合、それは消費cmd1よりも出力の生成の方が高速cmd2です。そうでない場合は、逆になります。パイプ自体が64kBを保持できることに注意してください。

のバッファサイズの-B指定についてもご覧くださいpv。次のようにいくつかpvのを使用できます。

$ cmd1 | pv -cCTrbN 'cmd1 -> cmd2' | cmd2 | pv -cCTrbN 'cmd2 -> cmd3' | cmd3
cmd1 -> cmd2: 1.92GiB { 53%} [ 387MiB/s]
cmd2 -> cmd3: 1.92GiB {  0%} [ 387MiB/s]
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.