sendfile() を使用して、スレッドまたはその他の効率的なファイル コピー方法でファイルをコピーします。
-
21-12-2019 - |
質問
Linux システムコールを使用しようとしています sendfile()
スレッドを使用してファイルをコピーします。
コードの次の部分を最適化することに興味があります。
fseek(fin, size * (number) / MAX_THREADS, SEEK_SET);
fseek(fout, size * (number) / MAX_THREADS, SEEK_SET);
/* ... */
fwrite(buff, 1, len, fout);
コード:
void* FileOperate::FileCpThread::threadCp(void *param)
{
Info *ft = (Info *)param;
FILE *fin = fopen(ft->fromfile, "r+");
FILE *fout = fopen(ft->tofile, "w+");
int size = getFileSize(ft->fromfile);
int number = ft->num;
fseek(fin, size * (number) / MAX_THREADS, SEEK_SET);
fseek(fout, size * (number) / MAX_THREADS, SEEK_SET);
char buff[1024] = {'\0'};
int len = 0;
int total = 0;
while((len = fread(buff, 1, sizeof(buff), fin)) > 0)
{
fwrite(buff, 1, len, fout);
total += len;
if(total > size/MAX_THREADS)
{
break;
}
}
fclose(fin);
fclose(fout);
}
解決
ファイルのコピーは CPU に依存しません。もしそうなら、制限はカーネル レベルにあり、ユーザー レベルで実行できることは何も並列化できないことがわかるでしょう。
機械式ドライブで行われたこのような「改良」は、実際には 劣化させる スループット。ファイルを読み書きする代わりに、ファイルを探すことに時間を無駄にしています。
ファイルが長く、すぐにデータの読み取りまたは書き込みが必要になることが予想されない場合は、 O_DIRECT
フラグが開いています。それは悪い考えです。 O_DIRECT
APIは基本的に 設計上壊れている.
代わりに、使用する必要があります posix_fadvise
ソース ファイルと宛先ファイルの両方で、POSIX_FADV_SEQUENTIAL フラグと POSIX_FADV_NOREUSE フラグを使用します。write (または sendfile) 呼び出しが終了したら、データがもう必要ないことを通知する必要があります (POSIX_FADV_DONTNEED を渡します)。そうすることで、ページ キャッシュはデータ フローを維持するために必要な範囲でのみ使用され、データが消費される (ディスクに書き込まれる) とすぐにページがリサイクルされます。
の sendfile
ファイル データをユーザー空間にプッシュしないため、メモリとプロセッサ キャッシュの負担がさらに軽減されます。デバイス固有ではないファイルのコピーに関して、他にできる賢明な改善策はこれくらいです。
適切なチャンク サイズを選択することも望ましいです。最近のドライブが 100M バイト/秒を超える速度でプッシュすることを考えると、一度に 1 メガバイト、常に 4096 バイトのページ サイズの倍数をプッシュする必要があるかもしれません。 (4096*256)
1 回で処理するのに適切な開始チャンク サイズです。 sendfile
または read
/write
呼び出します。
あなたが提案する読み取り並列化は、RAID 0 ボリューム上でのみ、また入力ファイルと出力ファイルの両方が物理ディスクにまたがる場合にのみ意味を持ちます。これにより、ファイルにまたがるソース ボリュームと宛先ボリュームの物理ディスクの数の小さい方ごとに 1 つのスレッドを設定できます。これは、非同期ファイル I/O を使用していない場合にのみ必要です。非同期 I/O では、特にチャンク サイズが大きく (メガバイト以上)、シングル スレッドの遅延ペナルティが無視できる場合には、複数のスレッドは必要ありません。
実際に非常に奇妙なシステムを使用している場合を除き、SSD 上の単一ファイルのコピーを並列化する意味はありません。