2013年4月11日

JavaScript と正規表現の速度

UnMHT の中では主に MHT ファイルや、内部の HTML ファイルのパースの部分で文字列処理を多用するのですが、ファイルが大きくなってくるとこの処理の速度がけっこう体感として重要になってきます。

今のコードの基礎部分はけっこう昔に作ったもので、その頃は Firefox の JavaScript の実行速度はそんなに早いものではなく、JavaScript でループ回したり複数回比較するよりも正規表現を使って match、replace、split した方が速いという場合があったのですが、ハテ今は事情違うんじゃないの?ってコトで再調査です。

var fstream
= Cc ["@mozilla.org/network/file-input-stream;1"]
  .createInstance (Ci.nsIFileInputStream);

fstream.init (file, -1, 0, 0);

var bstream
= Cc ["@mozilla.org/binaryinputstream;1"]
  .createInstance (Ci.nsIBinaryInputStream);
bstream.setInputStream (fstream);

var data = bstream.readBytes (file.fileSize);

bstream.close ();

var boundary = "----=_Part_73251CB_12DB0E1.1365674809317";

var calcTime = function (name, f, count) {
  var startTime = new Date ().getTime ();
  
  for (var i = 0; i < count; i ++) {
    f ();
  }
  
  var endTime = new Date ().getTime ();
  
  show (name + ": " + (endTime - startTime) + " [ms]\n");
};

var parseBoundaryS = function () {
  var parts = [];
  
  var midBoundary = "\r\n--" + boundary + "\r\n";
  var midBoundaryLen = midBoundary.length;
  
  var endBoundary = "\r\n--" + boundary + "--\r\n";
  var endBoundaryLen = endBoundary.length;
  
  var end = 0;
  
  for (;;) {
    var start = data.indexOf (midBoundary, end);
    if (start == -1) {
      start = data.indexOf (endBoundary, end);
      if (start == -1) {
        parts.push (data.substr (end));
      }
      else {
        parts.push (data.substr (end, start - end));
      }
      break;
    }
    
    parts.push (data.substr (end, start - end));
    end = start + midBoundaryLen;
  }
  
  return parts;
};

var parseBoundaryR = function () {
  var escaped = boundary
  .replace (/([\\\[\]\(\)\^\,\.\{\}\|\?\!\-\*\+\$])/g, "\\$1");
  var rexp = new RegExp ("\r\n--" + escaped + "(?:--)?(?:\r\n)?");
  
  return data.split (rexp);
};

calcTime ("parseBoundaryS", parseBoundaryS, 100);
calcTime ("parseBoundaryR", parseBoundaryR, 100);

parseBoundaryS は単純な文字列処理 (indexOf) によって、データを boundary で分割するもの、parseBoundaryR は正規表現 (split) によって、データを boundary で分割するものです。実際に出来上がってくるデータは多少違いますが、気にせずいきましょう。ちなみに今の UnMHT には parseBoundaryR 相当のコードが使われています。

これを Firefox 21 で実行してみます。するとどうでしょう、

parseBoundaryS: 83 [ms]
parseBoundaryR: 299 [ms]

………全然遅いじゃん! 

こりゃ以降正規表現に頼りすぎるのも考えものかも。
もちろん本格的に書き換えるならボトルネックの調査が先でしょうけど。

ちなみに回数を 100 回にして古いマシンの Firefox 2 でテストした結果 (このために let じゃなくて var で書いた)

parseBoundaryS: 520 [ms]
parseBoundaryR: 1564 [ms]

え、やっぱり遅くね?

私は何の幻覚を見たのかな……それとももっと昔のバージョンだったのかな
確かに当時はかなり色々比較した上で正規表現版使おうって決めたハズだったんだけど、自信無くなってきたゾ

0 件のコメント:

コメントを投稿