中スタックオーバーフローに対する回答、質問に記載されているいくつかの小さなタスクを実行するためのコード例を提供しました。元の質問は、どのスキルが最も速く実行されるかに関するものでした(したがって、ここではパフォーマンス基準が適用されます)。
別のコメント提示者/回答では、POSIX定義システムAPI呼び出し(この場合readdir
)を作成する代わりに提案しました。直接システムはカーネル(syscall(SYS_getdents,...)
)を呼び出し、パフォーマンスの差が25%の範囲にあると主張します。 (実装もベンチマークもしませんでした。実際にパフォーマンスが良くなると思います。)
私の質問は、提案されたシステムコールベースのソリューションのパフォーマンス特性についてです。なぜより速いかもしれません。パフォーマンスが良いいくつかの理由を考えてみましょう。
- POSIXは
readdir
本質的にsyscall(SYS_getdents,...)
/より複雑です。getdents()
readdir
(おそらく呼び出しはsyscall(SYS_getdents,...)
間接的なオーバーヘッドを追加するだけです。readdir
(カーネル呼び出しごとに)1つのレコードのみを返しますが、syscall(SYS_getdents,...)/
getdents()`は(おそらく)カーネル呼び出しごとに複数のレコードを返します。
上記の#1は本当とは想像できません。 glibcの実装は、directよりも「実際の」システムコールを呼び出すことができないほど似ていますreaddir
。getdents
readdir
syscall(SYS_getdents,...)
getdents()
readdir
また、呼び出しがラップされ、呼び出しgetdents
も可能であるため、#2は真であると想像することはできません(提案された答えは直接呼び出すのではなく具体的に使用されます)。 Linuxのglibcのすべては、おそらくこの場合#2になります。syscall(SYS_getdents,...)
getdents
syscall(SYS_getdents,...)
getdents
syscall(syscallid, args)
はい本当。
私の考えでは、最後の可能性が最高の説明です。カーネル呼び出しが少ないほどパフォーマンスが向上します。
「直接カーネル呼び出し」がPOSIX定義関数を呼び出すよりもはるかに速い理由の具体的な説明はありますか?
ベストアンサー1
PLT
これがLinuxで最も高価な呼び出しの1つであることを考えると、間接参照や可変引数(レジスタはメモリに保存する必要があります)などの要因はsyscall()'s
ほとんど影響しません。getdents
私のコンピュータでは、空のディレクトリを完全に読み取るのに約5μsかかります。
fdopendir
+readdir
効果は、getdents
バッファ割り当て/使用可能(0.1μs)を追加し、stat
提供されたfdがディレクトリタイプ(0.4μs)であることを確認することです。readdir
次に、各ディレクトリエントリに対して簡単な呼び出しを実行します(バッファ内の1つの場所を移動して再入力できます)。
したがって、ワンタイムオーバーヘッドは0.5μsです。これは空のディレクトリのディレクトリスキャン時間の10%ですが、100エントリディレクトリの場合は1%にすぎず、大きなディレクトリではほとんど無視できます。 fdopenが必要ない場合、このオーバーヘッドは5倍になります(割り当て/無料コストのみ)。 (diropen
直接使用できない場合はfdopenのみが必要です(例: 'ted)、ファイルディスクリプタを別途入手する必要がありますopenat
。)
したがって、カスタムワンタイム割り当てバッファを使用するgetdents
場合空ディレクトリであり、大きなディレクトリではほとんど無視できます。
呼び出しの場合、readdir
PLT間接コストは最新のハードウェアでは通常1ns未満で、関数呼び出しのオーバーヘッドは約1〜2nsです。ディレクトリスキャン時間がマイクロ秒程度であることを考慮すると、readdir
これらの要素を単一のμsに変換するには少なくとも1000回の呼び出しが必要ですが、スキャンコストは340μsで、累積された1μsはそのうち約0.3%です。影響は次のとおりです。無視できる。これをインライン化するとreaddir
(したがってコールオーバーヘッドとPLTオーバーヘッドを削除する)、コード拡張にのみ役立ちますが、getdents
ボトルネックが発生するため、パフォーマンスは大幅に向上しません。
(追加のロックによりコストが高くなりますが、通常の呼び出しは通常スレッドセーフなので、readdir_r
必要はありません。readdir_r
readdir
〜しない限りあなたはそれらを呼び出すいくつかのスレッドを持っています同じディレクトリストリーム。 POSIXはまだこれを明示的に明らかにしていないかもしれませんが、glibcがもう使用されていないことを考えると、この保証はすぐに標準化されるべきだと思いますreaddir_r
。 )