StringBuilder はどれだけ早いのだろうか?、実は大してかわりません

SqlCommand や DataTable を使うときに文字列をたくさん使うのですが、果たして世間一般(?)で言われているほど、String は遅く、StringBuilder は早いのでしょうか?というベンチマークです。

非常に長い場合は StringBuilder を使うほうが良いのですが、SqlCommand などに渡す SQL 文程度(長くても 5000 文字ぐらい)は、どうなのでしょうか?ということです。

■結論

結論から言えば、大して変わりません。以下は 20,000 件のデータを廻したときの結果です。

avg. Normal 7.54 sec
avg. StirngBuilder 7.22 sec
avg. SqlCommand 7.28 sec

違いは 2,3 % ぐらいしかないので誤差の範囲ですね。

以下は、ベンチマーク用の実験コード

■通常の String パターン

''' <summary>
''' 1.通常の string の連結で作成
''' </summary>
''' <remarks></remarks>
Public Sub TestNormal()
    Dim s As String = toMD5(DateTime.Now.ToString())

    Dim cn As New SqlConnection(&quot;&quot;)
    For j As Integer = 0 To MAX - 1
        Dim sql As String = &quot;&quot;
        ' よくある string.Format を使った作り方
        sql += &quot;SELECT * FROM DUAL &quot;
        sql += &quot; WHERE 1 = 1 &quot;
        sql += String.Format(&quot; AND col0 = '{0}' &quot;, s) : s = toMD5(s)
        sql += String.Format(&quot; AND col1 = '{0}' &quot;, s) : s = toMD5(s)
        sql += String.Format(&quot; AND col2 = '{0}' &quot;, s) : s = toMD5(s)
        sql += String.Format(&quot; AND col3 = '{0}' &quot;, s) : s = toMD5(s)
        sql += String.Format(&quot; AND col4 = '{0}' &quot;, s) : s = toMD5(s)
        sql += String.Format(&quot; AND col5 = '{0}' &quot;, s) : s = toMD5(s)
        sql += String.Format(&quot; AND col6 = '{0}' &quot;, s) : s = toMD5(s)
        sql += String.Format(&quot; AND col7 = '{0}' &quot;, s) : s = toMD5(s)
        sql += String.Format(&quot; AND col8 = '{0}' &quot;, s) : s = toMD5(s)
        sql += String.Format(&quot; AND col9 = '{0}' &quot;, s) : s = toMD5(s)

        Dim dt As New DataTable
        Dim da As New SqlDataAdapter(sql, cn) 'dummy
    Next
End Sub

string.Format を使って、ぽちぽちと文字列をフォーマットしていきます。

■StringBuilder を使ったパターン

よくあるように、文字列の連結は StringBuilder を使えッ!!! ってことなので、使ってみたのですが、大して変わりません。もっと長い SQL の場合、効果があるんでしょうが…そんなに長い SQL を書くのはどうなの???ってことなのです。

''' <summary>
''' 2.StringBuilder を使う
''' </summary>
''' <remarks></remarks>
Public Sub TestStringBuilder()
    Dim s As String = toMD5(DateTime.Now.ToString())

    Dim cn As New SqlConnection(&quot;&quot;)
    For j As Integer = 0 To MAX - 1
        Dim sql As String = &quot;&quot;
        ' よくある StringBuilder を使った作り方
        Dim sb As New System.Text.StringBuilder(&quot;&quot;)
        sb.Append(&quot;SELECT * FROM DUAL &quot;)
        sb.Append(&quot; WHERE 1 = 1 &quot;)
        sb.AppendFormat(&quot; AND col0 = '{0}' &quot;, s) : s = toMD5(s)
        sb.AppendFormat(&quot; AND col1 = '{0}' &quot;, s) : s = toMD5(s)
        sb.AppendFormat(&quot; AND col2 = '{0}' &quot;, s) : s = toMD5(s)
        sb.AppendFormat(&quot; AND col3 = '{0}' &quot;, s) : s = toMD5(s)
        sb.AppendFormat(&quot; AND col4 = '{0}' &quot;, s) : s = toMD5(s)
        sb.AppendFormat(&quot; AND col5 = '{0}' &quot;, s) : s = toMD5(s)
        sb.AppendFormat(&quot; AND col6 = '{0}' &quot;, s) : s = toMD5(s)
        sb.AppendFormat(&quot; AND col7 = '{0}' &quot;, s) : s = toMD5(s)
        sb.AppendFormat(&quot; AND col8 = '{0}' &quot;, s) : s = toMD5(s)
        sb.AppendFormat(&quot; AND col9 = '{0}' &quot;, s) : s = toMD5(s)

        Dim dt As New DataTable
        Dim da As New SqlDataAdapter(sql, cn) 'dummy
    Next
End Sub

■SqlCommand を使う

SqlCommand を使うと、最初に SQL 文を使うので文字列編集部分が極端に減ります。
これの場合は、20000 回の編集が 1 回になるわけですが、劇的に早くなる…はずなんですけど、結果は変わりません。

