2016-07-12 1 views
2

プログラム全体の目標は、ディレクトリ内のメインフォルダのサイズを決定することです。小さなドライブではうまく動作しますが、大きなドライブでは苦労します。私が絶対に必要とするドライブの1つに3時間以上かかりました。これは私のフォルダサイジングプログラムのコピーです、私は使用しています。フォルダサイズ決定用の並列ループ

public double getDirectorySize(string p) 
    { 

     //get array of all file names 
     string[] a = Directory.GetFiles(p, "*.*", SearchOption.AllDirectories); 

     //calculate total bytes in loop 
     double b = 0; 
     foreach (string name in a) 
     { 

      if (name.Length < 250) // prevents path too long errors 
      { 


        //use file info to get length of each file 
        FileInfo info = new FileInfo(name); 
        b += info.Length; 
      } 
     } 

     //return total size 
     return b; 
    } 

私は、並列ループを並列foreachループの形で使用することを考えていました。各pは、メインフォルダの名前を表します。どういうわけか、パスpをそのサブフォルダに分割し、並列のforeachループを使用してファイルサイズを収集し続けることを考えていました。ただし、サブディレクトリの量は不明です。これは私がフォルダサイズを取り戻そうとすることに問題を抱えているところです。事前に助けてくれてありがとう

更新

私は、次のインスタンス名を取ることだったので、私は、並列foreach文を考えたときに、私が考えていた

  DirectoryInfo di = new DirectoryInfo (Browse_Folders_Text_Box.Text); 
      FileInfo[] parsedfilename = di.GetFiles("*.*", System.IO.SearchOption.TopDirectoryOnly); 
      parsedfoldername = System.IO.Directory.GetDirectories(Browse_Folders_Text_Box.Text, "*.*", System.IO.SearchOption.TopDirectoryOnly); 
      //parsedfilename = System.IO.Directory.GetDirectories(textBox1.Text, "*.*", System.IO.SearchOption.AllDirectories); 





      // Process the list of folders found in the directory. 

      type_label.Text = "Folder Names \n"; 


      List<string> NameList = new List<string>(); 
      foreach (string transfer2 in parsedfoldername) 
      { 

       this.Cursor = Cursors.WaitCursor; 
       //Uses the path and takes the name from last folder used 
       string dirName = new DirectoryInfo(@transfer2).Name; 
       string dirDate = new DirectoryInfo(@transfer2).LastWriteTime.ToString(); 


       NameList.Add(dirName); 
       //Form2 TextTable = new Form2(NameList.ToString()); 



       //Display_Rich_Text_Box.AppendText(dirName); 
       //Display_Rich_Text_Box.AppendText("\n"); 
       Last_Date_Modified_Text_Box.AppendText(dirDate); 
       Last_Date_Modified_Text_Box.AppendText("\n"); 


       try 
       { 
        double b; 

        b = getDirectorySize(transfer2); 
        MetricByte(b); 



       } 
       catch (Exception) 
       { 
        Size_Text_Box.AppendText("N/A \n");      
       } 

      } 

      Display_Rich_Text_Box.Text = string.Join(Environment.NewLine, NameList); 
      this.Cursor = Cursors.Default; 

以下このforeachループでこの関数を呼び出しますメインフォルダ名の直下に少なくとも7つのサブフォルダがあることを知っているので、すべて同じレベルにあり、getDirectorySize()を使用してそれらをすべて同時に実行するサブフォルダ(サブフォルダ名)です。

+3

あなたは再帰検索を使用しての代わりに、全体を一度ドライブ上のすべてのファイルを取得する考えたことがあり'Directory.GetFiles'で?おそらく何百万ものエントリを含む巨大な結果配列を読み込むよりも、メモリに効率的かもしれないと思います。 –

+0

Directory.GetFilesは、フォルダであるpだけのファイルを取得しています。 foreach文を使ってドライブ内のメインフォルダ名を取得した後、この関数を呼び出します。私は再帰関数を考えましたが、通常はvoid関数であり、私はまだb(実際にはメインフォルダの合計サイズ)を返します。あなたは何を意味するかをもっと説明できますか? – Tasha

