2009-11-04 14 views
7

私は約500k jpgファイルの巨大なディレクトリを持っています、そして、私は特定の日付より古いすべてのファイルをアーカイブしたいと思います。現在、スクリプトは実行に数時間かかる。C#の実行ファイルコピー?

これは、GoGridのストレージサーバの非常にうんざりしたパフォーマンスと関係がありますが、同時に、私がやっていることを達成するためにRam/Cpuの方が効率的な方法があると確信しています。 。

var dirInfo = new DirectoryInfo(PathToSource); 
var fileInfo = dirInfo.GetFiles("*.*"); 
var filesToArchive = fileInfo.Where(f => 
    f.LastWriteTime.Date < StartThresholdInDays.Days().Ago().Date 
     && f.LastWriteTime.Date >= StopThresholdInDays.Days().Ago().Date 
); 

foreach (var file in filesToArchive) 
{ 
    file.CopyTo(PathToTarget+file.Name); 
} 

日()アゴー()のものは単なる糖衣構文です:

は、ここで私が持っているコードです。

+0

依存していることホストオペレーティングシステムではトップレベルでなければなりません。 –

+0

ええ、真実はそこに何百万ものファイルがある可能性があります、私はWindowsエクスプローラを介して同様のパフォーマンスの問題のためにディレクトリのカウントを取得することはできません。 – Scott

+2

文法ナチは言う: "Performant"は単語ではありません:) –

答えて

3
+0

ありがとうMauricio ...これはCPUの問題ではなくRAMの問題で動作します。達成にはまだ時間がかかりますが、少なくともRAMは私の上で飛び火しません。 – Scott

+0

私の問題を解決するのに十分うまくいきます。約2時間かかっていますが、今はバックグラウンドで最大4メガバイトのRAMで実行できますが、以前は数百メガバイトでした。 – Scott

1

(限られた数の)スレッドを使って試して、CopyTo()を実行することができます。現在、全体の動作は1コアに制限されています。

これはCPUバウンドになるとパフォーマンスが向上します。しかし、これがRAID上で動作する場合、動作する可能性があります。

+0

私はGoGridが "雲の中にある"と信じています。アクティブな接続には制限があります。それにかかわらず、良いアドバイス。 – user7116

2

私は80/20ルール を念頭に置いており、減速の大部分が​​であり、この減速がLINQクエリのパフォーマンスを上回る場合、私は心配しません。これは、​​行を削除し、 Console.WriteLine操作で置き換えることでテストできます。実際のコピーと比較した時間。 GoGridのオーバーヘッドと他の操作のオーバーヘッドがわかります。私の感心は、あなたの最後に現実的な大きな利益はありません。

EDIT:オクラホマので、80%が実際にディレクトリ内の百万個のファイルがある場合には驚くべきことではないGetFiles操作、です。あなたの最善の策は、直接のWin32 APIの使用を開始すること(のようなFindFirstFilefamily)とP/Invokeことがあります。

[DllImport("kernel32.dll", CharSet=CharSet.Auto)] 
static extern IntPtr FindFirstFile(string lpFileName, 
    out WIN32_FIND_DATA lpFindFileData); 

可能であれば私はまた、ディレクトリごとのファイル数を減らすために、ディレクトリ構造を変更すること、お勧めしたいです。これにより、状況が大幅に改善されます。

EDIT2GetFiles("*.*")からGetFiles()に変更することも検討します。あなたはすべてを求めているので、各ステップで規則を適用するのは意味がありません。

+0

操作の大部分はdirInfo.GetFiles( "*。*")ステートメントです。 私は5日分のファイルしかテストしていませんし、linqクエリを実行するディレクトリ内のファイル数を取得する前にRAM/Patienceを使い果たしてしまいます。 GetFiles []は、すべてを返さなくても、範囲内にあるファイルを返すような、より良い方法がありますか? 少なくともこの操作を初めてこの10%のチャンクに分割して、毎晩アーカイバを実行させることができます。 今のところ、私は本当にどこにでも行くことができません。 – Scott

+0

はい、ディレクトリ構造を変更することは私がやろうとしていることですが、一日待っていなくてもサーバーにタイムアウトすることなくファイルにアクセスする必要があります:) – Scott

10

あなたが改善できると思う唯一の部分はdirInfo.GetFiles("*.*")です。 .NET 3.5以前では、すべてのファイル名を持つ配列を返します。これは、構築に時間がかかり、大量のRAMを使用します。 .NET 4.0には、代わりにIEnumerable<string>を返す新しいDirectory.EnumerateFilesメソッドがあり、ディスクから読み込んだときにすぐに結果をフェッチします。これによりパフォーマンスは少し向上しますが、奇跡は期待できません。

+0

実際には何が行われる必要があるのか​​をexatcly、EnumerateFilesは全リスト。アレイに必要なすべてのメモリを節約できます。 その500kファイル* 100バイト= 50MBのRAMを考えてみましょう。 Enumerateを使用すると、一度に1つのファイルしか取得できないため、100バイトを使い切ります。 – Kugel

+0

+1、.Net 4.0にはSystem.IOにはすばらしい機能がたくさんあります。ディレクトリ内に百万個のファイルがある状況を改善するかどうかはわかりません:-D – user7116

2

コピーを実行するには、サードパーティのユーティリティを使用することを検討する必要があります。 robocopyのようなものは、あなたの処理を大幅にスピードアップするかもしれません。他を見る:https://serverfault.com/questions/54881/quickest-way-of-moving-a-large-number-of-files

+0

+1、robocopy/minage = X/maxage = Y – user7116

+2

そしてroboopyはデフォルトでWin7とServer 2008に含まれています! – joshperry

+0

はい、正確には私が「第三者」と呼ぶものではありません;) –

0

これを聞いてください。Hanselminutes podcast ScottはBansheeのメディアプレイヤーの作者であるAaron Bockoverと話をし、彼らはこの正確な問題に立ち向かって、ポッドキャストの8:20に話しました。

.Net 4.0を使用できる場合は、Thomas Levesqueの言葉通りDirectory.EnumerateFilesを使用します。そうでない場合は、ネイティブWin32 APIを使用してMono.Posixで行ったのと同じように、独自のディレクトリウォークコードを記述する必要があります。

関連する問題