2009-07-24 10 views
17

私のバイナリからテキストデコードアプリケーション(.NET 2.0)では、次の行が見つかりました。DateTime.ToStringを使用してコードのパフォーマンスを向上させるにはどうすればよいですか?

logEntryTime.ToString("dd.MM.yy HH:mm:ss:fff") 

は合計処理時間の33%を要します。誰もがそれをより速くする方法に関するアイデアはありますか?

編集:このアプリケーションはいくつかのバイナリログを処理するために使用され、現在は実行に15時間かかります。だから1/3は5時間です。

編集:プロファイリングにNProfを使用しています。アプリケーションは約17 GBytesのバイナリログを処理しています。

+4

33%? 2msの処理時間の33%はリファクタの価値がないかもしれません。 –

+0

また、33%を決定するためにどのツールを使用しましたか?異なるツールは時々微妙に異なるものを測定するので、それは知る価値があります。 – Stobor

+2

結果の文字列を出力にどのように追加しますか? Stringbuilder? –

答えて

13

.NETには、パターンを解析して覚えておくことのできる「フォーマッタ」型の型がないことは残念です。

常に同じフォーマットを使用している場合は、フォーマッタを手作業で作成してください。線に沿って何か:

public static string FormatDateTime(DateTime dt) 
{ 
    char[] chars = new char[21]; 
    Write2Chars(chars, 0, dt.Day); 
    chars[2] = '.'; 
    Write2Chars(chars, 3, dt.Month); 
    chars[5] = '.'; 
    Write2Chars(chars, 6, dt.Year % 100); 
    chars[8] = ' '; 
    Write2Chars(chars, 9, dt.Hour); 
    chars[11] = ' '; 
    Write2Chars(chars, 12, dt.Minute); 
    chars[14] = ' '; 
    Write2Chars(chars, 15, dt.Second); 
    chars[17] = ' '; 
    Write2Chars(chars, 18, dt.Millisecond/10); 
    chars[20] = Digit(dt.Millisecond % 10); 

    return new string(chars); 
} 

private static void Write2Chars(char[] chars, int offset, int value) 
{ 
    chars[offset] = Digit(value/10); 
    chars[offset+1] = Digit(value % 10); 
} 

private static char Digit(int value) 
{ 
    return (char) (value + '0'); 
} 

これはかなり醜いですが、それはもちろん、おそらくそれより多くの効率的な...ベンチマークです!

+0

笑;私たちは非常に似たようなコードに取り組んでいたと思います! –

+1

@Marc偉大な心は似ていると思う! – inspite

+8

...と愚か者はめったに違いはありません:) –

9

時間の33%かかると確信していますか?どのように測定したのですか?

Basic: 2342ms 
Custom: 1319ms 

それとも我々はIO(Stream.Null)を切り出す場合:これは、これは物事が少しビット速くなり

...私には少し疑わしい以上に聞こえる

Basic: 2275ms 
Custom: 839ms 

