2012-11-13 13 views
7

現在の私のプロジェクトでは、文字列を解析し、その部分をコンソールに書き込む必要があります。あまりオーバーヘッドをかけずにこれを行う方法をテストする中で、私がテストしている方法のほうが実際にはConsole.WriteLineよりも高速であることがわかりました。コンソールのcharへの書き込み、最も速い方法

ベンチマークの適切な方法ではありませんが、私は通常これよりも速く、これは数回実行した後ではわかりやすい、大まかではありません。

static void Main(string[] args) 
{ 
    var timer = new Stopwatch(); 

    timer.Restart(); 
    Test1("just a little test string."); 
    timer.Stop(); 
    Console.WriteLine(timer.Elapsed); 

    timer.Restart(); 
    Test2("just a little test string."); 
    timer.Stop(); 
    Console.WriteLine(timer.Elapsed); 

    timer.Restart(); 
    Test3("just a little test string."); 
    timer.Stop(); 
    Console.WriteLine(timer.Elapsed); 
} 

static void Test1(string str) 
{ 
    Console.WriteLine(str); 
} 

static void Test2(string str) 
{ 
    foreach (var c in str) 
     Console.Write(c); 
    Console.Write('\n'); 
} 

static void Test3(string str) 
{ 
    using (var stream = new StreamWriter(Console.OpenStandardOutput())) 
    { 
     foreach (var c in str) 
      stream.Write(c); 
     stream.Write('\n'); 
    } 
} 

ご覧のとおり、Test1はConsole.WriteLineを使用しています。私の最初の考えは、すべてのcharに対してWriteを呼び出すことでした.Test2を参照してください。しかし、これは約2倍の時間を要しました。私の推測では、すべての書き込みが完了するとフラッシュされるため、遅くなります。そこで私はStreamWriter(AutoFlush off)を使ってTest3を試しましたが、結果として約25%であるがTest1よりも速くであり、私は本当に興味があります。それとも、コンソールへの書き込みが適切にベンチマークされないのでしょうか? (さらに多くのテストケースを追加すると、いくつかの奇妙なデータに気が付いた...)

誰かが私を啓発できますか?

また、これを行うためのより良い方法がある場合(文字列を使用し、その一部をコンソールに書き込むだけです)、自由にコメントしてください。

+1

ラン 'Test1'最後と顕著な違いがあるのか​​どうかを確認:ここでは

は、上記の数値を得るために書き換えテストです。私は、ある種のキャッシュがあると推測しています。 – keyboardP

+4

正確なベンチマークを得るには、それぞれ数千回ループする必要があると思いますか? – James

+0

あなたは時期尚早に最適化していますか? – BAF

答えて

5

最初に、あなたのテストハーネスが何かを残しているとの他のコメントに同意します...私はそれを書き直して以下に含めました。あなたはバッファされたストリーム・ライターが良くより25%高速である適切である。このことから

//Test 1 = 00:00:03.7066514 
//Test 2 = 00:00:24.6765818 
//Test 3 = 00:00:00.8609692 

:書き換え後の結果は、明確な勝者を投稿してください。それは、バッファリングされているためだけに高速です。内部的には、StreamWriterの実装では、(ストリームの種類に応じて)約1〜4kbのデフォルトのバッファサイズが使用されます。 StreamWriterを8バイトのバッファ(最小の許容値)で構築すると、ほとんどのパフォーマンスの向上が見えなくなります。それぞれの書き込みの後にFlush()を呼び出すことで、これを見ることもできます。

