2012-03-09 17 views
9

Grand Central Dispatchを使用すると、いつどこで起こるかを心配することなく読み書きをスケジュールできます。以前のNSStreamベースのアプローチと比較すると、これは外部での管理が少なくて済みます。しかし、私の素朴な実装はNSStreamベースのアプローチよりも遅いです。GCDでファイルをコピーする最も効率的な方法は?

NSStreamでは、ソースと宛先の両方の優先IOサイズ(NSURLPreferredIOBlockSizeKey)を照会しました。次に、私は全体の "優先入力サイズのチャンク"をバッファに読み込み、バッファ内の少なくとも "好ましい出力サイズ"バイトを得た時点で、すべてのチャンクを書き出しました(最後のチャンクを除いて)。これは読み込みと書き込みのパフォーマンスに関して最適に近いはずです。

しかし、GCDではこれにあまり影響しません。ソースの優先IOサイズが100kBで、ターゲットの優先IOサイズが1MBであると仮定します。私の純粋な実装では、NSStreamベースのソリューションと同じくらい頻繁に10回書くようになりました。

GCDでこれを解決する最も効率的な方法は何ですか?リーダブロックのバッファに書き込むだけで、十分なデータが収集されるとすぐに、「好ましい出力サイズ」の書き込みブロックをスケジュールしますか?私はGCDがここで私がまだ気づいていない解決策を提供するかもしれないと思います。

は、ここに私の現在のGCDソリューションの最も重要な部分です:

// input_ and output_ are of type dispatch_io_t 

dispatch_io_read(
    input_, 
    0, 
    SIZE_MAX, 
    dispatch_get_main_queue(), 
    ^(bool done, dispatch_data_t data, int error) { 
     size_t data_size; 

     if (error) { 
      NSLog(@"Input: error %d", error); 
      [self cancel]; 
      return; 
     } 
     if (data) { 
      data_size = dispatch_data_get_size(data); 
      if (data_size > 0) { 
       dispatch_io_write(
        output_, 
        0, 
        data, 
        dispatch_get_main_queue(), ^(bool done, dispatch_data_t data, int error) { 
         // TODO: I don't know how to get the offset (for progress). So I need to 
         // pass it from the calling block. 
         if (error) { 
          NSLog(@"Output: error %d", error); 
          return; 
         } 
         if (done) { 
          bytesWritten_ += data_size; 
          // Update progress report here. 
         } 
        } 
       ); 
      } 
     } 
    } 
); 

答えて

8

それはほとんどの状況で必要はありませんが、あなたはdispatch_io_set_high_water(3)dispatch_io_set_low_water(3) APIとGCDが使用するIOサイズに影響を与えることができます。

GCDはチャネルの最高水準点よりも大きなチャンクを読み書きしません。読み取り/書き込みハンドラも、低水準点よりも小さいデータオブジェクトで呼び出されることはありません。

など。あなたの例では、input_の低水準点を1MBに設定することで、現在の読み取りコールバックが1MB未満のデータオブジェクトをdispatch_io_write(3)に渡さないようにすることができます。

このコントロールでは十分でない場合は、dispatch_io_write(3)に渡すのに十分な大きさに達するまで、dispatch_data_create_concat(3)によって、読み取りハンドラの連続呼び出しから受け取った複数のデータオブジェクトを組み合わせることもできます。

しかし、ソース側の低水準点を優先ソースチャンクサイズの倍数に設定し、優先チャネルチャンクサイズに到達し、宛先チャネルの最高水準点を優先チャネルチャンクに設定することが望ましいサイズ(またはその倍数)は、現在のNSStreamベースのソリューションと同じパフォーマンスを提供します。

implementationでGCD IOバッファポリシーの詳細を確認できます。

いずれにしても、デフォルトのGCD IOバッファリングに関するパフォーマンス上の問題がある場合は、bugの内容を必ずご記入ください。

+0

驚くばかりです。私は実装を読んで、 'dispatch_data_t'と' dispatch_data_create_concat'と 'dispatch_data_create_subrange'を使って正しい量の*コールバック*を得ることができましたが、実際には' read'/'write'コール(私は二重チェックしたい)とウォーターマークで遊んで私に良い解決策を与えるかどうかを確認する必要があります。より多くのテストをするために周回すると更新されます。 – DarkDust

+0

大丈夫なので、 'read'と' write'のsyscallsは、期待どおりに最高水準点をそれぞれの優先IOサイズに設定すると、実際に期待されるパラメータを持っています。それで私は私の "問題"を解決することができます。低水準点を設定することは、ブロックが「短い」読み取り/書き込みのために呼び出される頻度に影響するだけで、読み取りデータを累積し、手動で書き込みするためにパーティションをスケジュールする必要があります。もちろん、問題はありません。 – DarkDust

+2

しかし、「最適化された」バージョンは、「純粋な」実装よりも速く実行されないことが判明しました。これは、カーネルがすでにスマートであり、NSStreamとGCDのソリューションを比較すると、 。ああ。しかし、私を助けてくれてありがとう。 – DarkDust

関連する問題