+1

また、 'Parallel'はプログラムのCPU境界部分にのみ有効です。並列化できるCPUアクティビティがいくつかある可能性がありますが、並列化の利点を生かしたI/Oに大部分の時間が費やされていると思われます。 –

答えて

1

同じ物理ドライブへの並列アクセスでは作業がスピードアップしません。

あなたの主な問題はGetFilesメソッドです。すべてのファイル名を収集するすべてのサブフォルダを通過します。その後、同じファイルをループします。

代わりにEnumerateFilesメソッドを使用してください。

このコードを試してください。はるかに速くなります。

public long GetDirectorySize(string path) 
{ 
    var dirInfo = new DirectoryInfo(path); 
    long totalSize = 0; 

    foreach (var fileInfo in dirInfo.EnumerateFiles("*.*", SearchOption.AllDirectories)) 
    { 
     totalSize += fileInfo.Length; 
    } 
    return totalSize; 
} 

MSDN

EnumerateFilesとGetFilesメソッドのように異なりますが、以下:あなたがEnumerateFilesを使用すると、コレクション全体が返される前に、あなたが名前のコレクションを列挙開始することができます。 GetFilesを使用するときは、配列にアクセスする前に、名前の配列全体が返されるまで待つ必要があります。したがって、多くのファイルとディレクトリで作業している場合、EnumerateFilesの方が効率的です。 Microsoft Scripting Runtimeは約90%高速であるように思わ使用し

+0

なぜファイルを列挙するのが速いのですか?あなたはもう少し説明できますか? – Tasha

+0

もう少し早く3時間から約2時間にしました。多分30分くらい取り除くために何かできることはありますか? – Tasha

0

私はフォルダ/ファイルサイズではありませんが、同様のことをしなければなりませんでした。

私は便利なコードはありませんが、私は次のものをスターターとして使用しました。ディレクトリ順次反復する次の例

が、並行して、ファイルを 処理:それはMSDN上のソースからディレクトリ

に十分なファイルがある場合は、並列に実行されます。これはおそらく、ファイルとディレクトリの比率が大きい場合には、 のベスト・アプローチです。 ディレクトリの繰り返しをパラレル化し、各ファイル に順次アクセスすることも可能です。 プロセッサの数が多いマシンを特に対象としている場合を除き、ループ を両方とも並列化することは効率的ではありません。ただし、すべての場合と同様に、アプリケーション を完全にテストして、最良の方法を決定する必要があります。

