#20 可変長文字列と固定長文字列を比較する

結論
固定長文字列よりも可変長文字列の方がパフォーマンスが高い。固定長文字列は、レコード型変数やWindowsAPIの引数などの必要なところだけで使うようにする。

文字列型(String)の変数には「可変長文字列」と「固定長文字列」の2種類があります。あらかじめ型宣言で長さを指定する必要もなく、しかも最大で約2GBまでという、実質的には制限を考えることなく文字データを扱える可変長文字列と、"Dim strData As String * 20" というようにその長さの上限を決めた上で文字データを扱う固定長文字列です。

テーブルのレコードを読み込む際に、"strNamae = rst!名前" といったコードをしばしば使うと思います。テーブル上でテキスト型として定義されたフィールドデータを、文字列型変数にいったんセットして後処理を行うというパターンです。このとき、フィールド型が「メモ型」ならともかく、「テキスト型」の場合にはテーブル上であらかじめそのフィールドサイズは指定されているはずです。それならば、「それを代入する変数はそれと同じ長さにしておいた方が効率的ではないか?」と疑問に思ったことはありませんか?。もしフィールドサイズが20なら、"Dim strNamae As String * 20" というように。 

これら2種類の文字列型には、その長さの制約の違いはともかくとして、文字列を処理する上でのパフォーマンスにはどのような違いがあるのでしょうか?。固定長文字列の場合には数値型と同様に、常にメモリ上で一定の位置に一定のサイズの領域(String * 20 なら20バイト)を確保していると考えられます(これは不確定な推測です)。一方、可変長文字列の場合にはどうでしょう?。たとえ"最大で約2GBまで"といっても型宣言の段階で2GBの領域を押さえておくはずがありません。文字列に値が代入されるごとに、あるいは結合されたりするごとに、メモリ領域の占有する位置とサイズを変えていく、言いかえればあちこちメモリ上を移動していくと考えられます(これもあくまで推測です)。こう考える理由は、例えば"あいうえお"という文字列の後ろに"かきくけこ"という文字列を付け加えるとします。このとき、もしそのままのメモリアドレスに付け加えたとすると、"かきくけこ"の部分の文字列領域が他の変数のメモリ領域と衝突してしまう可能性があるからです。もしそういった動きをしているとしたら、可変長文字列の方はおのずとパフォーマンスが落ちそうな感じがします。

そんな疑問と推測をもとに、ここでは2種類の文字列型について次の4つの処理を行い、その処理時間を測定・比較してみました。文字列処理の関数については関数ごとの特性からそれぞれ異なる結果が出そうですが、ここでは「Mid$関数」だけを取り上げてみました。

  1. 短いテキストの代入
  2. 長いテキストの代入
  3. 文字列の結合(後ろにどんどん文字をつなげていく)
  4. Mid$関数による部分文字列の取り出し
テストコードは次のようなものです。

  Dim strFixed As String * 4096     '固定長文字列を宣言
  Dim strVariable As String           '可変長文字列を宣言
  Dim strTemp As String
  Dim iintLoop As Integer             'テスト回数のループカウンタ
  Const cintTestMax As Integer = 4000 'テスト回数
  
  ts_Watch "テスト開始", True
  
  '短いテキストの代入*********************
  For iintLoop = 1 To cintTestMax
    strFixed = "1234"
  Next iintLoop
  ts_Watch "固定長への短いテキスト"
  
  For iintLoop = 1 To cintTestMax
    strVariable = "1234"
  Next iintLoop
  ts_Watch "可変長への短いテキスト"
  
  '長いテキストの代入*********************
  For iintLoop = 1 To cintTestMax
    strFixed = String$(4000, "A")
  Next iintLoop
  ts_Watch "固定長への長いテキスト"
  
  For iintLoop = 1 To cintTestMax
    strVariable = String$(4000, "A")
  Next iintLoop
  ts_Watch "可変長への長いテキスト"

  '文字列の結合*********************
  strFixed = ""
  For iintLoop = 1 To cintTestMax
    strFixed = strFixed & "A"
  Next iintLoop
  ts_Watch "固定長の文字列結合"
  
  strVariable = ""
  For iintLoop = 1 To cintTestMax
    strVariable = strVariable & "A"
  Next iintLoop
  ts_Watch "可変長の文字列結合"

  '部分文字列の取り出し*********************
  strTemp = String$(4000, "A")
  For iintLoop = 1 To cintTestMax
    strFixed = Mid$(strTemp, 2000, 100)
  Next iintLoop
  ts_Watch "固定長の文字列取り出し"
  
  strVariable = String$(4000, "A")
  For iintLoop = 1 To cintTestMax
    strVariable = Mid$(strTemp, 2000, 100)
  Next iintLoop
  ts_Watch "可変長の文字列取り出し"



そして、テスト結果はつぎのようなものになりました。
文字列処理 固定長文字列 可変長文字列
長いテキストの代入 1.49 0.71
短いテキストの代入 0.70 0.05
文字列の結合 1.26 0.06
文字列の取り出し 0.75 0.07
最初の推測は完全に覆されてしまいました。まったく反対の結果で、すべてのテストについて固定長文字列よりも可変長文字列の方がパフォーマンスが高いことは明らかです。残念ながら、どのような内部動作が理由でこのようになるのかは分かりません(勉強不足です)。しかし、可変長文字列は、文字列の長さを気にせず扱えかつ固定長文字列よりも高速な処理が可能な、大変都合のよいデータ型であることは確かなようです。そして、固定長文字列は、構造体(レコード型変数)やWindowsAPIの引数など、どうしても必要なところだけで使うようにするのがよいといえるでしょう。最後に、今回のテストからの教訓・・・・・・勝手な想像でパフォーマンスの良し悪しを決め付けてはいけない!。
| Index | Prev | Next |

 

Copyright © T'sWare All rights reserved