using System.Diagnostics; 
using System; 
using System.IO; 
static class Program 
{ 
    static void Main() 
    { 
     DateTime when = DateTime.Now; 
     const int LOOP = 1000000; 

     Stopwatch basic = Stopwatch.StartNew(); 
     using (TextWriter tw = new StreamWriter("basic.txt")) 
     { 
      for (int i = 0; i < LOOP; i++) 
      { 
       tw.Write(when.ToString("dd.MM.yy HH:mm:ss:fff")); 
      } 
     } 
     basic.Stop(); 
     Console.WriteLine("Basic: " + basic.ElapsedMilliseconds + "ms"); 

     char[] buffer = new char[100]; 
     Stopwatch custom = Stopwatch.StartNew(); 
     using (TextWriter tw = new StreamWriter("custom.txt")) 
     { 
      for (int i = 0; i < LOOP; i++) 
      { 
       WriteDateTime(tw, when, buffer); 
      } 
     } 
     custom.Stop(); 
     Console.WriteLine("Custom: " + custom.ElapsedMilliseconds + "ms"); 
    } 
    static void WriteDateTime(TextWriter output, DateTime when, char[] buffer) 
    { 
     buffer[2] = buffer[5] = '.'; 
     buffer[8] = ' '; 
     buffer[11] = buffer[14] = buffer[17] = ':'; 
     Write2(buffer, when.Day, 0); 
     Write2(buffer, when.Month, 3); 
     Write2(buffer, when.Year % 100, 6); 
     Write2(buffer, when.Hour, 9); 
     Write2(buffer, when.Minute, 12); 
     Write2(buffer, when.Second, 15); 
     Write3(buffer, when.Millisecond, 18); 
     output.Write(buffer, 0, 21); 
    } 
    static void Write2(char[] buffer, int value, int offset) 
    { 
     buffer[offset++] = (char)('0' + (value/10)); 
     buffer[offset] = (char)('0' + (value % 10)); 
    } 
    static void Write3(char[] buffer, int value, int offset) 
    { 
     buffer[offset++] = (char)('0' + (value/100)); 
     buffer[offset++] = (char)('0' + ((value/10) % 10)); 
     buffer[offset] = (char)('0' + (value % 10)); 
    } 
} 
+2

私は完全にそれを信じていません - 私はいくつかのログテストを行ったときに、フォーマットと解析の日時がCPUアクセスを支配することが分かりました。確かに、それは事実上ロギング以外は何もしていませんでした。 –

+0

マイクロベンチマークはIOバウンドです。CPUサイクルを測定したい場合は、おそらく書き込みをファイルに捨てるべきでしょう。 –

+1

はい、しかしOPはファイルに書き込んでいるので、これを無視するのはちょっと人工的ですが、実際にはStream.Nullを使用するとその違いがもっと分かります。 –

0

バイナリとテキストの各レコードの大きさを知っていますかログはどうなるの?そうであれば、ログファイルの処理を複数のスレッドに分割することができ、マルチコア/プロセッサーPCの使用率が向上します。結果が別々のファイルであることに気にしない場合は、コアごとに1つのハードディスクを用意し、ディスクヘッドを移動する必要がある量を減らすことをお勧めします。

1

これは、「S」(ISO)形式のバリアントを提供し、それ自体が答えではなく、ジョンスキートのスチールハーフメッシュマスク答えにaddedumではありません。

/// <summary> 
    ///  Implements a fast method to write a DateTime value to string, in the ISO "s" format. 
    /// </summary> 
    /// <param name="dateTime">The date time.</param> 
    /// <returns></returns> 
    /// <devdoc> 
    ///  This implementation exists just for performance reasons, it is semantically identical to 
    ///  <code> 
    /// text = value.HasValue ? value.Value.ToString("s") : string.Empty; 
    /// </code> 
    ///  However, it runs about 3 times as fast. (Measured using the VS2015 performace profiler) 
    /// </devdoc> 
    public static string ToIsoStringFast(DateTime? dateTime) { 
     if (!dateTime.HasValue) { 
      return string.Empty; 
     } 
     DateTime dt = dateTime.Value; 
     char[] chars = new char[19]; 
     Write4Chars(chars, 0, dt.Year); 
     chars[4] = '-'; 
     Write2Chars(chars, 5, dt.Month); 
     chars[7] = '-'; 
     Write2Chars(chars, 8, dt.Day); 
     chars[10] = 'T'; 
     Write2Chars(chars, 11, dt.Hour); 
     chars[13] = ':'; 
     Write2Chars(chars, 14, dt.Minute); 
     chars[16] = ':'; 
     Write2Chars(chars, 17, dt.Second); 
     return new string(chars); 
    } 

4桁のシリアライザと同様、 :

private static void Write4Chars(char[] chars, int offset, int value) { 
     chars[offset] = Digit(value/1000); 
     chars[offset + 1] = Digit(value/100 % 10); 
     chars[offset + 2] = Digit(value/10 % 10); 
     chars[offset + 3] = Digit(value % 10); 
    } 

これは約3倍高速です。 (VS2015パフォーマンスプロファイラを使用して測定)

関連する問題