static void Main() 
    {    
     try 
     { 
     TraverseTreeParallelForEach(@"C:\Program Files", (f) => 
     { 
      // Exceptions are no-ops. 
      try { 
       // Do nothing with the data except read it. 
       byte[] data = File.ReadAllBytes(f); 
      } 
      catch (FileNotFoundException) {} 
      catch (IOException) {} 
      catch (UnauthorizedAccessException) {} 
      catch (SecurityException) {} 
      // Display the filename. 
      Console.WriteLine(f); 
     }); 
     } 
     catch (ArgumentException) { 
     Console.WriteLine(@"The directory 'C:\Program Files' does not exist."); 
     } 

     // Keep the console window open. 
     Console.ReadKey(); 
    } 

    public static void TraverseTreeParallelForEach(string root, Action<string> action) 
    { 
     //Count of files traversed and timer for diagnostic output 
     int fileCount = 0; 
     var sw = Stopwatch.StartNew(); 

     // Determine whether to parallelize file processing on each folder based on processor count. 
     int procCount = System.Environment.ProcessorCount; 

     // Data structure to hold names of subfolders to be examined for files. 
     Stack<string> dirs = new Stack<string>(); 

     if (!Directory.Exists(root)) { 
      throw new ArgumentException(); 
     } 
     dirs.Push(root); 

     while (dirs.Count > 0) { 
     string currentDir = dirs.Pop(); 
     string[] subDirs = {}; 
     string[] files = {}; 

     try { 
      subDirs = Directory.GetDirectories(currentDir); 
     } 
     // Thrown if we do not have discovery permission on the directory. 
     catch (UnauthorizedAccessException e) { 
      Console.WriteLine(e.Message); 
      continue; 
     } 
     // Thrown if another process has deleted the directory after we retrieved its name. 
     catch (DirectoryNotFoundException e) { 
      Console.WriteLine(e.Message); 
      continue; 
     } 

     try { 
      files = Directory.GetFiles(currentDir); 
     } 
     catch (UnauthorizedAccessException e) { 
      Console.WriteLine(e.Message); 
      continue; 
     } 
     catch (DirectoryNotFoundException e) { 
      Console.WriteLine(e.Message); 
      continue; 
     } 
     catch (IOException e) { 
      Console.WriteLine(e.Message); 
      continue; 
     } 

     // Execute in parallel if there are enough files in the directory. 
     // Otherwise, execute sequentially.Files are opened and processed 
     // synchronously but this could be modified to perform async I/O. 
     try { 
      if (files.Length < procCount) { 
       foreach (var file in files) { 
        action(file); 
        fileCount++;        
       } 
      } 
      else { 
       Parallel.ForEach(files,() => 0, (file, loopState, localCount) => 
              { action(file); 
               return (int) ++localCount; 
              }, 
           (c) => { 
              Interlocked.Add(ref fileCount, c);       
           }); 
      } 
     } 
     catch (AggregateException ae) { 
      ae.Handle((ex) => { 
         if (ex is UnauthorizedAccessException) { 
          // Here we just output a message and go on. 
          Console.WriteLine(ex.Message); 
          return true; 
         } 
         // Handle other exceptions here if necessary... 

         return false; 
      }); 
     } 

     // Push the subdirectories onto the stack for traversal. 
     // This could also be done before handing the files. 
     foreach (string str in subDirs) 
      dirs.Push(str); 
     } 

     // For diagnostic purposes. 
     Console.WriteLine("Processed {0} files in {1} milleseconds", fileCount, sw.ElapsedMilliseconds); 
    } 
0

は、残念ながら、そうでない場合は、Windowsエクスプローラは間違いなくそれを活用しているだろう、あなたはそれを介して再帰せずにディスク上のフォルダのサイズを取得できるようになる管理対象隠されたかのWin32 APIはありません。あなたは何百万を持っていない限り

private static long GetFolderSize(string sourceDir) 
{ 
    long size = 0; 
    string[] fileEntries = Directory.GetFiles(sourceDir); 

    foreach (string fileName in fileEntries) 
    { 
     Interlocked.Add(ref size, (new FileInfo(fileName)).Length); 
    } 

    var subFolders = Directory.EnumerateDirectories(sourceDir); 

    var tasks = subFolders.Select(folder => Task.Factory.StartNew(() => 
    { 
     if ((File.GetAttributes(folder) & FileAttributes.ReparsePoint) != FileAttributes.ReparsePoint) 
     { 
      Interlocked.Add(ref size, (GetFolderSize(folder))); 
      return size; 
     } 
     return 0; 
    })); 

    Task.WaitAll(tasks.ToArray()); 

    return size; 
} 

この例で、多くのメモリを消費しません。ここで

はあなたが同じことを達成するための標準的な非並列再帰関数と比較ができ、作業を並列うサンプル方法です1つのフォルダ内のファイル。

+0

どのようなライブラリは、キーワードとしてタスクを取得するために使用していますか? – Tasha

+0

System.Threading.Tasksの一部 –

0

var fso = new Scripting.FileSystemObject(); 
double size = fso.GetFolder(path).Size; 

参考:What is the fastest way to calculate a Windows folders size?

+0

ライブラリーとは何ですか私はそれを見つけることができません – Tasha

+0

それはCOMタブにあります。パスは 'C:\ Windows \ SysWOW64 \ scrrun.dll'のようなものです – Slai

+0

何がそんなに速くなりますか? – Tasha

関連する問題