web-dev-qa-db-ja.com

ファイル記述子はファイルへの書き込みを最適化しますか?

ファイル記述子に書き込むのではなく、コマンドをファイルに直接出力するのと同じですか?

イラスト

直接ファイルに書き込む:

for i in {1..1000}; do >>x echo "$i"; done

Fdの使用:

exec 3>&1 1>x
for i in {1..1000}; do echo "$i"; done
exec 1>&3 3>&-

後者の方が効率的ですか?

7
user147505

execを使用してループの前にファイルを開くことと、ループ内のコマンドにリダイレクトを配置することの主な違いは、前者はファイル記述子を一度だけ設定する必要があり、後者はファイルを開いたり閉じたりすることです。ループの各反復。

一度実行する方が効率的ですが、ループ内で外部コマンドを実行する場合、コマンドの起動コストの違いはおそらくなくなります。 (echoここはおそらく組み込みなので、それは適用されません)

出力が通常のファイル以外に送信される場合(たとえば、xが名前付きパイプである場合)、ファイルを開いたり閉じたりする動作が他のプロセスから見える可能性があるため、違いがある可能性があります行動においても。


execによるリダイレクトとコマンドでのリダイレクトに実際の違いはないことに注意してください。どちらもファイルを開き、ファイル記述子番号を処理します。

これらの2つは、ファイルへのopen()とファイルへのwrite()の両方で、ほぼ同等である必要があります。 (ただし、コマンドの実行中にfd 1が格納される方法には違いがあります。):

for i in {1..1000}; do 
    >>x echo "$i"
done


for i in {1..1000}; do
    exec 3>&1 1>>x         # assuming fd 3 is available
    echo "$i"              # here, fd 3 is visible to the command
    exec 1>&3 3>&-
done

12
ilkkachu

はい、より効率的です

テストする最も簡単な方法は、カウントを500000に増やして時間を計ることです。

> time bash s1.sh; time bash s2.sh
bash s1.sh  16,47s user 10,00s system 99% cpu 26,537 total
bash s2.sh  10,51s user 3,50s system 99% cpu 14,008 total

strace(1)が理由を明らかにします(write + 5 * open + 2 * fcntl + 2 * dup + closeではなく、単純なwriteがあります):

ために for i in {1..1000}; do >>x echo "$i"; done 我々が得る:

open("x", O_WRONLY|O_CREAT|O_APPEND, 0666) = 3
fcntl(1, F_GETFD)                       = 0
fcntl(1, F_DUPFD, 10)                   = 10
fcntl(1, F_GETFD)                       = 0
fcntl(10, F_SETFD, FD_CLOEXEC)          = 0
dup2(3, 1)                              = 1
close(3)                                = 0
write(1, "997\n", 4)                    = 4
dup2(10, 1)                             = 1
fcntl(10, F_GETFD)                      = 0x1 (flags FD_CLOEXEC)
close(10)                               = 0
open("x", O_WRONLY|O_CREAT|O_APPEND, 0666) = 3
fcntl(1, F_GETFD)                       = 0
fcntl(1, F_DUPFD, 10)                   = 10
fcntl(1, F_GETFD)                       = 0
fcntl(10, F_SETFD, FD_CLOEXEC)          = 0
dup2(3, 1)                              = 1
close(3)                                = 0
write(1, "998\n", 4)                    = 4
dup2(10, 1)                             = 1
fcntl(10, F_GETFD)                      = 0x1 (flags FD_CLOEXEC)
close(10)                               = 0
open("x", O_WRONLY|O_CREAT|O_APPEND, 0666) = 3
fcntl(1, F_GETFD)                       = 0
fcntl(1, F_DUPFD, 10)                   = 10
fcntl(1, F_GETFD)                       = 0
fcntl(10, F_SETFD, FD_CLOEXEC)          = 0
dup2(3, 1)                              = 1
close(3)                                = 0
write(1, "999\n", 4)                    = 4
dup2(10, 1)                             = 1
fcntl(10, F_GETFD)                      = 0x1 (flags FD_CLOEXEC)
close(10)                               = 0
open("x", O_WRONLY|O_CREAT|O_APPEND, 0666) = 3
fcntl(1, F_GETFD)                       = 0
fcntl(1, F_DUPFD, 10)                   = 10
fcntl(1, F_GETFD)                       = 0
fcntl(10, F_SETFD, FD_CLOEXEC)          = 0
dup2(3, 1)                              = 1
close(3)                                = 0
write(1, "1000\n", 5)                   = 5
dup2(10, 1)                             = 1
fcntl(10, F_GETFD)                      = 0x1 (flags FD_CLOEXEC)
close(10)                               = 0

exec 3>&1 1>xきれいになりました

write(1, "995\n", 4)                    = 4
write(1, "996\n", 4)                    = 4
write(1, "997\n", 4)                    = 4
write(1, "998\n", 4)                    = 4
write(1, "999\n", 4)                    = 4
write(1, "1000\n", 5)                   = 5

ただし、違いは「FDの使用」によるものではなく、リダイレクトを行う場所によるものです。たとえば、for i in {1..1000}; do echo "$i"; done > x 2番目の例とほぼ同じパフォーマンスが得られます。

bash s3.sh  10,35s user 3,70s system 100% cpu 14,042 total
7
Matija Nalis

物事を要約し、このスレッドに新しい情報を追加するために、効率の高い順に4つの方法を比較します。 2つのテストシリーズに基づいて、100万回の反復の時間測定(ユーザー+ sys)によって効率を推定します。

  1. これら2つはほぼ同じです。
    • 単純な>ループリダイレクト(時間:100%
    • ループ全体でexecを1回使用する(時間:〜100%
  2. 各反復で>>を使用(時間:200%-250%
  3. 各反復でexecを使用(時間:40%-480%

結論はこれです:

exec>>のような単純なリダイレクトの使用にはsmallの違いがあります。 (シンプルな方が安い)。単一のコマンド実行レベルでは表示されませんが、繰り返し回数が多いと、違いが明らかになります。他の回答でikkachuが気づいたように、コマンドの実行の重みは違いを隠していますが。

0
user147505