ecl.js (Escape Codec Library) と Sleipnir の相性が悪い件について
とあるサイトで ecl.js を使っています。ecl.js とは JavaScript で文字コード変換を行うライブラリで Escape Codec Library が正式名称です。配布元は Escape Codec Library: ecl.js です。ちなみに yaplog に組み込まれている /blog/js/urlEnDecoding.js も元ネタは ecl.js みたいですね。もちろん Sleipnir で yaplog 見ると激遅です・・・
文字列をすべてのコンピュータで読めるような形式に変換したり、変換されたものを元の文字列にデコードすることができる関数のライブラリです。
ビルトイン関数 escape() , unescape() とは異なり、どの種類のブラウザでも同じ変換結果が得られます。
JISコード変換テーブルを搭載し、従来JavaScriptでは実現が困難であった、Shift_JISコードやEUC-JPコードなどの文字コードとしてのエンコード・デコードも可能となっています。なお、この変換テーブルには、直接漢字は記述せず、Unicode 番号を圧縮したデータをASCIIコードの文字で記述しているので、任意の文字コードで使えるという特徴があります。
Internet Explorer 5.0以上、Netscape 7、Operaなどで使用可能です。
さてこの ecl.js ですが、どうも僕が愛用している Sleipnir と相性がめちゃくちゃ悪いようです。ecl.js が組み込まれていると数秒間フリーズしたかのような状況になります。というのをたまたま他の情報を探していたときに
Sleipnirで50タブくらい開いている状態でEscape Codec Library: ecl.jsを使用しているサイトにアクセスすると激しく重くなる件 - 文殊堂
で見つけました。
遅い原因をちょっと調べてみて不具合が発生しないように改良を加えてみました。
まず処理が思い場所は JCT11280 文字列を生成する無名関数部分です。JCT11280 文字列には何が入っているかは ecl.js のページに説明がありまして
これを生成する関数は、11280文字分のUnicode 番号を差分圧縮して、ASCIIコードの文字のみを使って符号化したデータとデコードプログラムからなり、ファイル読み込み時に実行されて、元の文字を生成するようになっています。この仕組みにより、11280文字を記述するのに必要なバイト数の半分近くにまでサイズが縮小され、そして漢字等が直接記述されていないので、任意の文字コードで使用することが可能となります。
また、</ といったブラウザを混乱させるおそれのある文字列も含まれていないので、HTMLファイルに直接この生成関数を記述することも可能です。
です。11280文字のJIS漢字を JavaScript が load されたときに動的に生成しているわけです。でもって Sleipnir で遅くなる理由はその生成アルゴリズムにあるわけです。文字生成プログラムの後半部分を見やすく展開すると
c=34,i=2,p,s="",u=String.fromCharCode,t=u(12539); while(++c<127)C[u(c)]=c^39&&c^92?i++:0; i=0; while(0<=(c=C[a.charAt(i++)]))if(16==c)if((c=C[a.charAt(i++)])<87){if(86==c)c=1879; while(c--)s+=u(++p)}else s+=s.substr(8272,360); else if(c<86)s+=u(p+=c<51?c-16:(c-55)*92+C[a.charAt(i++)]); else if((c=((c-86)*92+C[a.charAt(i++)])*92+C[a.charAt(i++)])<49152)s+=u(p=c<40960?c:c|57344); else{c&=511; while(c--)s+=t; p=12539}return s')();
となっています。文字列を生成する毎に + 演算子を使って文字列を結合しています。この方法は、JavaScript の要素追加・変更で innetHTML と DOM の速度検証に書いたように + 演算子を使って文字列を結合すると IE 系でパフォーマンスが劣化します。そして理由は不明ですが Sleipnir では更に超絶パフォーマンスが劣化するようです。
したがって文字列の結合を配列 + join 方式に変更してみました。
c=34,i=2,p,s=[],u=String.fromCharCode,t=u(12539); while(++c<127)C[u(c)]=c^39&&c^92?i++:0; i=0; while(0<=(c=C[a.charAt(i++)]))if(16==c)if((c=C[a.charAt(i++)])<87){if(86==c)c=1879; while(c--)s.push(u(++p))}else s.push(s.join("").substr(8272,360)); else if(c<86)s.push(u(p+=c<51?c-16:(c-55)*92+C[a.charAt(i++)])); else if((c=((c-86)*92+C[a.charAt(i++)])*92+C[a.charAt(i++)])<49152)s.push(u(p=c<40960?c:c|57344)); else{c&=511; while(c--)s.push(t); p=12539}return s.join("")')();
テストプラグラムを作成してパフォーマンス測定してみました。
Sleipnir | ie6 | firefox2 | opera9 | safari3 for win | |
ecl オリジナル | 10781 ms | 141 ms | 94 ms | 63 ms | 31 ms | ecl 高速化版 | 78 ms | 62 ms | 94 ms | 62 ms | 31 ms |
というわけで Sleipnir でのパフォーマンスが改善されフリーズ状態にならなくなることが確認できました。その他のブラウザでの速度も影響はなさそうです。
改良版 ecl.js はこちらからダウンロードできます。ecl_new.js(右クリックで保存してください。)
コメントやシェアをお願いします!
OK.2nd
Webポータルシステム「MyHome Portal」で悩まされていた問題を、ecl_new.jsで解消できました。
http://ok2nd.blog87.fc2.com/blog-entry-188.html
ありがとうございました。大感謝です。
drk
da0 さん>さらなる深追い情報有り難うございます。参考にさせて頂きます。
da0
Netscape7.1の場合、圧縮文字列のaを a="...".split("") と変えてcharAt(i++)ではなく[i++]にした方が更に高速になりました。しかもcharAtより文字数が少ないので微妙に圧縮率向上
for(i=0;0<=(c=C[a[i++]]);)if(16==c)if((c=C[a[i++]])<87){
for(c<86||(c=1879);c--;)s[j++]=u(++p)}else s[j++]=s.join("").substr(8272,360);
else if(c<86)s[j++]=u(p+=c<51?c-16:(c-55)*92+C[a[i++]]);
else if((c=((c-86)*92+C[a[i++]])*92+C[a[i++]])<49152)s[j++]=u(p=c<40960?c:c|57344);
else{for(c&=511;c--;)s[j++]=t;p=12539}
return s.join("")
da0
InternetExplorer6の場合pushを使うより添字を使った方が高速でした。Netscape7.1だとpushの方が高速
C={" ":0,"!":1},c=34,i=2,j=0,p,s=[],u=String.fromCharCode,t=u(12539);
while(++c<127)C[u(c)]=c^39&&c^92?i++:0;
i=0;
while(0<=(c=C[a.charAt(i++)]))if(16==c)if((c=C[a.charAt(i++)])<87){if(86==c)c=1879;
while(c--)s[j++]=u(++p)}else s[j++]=s.join("").substr(8272,360);
else if(c<86)s[j++]=u(p+=c<51?c-16:(c-55)*92+C[a.charAt(i++)]);
else if((c=((c-86)*92+C[a.charAt(i++)])*92+C[a.charAt(i++)])<49152)s[j++]=u(p=c<40960?c:c|57344);
else{c&=511;
while(c--)s[j++]=t;
p=12539}return s.join("")