''' <summary>
''' SqlCommand で Const/Dim  を使う
''' </summary>
''' <remarks></remarks>
Public Sub TestConst()
    Dim s As String = toMD5(DateTime.Now.ToString())
    Dim cn As New SqlConnection(&quot;&quot;)

    ' ここは dim でも同じ
    Const sql As String = _
        &quot;SELECT * FROM DUAL0 &quot; + _
        &quot; WHERE 1 = 1 &quot; + _
        &quot; AND col0 = @col0 &quot; + _
        &quot; AND col1 = @col1 &quot; + _
        &quot; AND col2 = @col2 &quot; + _
        &quot; AND col3 = @col3 &quot; + _
        &quot; AND col4 = @col4 &quot; + _
        &quot; AND col5 = @col5 &quot; + _
        &quot; AND col6 = @col6 &quot; + _
        &quot; AND col7 = @col7 &quot; + _
        &quot; AND col8 = @col8 &quot; + _
        &quot; AND col9 = @col9 ; &quot;

    Dim cmd As New SqlCommand(sql, cn)
    cmd.Parameters.Add(New SqlParameter(&quot;@col0&quot;, Nothing))
    cmd.Parameters.Add(New SqlParameter(&quot;@col1&quot;, Nothing))
    cmd.Parameters.Add(New SqlParameter(&quot;@col2&quot;, Nothing))
    cmd.Parameters.Add(New SqlParameter(&quot;@col3&quot;, Nothing))
    cmd.Parameters.Add(New SqlParameter(&quot;@col4&quot;, Nothing))
    cmd.Parameters.Add(New SqlParameter(&quot;@col5&quot;, Nothing))
    cmd.Parameters.Add(New SqlParameter(&quot;@col6&quot;, Nothing))
    cmd.Parameters.Add(New SqlParameter(&quot;@col7&quot;, Nothing))
    cmd.Parameters.Add(New SqlParameter(&quot;@col8&quot;, Nothing))
    cmd.Parameters.Add(New SqlParameter(&quot;@col9&quot;, Nothing))

    For j As Integer = 0 To max - 1
        cmd.Parameters(&quot;@col0&quot;).Value = s : s = toMD5(s)
        cmd.Parameters(&quot;@col1&quot;).Value = s : s = toMD5(s)
        cmd.Parameters(&quot;@col2&quot;).Value = s : s = toMD5(s)
        cmd.Parameters(&quot;@col3&quot;).Value = s : s = toMD5(s)
        cmd.Parameters(&quot;@col4&quot;).Value = s : s = toMD5(s)
        cmd.Parameters(&quot;@col5&quot;).Value = s : s = toMD5(s)
        cmd.Parameters(&quot;@col6&quot;).Value = s : s = toMD5(s)
        cmd.Parameters(&quot;@col7&quot;).Value = s : s = toMD5(s)
        cmd.Parameters(&quot;@col8&quot;).Value = s : s = toMD5(s)
        cmd.Parameters(&quot;@col9&quot;).Value = s : s = toMD5(s)
        Dim dt As New DataTable
        Dim da As New SqlDataAdapter(cmd) 'dummy
    Next
End Sub

以上、こんな風に実験してみると普通に string を使っている限りはスピードに変化はありません、ってことです。
ただし、データベースアクセスに関しては、単純な SqlDataAdapter の呼び出しよりも、SqlCommand でプリコンパイル SQL を使ったほうが、10 倍以上早くなるので、件数が多い場合はパフォーマンスに注意が必要です。

カテゴリー: 開発, C# パーマリンク

StringBuilder はどれだけ早いのだろうか?、実は大してかわりません への5件のフィードバック

  1. はに丸 のコメント:

    根本的にいろいろ間違ってますね。
    まず、文字列連結有無にかかわらずバインド変数を使うべきです。
    それと、文字列連結のスピードは連結回数の総和が問題では無く、大きな文字列に連結することが問題となります。
    例えば、csv出力なんかで、1行のデータを作成する為に連結する場合はstring結合はまったく問題になりませんが、行そのものも縦方向に連結する際には問題となります。

    • masuda のコメント:

      I see. このネタは、文字列連結してSQL組み立てるとアカンからストアド使いましょう、のネタのつもりだったのですが、実際組み立ててみたら(ストアドを使ってないけど)「たいして時間が変わらなかった」ってネタ話です。その頃、「Java で StringBuilder を使わねばいかん」って話が盛り上がっていたので、別に += でも変わらん、っていうカウンターでもありましたが。

  2. 通りすがり のコメント:

    ストアドを使用したほうがいいのはそもそもVBA的な観点からではないでしょう・・

  3. 匿名 のコメント:

    この例ですと文字列の連結よりFormatの解釈処理に時間がかかって
    差が付かなかったのではないでしょうか?

    • masuda のコメント:

      .NET の GC を見ると、半分ぐらいは文字列処理に使っているので、その可能性は高いですね。const 文字列を使うよりも、enum で int 型を使ったほうが早い(メモリの解放とか文字列処理がない分だけ早い)ってのを、一度やってみたいと思ってます。

コメントは停止中です。