private static StreamWriter stdout = new StreamWriter(Console.OpenStandardOutput()); 
    static void Main(string[] args) 
    { 
     Action<string>[] tests = new Action<string>[] { Test1, Test2, Test3 }; 
     TimeSpan[] timming = new TimeSpan[tests.Length]; 

     // Repeat the entire sequence of tests many times to accumulate the result 
     for (int i = 0; i < 100; i++) 
     { 
      for(int itest =0; itest < tests.Length; itest++) 
      { 
       string text = String.Format("just a little test string, test = {0}, iteration = {1}", itest, i); 
       Action<string> thisTest = tests[itest]; 

       //Clear the console so that each test begins from the same state 
       Console.Clear(); 
       var timer = Stopwatch.StartNew(); 
       //Repeat the test many times, if this was not using the console 
       //I would use a much higher number, say 10,000 
       for (int j = 0; j < 100; j++) 
        thisTest(text); 
       timer.Stop(); 
       //Accumulate the result, but ignore the first run 
       if (i != 0) 
        timming[itest] += timer.Elapsed; 

       //Depending on what you are benchmarking you may need to force GC here 
      } 
     } 

     //Now print the results we have collected 
     Console.Clear(); 
     for (int itest = 0; itest < tests.Length; itest++) 
      Console.WriteLine("Test {0} = {1}", itest + 1, timming[itest]); 
     Console.ReadLine(); 
    } 

    static void Test1(string str) 
    { 
     Console.WriteLine(str); 
    } 

    static void Test2(string str) 
    { 
     foreach (var c in str) 
      Console.Write(c); 
     Console.Write('\n'); 
    } 

    static void Test3(string str) 
    { 
     foreach (var c in str) 
      stdout.Write(c); 
     stdout.Write('\n'); 
    } 
+0

このスレッドは安全ですか?また、私は、OSアーキテクチャがWin10で変更されていることを理解しています。 – TheGeekZn

3

私は10000回ごとのテストを実行してきましたし、結果は私のマシン上で、次のとおりです。

test1 - 0.6164241 
test2 - 8.8143273  
test3 - 0.9537039 

これは私が使用したスクリプトです:

static void Main(string[] args) 
     { 
      Test1("just a little test string.");  // warm up 
      GC.Collect(); // compact Heap 
      GC.WaitForPendingFinalizers(); // and wait for the finalizer queue to empty 
      Stopwatch timer = new Stopwatch(); 
      timer.Start(); 
      for (int i = 0; i < 10000; i++) 
      { 
       Test1("just a little test string."); 
      } 
      timer.Stop(); 
      Console.WriteLine(timer.Elapsed); 
     } 
2

私はコードへの変更しました各テストを1000回実行します。

static void Main(string[] args) { 
     var timer = new Stopwatch(); 

     timer.Restart(); 
     for (int i = 0; i < 1000; i++) 
      Test1("just a little test string."); 
     timer.Stop(); 
     TimeSpan elapsed1 = timer.Elapsed; 

     timer.Restart(); 
     for (int i = 0; i < 1000; i++) 
      Test2("just a little test string."); 
     timer.Stop(); 
     TimeSpan elapsed2 = timer.Elapsed; 

     timer.Restart(); 
     for (int i = 0; i < 1000; i++) 
      Test3("just a little test string."); 
     timer.Stop(); 
     TimeSpan elapsed3 = timer.Elapsed; 

     Console.WriteLine(elapsed1); 
     Console.WriteLine(elapsed2); 
     Console.WriteLine(elapsed3); 

     Console.Read(); 
    } 

マイ出力:

00:00:05.2172738 
00:00:09.3893525 
00:00:05.9624869 
+0

あなたのマシンはslooooooowです – RAS

+1

私はもっと私のコンソール設定だと思います。コンソールウィンドウを80x40に縮小すると、1と2がサブ秒に戻ります。 – Blorgbeard

2

また、私はこの1万回を走ったし、これらの結果を得た:他の人が観察するものと調和しているようだ

00:00:00.6947374 
00:00:09.6185047 
00:00:00.8006468 

を。

timer.Start(); 
using (var stream = new StreamWriter(Console.OpenStandardOutput())) 
{ 
    for (int i = 0; i < testSize; i++) 
    { 
     Test4("just a little test string.", stream); 
    } 
} 
timer.Stop(); 

この1は、このようにそれを毎回再作成のオーバーヘッドを回避、各テストのためのストリームを再利用:Test3Test1より遅かったなぜ私はそう第四テストを書いて、興味がありました。結果:

00:00:00.4090399 

これが最速ですが、それはあなたが後にあるものにならない可能性がある、usingブロックの最後にすべての出力を書き込みます。私は、このアプローチがより多くの記憶を噛み砕くであろうと想像するでしょう。

関連する問題