ラベル ライブラリ の投稿を表示しています。 すべての投稿を表示
ラベル ライブラリ の投稿を表示しています。 すべての投稿を表示

2018年11月19日

CPU負荷を抑えて重い処理を軽くするJavaScriptライブラリ「chillout.js」

「chillout.js」とは?


chillout.js は「処理時間を短くする」という物理的な高速化とは違い、CPU負荷を抑えてリソースに余裕を持たせ、重い処理でも軽く感じさせることでユーザーにとって体感的・心理的な高速化につなげる JavaScriptライブラリです。


重い処理から開放されるために


JavaScriptでfor文など繰り返し(ループ)処理をしたとき、重い処理により一瞬でもページが固まってしまうことがあります。
そんなときブラウザ画面は読み込み中のままローディングのくるくるが止まらなかったりゲーム中にマウスが効かなくてカクカクだったり、
Webページだけじゃなくnode.jsなど画面のいらない処理でも高CPU負荷が続くとマシンごと重くなって大変です。

特にスペックの低いPCやタブレットの場合、 CPU使用率100%の状態では加熱してきて冷却ファンが高回転になり、そのまま使い続けると冷却が追いつかずに熱暴走してしまう可能性もあるので、うっかり重いページを開いてファンが「ウイーン!」って言い出すとヒヤヒヤします。

最近のPCは性能がよくなってるのでそんな事態になりにくいかもしれませんが、自分のPCも真夏の気温で動かなくなって修理にだしたのはまだ新しい記憶です😭 (直りました)。

「CPUファンが回りっぱなしかと思ったらフリーズして電源が落ちた」なんてことになると作業途中だったらやり直しになるし、HDDやSSDなどの内部にもダメージが残るかもしれないのでなるべく処理を軽くしたいところです。

JavaScriptでCPU負荷を抑えるには?


重い処理のほとんどはループ処理によって発生します。ループの中でさらにループ、その中でさらにループ…。
ループ間の処理が重くなってくるとマシンのCPUは休む暇なく動き、結果として内部が熱くなるため冷却ファンをたくさん回すことになります。
単純に考えた場合、そうならないようループの途中で一定時間処理を休止させればいいんですが、それができません。
JavaScriptには一定の時間休む sleep のような機能がないからです

そこで、sleepするにはどうするか?というと「非同期」でループ処理します。

CPUを休ませるために非同期でループ処理する


JavaScriptのループは for文や while文、また Array に対して forEachなどがありそれぞれ同期で処理されますが、上述したように sleep して休ませることができないため、これらのループ処理を非同期で実現 します。

同期処理を非同期化するには、
  • setTimeoutを使う
  • process.nextTickを使う(node.js)
  • DOMイベントを使う
  • MessageChannelを使う

などの方法があり、一連の処理を Promise と組み合わせると非同期ループが実現できます

(setInterval や requestAnimationFrame を使うこともできますが、これらは精度を保とうとより正確にループしようとするため今回の用途には不向きです。)

今までも CPU負荷を抑えるためのライブラリをいくつか作ってきたのですが、当時は Promise という便利なものがJavaScriptになかったので自前で Deferred と呼ばれる Promise のようなものを定義していました。そのためにライブラリのサイズが大きくなるしAPIがガラパゴス化してたんですが、ようやくシンプルな実装にできたと思います。

処理時間を短くするんじゃなく、体感速度を向上させる心理的な高速化



処理の高速化というと、とにかく1ミリ秒でも処理時間を短くすることが手法とされますが、Webページやアプリ、ゲームといった、人が画面を見たり操作する場合 心理的に「速い」と感じればユーザーのストレスが減り結果として高速化につながります


Twitterでいい例があったので紹介。
これはエクセルマクロの話ですが 処理時間を短くするんじゃなく待ち時間を退屈させないようにして体感速度を向上させて、心理的な高速化しています。


CPU負荷を抑えてループ処理を軽くするJavaScriptライブラリ「chillout.js」



冒頭が長くなっちゃいましたが、ライブラリの紹介です。

chillout.js は、ループ処理中に適度な休憩(sleep)を入れてあげ、カクカクする重さを感じさせないライブラリ です。
また、処理が重くなるとでる「警告: 応答のないスクリプト」というブラウザ警告なしでJavaScriptを実行できます

ループ処理が重いときにはCPUが休まるくらいの休止時間、処理が速いときには休止時間なしか、わずかな休止時間をいれ本来のループを邪魔しないようにします

ベンチマーク


for文と chillout.repeat を比較します。

function heavyProcess() {
  var v;
  for (var i = 0; i < 5000; i++) {
    for (var j = 0; j < 5000; j++) {
      v = i * j;
    }
  }
  return v;
}

例として上のような重い処理(テスト用に5000*5000回繰り返す処理)に対して、

for文

JavaScriptのfor文。

var time = Date.now();
for (var i = 0; i < 1000; i++) {
  heavyProcess();
}
var processingTime = Date.now() - time;
console.log(processingTime);

CPUグラフ:
  • 処理時間: 107510ms.
  • CPU平均使用率(Nodeプロセス): 97.13%

CPUグラフは上のようになり、CPU均使用率は 100% までいってないけど 97.13% になりました。

chillout.repeat

これを「chillout.js」のメソッド chillout.repeat で実行します。

var time = Date.now();
chillout.repeat(1000, function(i) {
  heavyProcess();
}).then(function() {
  var processingTime = Date.now() - time;
  console.log(processingTime);
});

CPUグラフ:
  • 処理時間: 138432ms.
  • CPU平均使用率(Nodeプロセス): 73.88%

ベンチマーク結果


  ForStatement (for文) chillout.repeat
処理時間 107,510ms. 138,432ms.
CPU平均使用率(Nodeプロセス) 97.13% 73.88%


グラフでは少しわかりにくいかもしれませんが、CPU平均使用率は for文 97.13% に対し、 chillout.repeat は 73.88% となり、for文よりもCPU使用率が抑えられてます

そのかわりループ中にCPUを休ませてるため処理時間は 107,510ms から 138,432ms となり少しかかっています。
もっとCPU使用率を抑えるには単純に休止時間を増やせばいいんですが、そうすると処理時間が長くなるため適度なバランスにしています。

画面上ではわかりませんが、for文を実行してるときはCPUファンが「ウイーン!」と激しく回っていたのに対し、chillout.repeat のときはファンが静かめでした(環境によってそこまで変わらないかもですが)。

chillout.js は、処理速度を少し遅くするかわりに低いCPU使用率で安定してJavaScriptを実行できる特徴があります。

特にブラウザやゲームなど、人が画面を見て操作するJavaScriptのパフォーマンスにおいて最も重要なことの一つは、 数値的な速度だけでなく、安定したレスポンスによってユーザーにストレスなく動かすことと考えています。

(ベンチマークスペック: Windows8.1 タブレット / Intel(R) Atom(TM) CPU Z3740 1.33GHz)

API



for文やwhile文、Array.forEachに対応するメソッド4つがあります。そのうち2つ紹介。

chillout.repeat

for文のような動きをします。

// 5回繰り返す
chillout.repeat(5, function(i) {
  console.log(i);
}).then(function() {
  console.log('done');
});
// 0
// 1
// 2
// 3
// 4
// 'done'

chillout.forEach

Array.forEachと同じように動く。

// 配列を回す
var values = ['a', 'b', 'c'];
chillout.forEach(values, function(value) {
  console.log(value);
}).then(function() {
  console.log('done');
});

// 'a'
// 'b'
// 'c'
// 'done'

非同期処理で Promise が返されるため then で繋ぎます。
(他のAPIの詳細は chillout.jsのGitHub を参考ください。)

比較表


既存のJavaScriptループを chillout.js のAPIに置き換えると、大抵の場合 CPU使用率を抑えて実行できます。

変換例:

JavaScript chillout.jsの場合
[1, 2, 3].forEach(function(v, i) {}) chillout.forEach([1, 2, 3], function(v, i) {})
for (i = 0; i < 5; i++) {} chillout.repeat(5, function(i) {})
for (i = 10; i < 20; i += 2) {} chillout.repeat({ start: 10, step: 2, done: 20 }, function(i) {})
while (true) {} chillout.till(function() {})
while (cond()) {} chillout.till(function() {
  if (!cond()) return chillout.StopIteration;
})
for (value of [1, 2, 3]) {} chillout.forOf([1, 2, 3], function(value) {})


GitHub / chillout.js



GitHub / chillout.js

※このブログの内容は常に更新してるわけじゃないので古くなってる可能性があります。特にAPIの最新の情報は GitHub / chillout.jsを参考ください。


2015年10月11日

ECMAScriptのParser/Tokenizer(字句解析)をJavaScriptで書きました

ECMAScript のコードを字句解析するコンパクトなパーサーライブラリ、chiffon.js を書きました。
chiffon.min.js は、3KB ほどのサイズです(v1.2.0 現在)。

特徴

  • ライブラリのファイルサイズが軽い
  • ECMAScript6 の仕様 を元にしてる
  • 解析結果が esprima のtokenize結果と同じになる
  • esprima ほど厳密ではない(構文エラーあっても解析を続けるなど)
  • 速度は esprima と同じくらいかちょっと速い
  • 正規表現で解析してる

Chiffon はとにかくファイルサイズが軽いのが特徴です。
正規表現で一発解析してて、その正規表現が少し長くなっちゃってるため、これ以上なかなかファイルサイズが削れないです。
構文エラーなどあってもエラーを吐かずに解析を続けます。
esprima ほど厳密ではないものの、ほとんどは esprima と同じ結果になります。
Parser と言ってますが、いまのところ Tokenize(字句解析) してるだけなので、厳密には Parser とは言わないのかもしれません。

使い方

ブラウザの場合:

<script src="chiffon.js"></script>
または、
<script src="chiffon.min.js"></script>
Chiffon というオブジェクトがグローバルに定義されます。

Node.js の場合:

npm install chiffon
var Chiffon = require('chiffon');

bower:

bower install chiffon

tokenize

ECMAScriptのコード文字列をトークン化します。
var tokens = Chiffon.tokenize('var a = 1');
console.log(tokens);
/*
[ { type: 'Keyword',    value: 'var' },
  { type: 'Identifier', value: 'a' },
  { type: 'Punctuator', value: '=' },
  { type: 'Numeric',    value: '1' } ]
*/

返される結果のトークン名リスト:

  • Comment
  • LineTerminator
  • Template
  • String
  • Punctuator
  • RegularExpression
  • Numeric
  • UnicodeEscapeSequence
  • Identifier
  • Null
  • Boolean
  • Keyword

※多少変化する可能性があります。最新の情報はこちらを参考ください
※JavaScript AST は現在サポートされてません。

Options

  • comment : trueにするとCommentトークンを残します (default=false)
  • lineTerminator : trueにするとLineTerminatorトークンを残します (default=false)

untokenize

tokenize() で返されたトークンを文字列に戻します。
var tokens = Chiffon.tokenize('var a = 1');
var code = Chiffon.untokenize(tokens);
console.log(code); // 'var a=1'

minify

JavaScriptのコードを minify します。
var min = Chiffon.minify('var a = 1 + 1; // comment');
console.log(min); // var a=1+1;

minify は以下のような感じで単純に実装されてます。
function minify(code) {
  return untokenize(tokenize(code));
}

Demo

レポジトリ




2014年11月29日

encoding.js 1.0.12 リリースしました

JavaScript 文字コード変換ライブラリ encoding.js 1.0.12 リリース しました

アップデート内容

  • UTF-8 <=> JIS 変換テーブルのバグ修正 (文字化けが直りました)
  • JIS X 0208 を対象にした変換テストが通るようになりました
  • ファイルサイズが約60% 減りました
  • 実行速度が向上しました
  • convert で引数にオブジェクトが渡せるようになりました

UTF-8 <=> JIS 変換テーブルのバグ

UTF-8 から JIS, JIS から UTF-8 への変換テーブルが変なことになってました。
変換テーブルは MS 基本コードポイント (Unicode) を参照していましたが、 UTF-8 に変換する際に無効なコードポイントになってしまい文字化けが発生していました。
MS 基本コードポイントと IBM 基本コードポイントの差は以下のようになっています。

コードポイント:
文字名 IBM CP (Unicode) MS CP (Unicode) IBM CP (UTF-8) MS CP (UTF-8)
― (全角ダッシュ) U+2014 U+2015 E28094 E28095
~ (波形ダッシュ) U+301C U+FF5E E3809C EFBD9E
∥ (二重縦線) U+2016 U+2225 E28096 E288A5
- (負符号) U+2212 U+FF0D E28892 EFBC8D
U+00A2 U+FFE0 C2A2 EFBFA0
U+00A3 U+FFE1 C2A3 EFBFA1
U+00AC U+FFE2 C2AC EFBFA2

encoding.js の場合、MS コードポイントのまま変換しようとしていまい、対応する文字がなく '?' などになってしまっていました。
これは元々直したつもりでいたのですが、 encoding.js 1.0.10 Line 2973 あたりを見てみると以下のようになっていて
0xEFBC8F:0x213F,0xEFBCBC:0x2140,0xE3809C:0x2141,0xEFBD9E:0x2141,0xE288A5:0x2142,
そもそも 0x2141 が消しきれてなくて重複していたり、対応する JIS テーブルに文字がなくて変換すると 1文字分消えてしまったりと、 バグが多かったのですが
今回、IBM 基本コードポイントの差分によって変換してあげることで、ぶじ文字化けが解消されました。

JIS X 0208 を対象にした変換テストが通るようになりました

この変換テーブルの修正により、JIS X 0208-1997 (JIS規格 第一・第二水準漢字) を対象にした変換テスト (UTF-16, UTF-8, Shift_JIS, EUC-JP, ISO-2022-JP(JIS)) が通るようになりました。 また、Unicode サロゲートペア の処理も修正しています。

ファイルサイズが約60% 減りました

encoding.js では、変換テーブルだけでかなりのサイズを占めています。
前バージョンまでは、JIS→UTF-8 テーブルと UTF-8→JIS テーブルの 2つを保持していたのですが、
今回、UTF-8→JIS のみを静的に持ち、JIS→UTF-8 変換が必要になった際、動的にもう一つのテーブルを生成するようにしました。
生成されるのは呼ばれた最初の 1回のみです。
その際の速度が気になり、ベンチとってみましたが 約 2~5ms 程度で問題ないと思われます。
ファイルサイズは非minifyで 261KB (min: 219KB) だったのが 152KB (min: 116KB) になり、約60% 減らすことができました。

実行速度が向上しました

前までは、変換テーブルに文字があるかないかを、if (TABLE[c] === void 0) { ... } みたいな感じで判断していましたが、
これは非常に遅く、また in を使うより hasOwnProperty が速く、実行環境にもよりますが
var hasOwn = Object.prototype.hasOwnProperty.call.bind(Object.prototype.hasOwnProperty);
if (hasOwn(obj, key)) { ... }
のように bind して使うよりも hasOwnProperty.call(obj, key) のほうが若干速かったため、後者を採用しています。

変換速度は、1MB の日本語テキスト (UTF-8) に対して SJIS に文字コード変換した場合、平均 約 50ms. (EUC-JP, JIS 等も同程度)、
UNICODE (UTF-16) から UTF-8 とその逆は約 5ms ほどで、前よりだいぶ速くなりました。

convert で引数にオブジェクトが渡せるようになりました

convert の第二引数に Object を渡せるようになりました。
// 以前の指定の仕方 (SJISに変換)
var sjisArray = Encoding.convert(unicodeArray, 'SJIS', 'UNICODE');

// Object で指定
var sjisArray = Encoding.convert(unicodeArray, {
  to: 'SJIS',
  from: 'UNICODE'
});
前のように 第二引数を to, 第三引数を from として文字列で渡しても動きます。


文字化けという大きなバグを修正しているので、アップデートをおすすめします。

Download


Node.js では、encoding-japanese というモジュール名になっています
npm install encoding-japanese
var encoding = require('encoding-japanese');

上のようにして利用できます。

encoding.convert() は Buffer に対しても使えます(配列で返ります)

Demo

Repository




2013年9月25日

jQuery.DeferredをMochiKit.Async.Deferredな感じに操作できるプラグイン作った

日頃、もちもちな Deferred に慣れてるせいか、jQuery.Deferred があまりに使い難いので作ってみました
JSDeferred でもよかったのですが、エラー処理が楽なので伝統を継承する感じで。
dojo や Closure Library などの Deferred は MochiKit.Async.Deferred からの派生なので、汎用性はあると思います。

機能概要

  • jQuery.Deferred 本体に機能を追加してるだけなので、本来の機能 .done() とかはそのまま使えて他のプラグインも普通に動く(と思う)
  • minify版 5KB とけっこう軽量
  • jQueryオブジェクトに async という関数オブジェクトが追加される
  • Deferred は、今までどおり var d = $.Deferred(); で生成
  • たぶん古い環境でも動く

Download

使い方

スクリプトを読み込みます
<script src="/path/to/jquery.async.js"></script>
or
<script src="/path/to/jquery.async.min.js"></script>

基本の Deferred チェイン:

var d = $.Deferred();
d.addCallback(function() {
    return 1;
}).addCallback(function(res) {
    console.log(res); // 1
});
d.callback();

succeed() を使った例:

$.async.succeed(1).addCallback(function(res) {
    return res + 1;
}).addCallback(function(res) {
    console.log(res); // 2
});

値の受け渡しと、エラー処理:

$.async(function() {
    return 1;
}).addCallback(function(res) {
    console.log(res); // 1
    throw new Error('error');
}).addCallback(function(res) {
    console.log('このメッセージは表示されない');
    return 'noop';
}).addErrback(function(err) {
    console.log(err); // error
    return 'hello';
}).addBoth(function(res) {
    console.log(res); // hello
});
$.async() は、引数の関数を非同期で実行して新しい Deferred チェインを作ります。JSDeferred でいう next() みたいな感じ。

基本的な使い方は GitHub に書いてありますので参考ください

addCallback() とか addErrback() などの 他の機能については MochiKit.Async と同じなのでリファレンスを参考ください

バグとか問題等ありましたら issues または、Twitter @polygon_planet までご報告ください

レポジトリ


2012年9月4日

重い処理を軽くできるJavaScriptライブラリ作ってみた

追記: この記事のものは古くメンテナンスされていません。
CPU負荷をかけずにループ処理するJavaScriptライブラリは「CPU負荷を抑えて重い処理を軽くするJavaScriptライブラリ「chillout.js」 | 圧縮電子どうのこうの」を参照ください。

lazyIter.js

lazyIter.js は CPU 負荷をかけずにループ処理が可能な JavaScript ライブラリです。

重いループ処理などを軽くすることができます。
ループ内で処理の負荷に応じて遅延させ、結果的に重い処理は軽くなり
元々軽い処理は、ほぼ従来の速度のまま実行できます。

処理の負荷だけでなく、時間のかかるループ処理は
「応答のないスクリプト」警告がでてしまいます。
そのような警告なしで実行できるのも特徴の一つです。

どうに違いがあるのかは CPU 負荷テストページ で実際に試してみてください

Pot.js が実装しているイテレータから派生しています。
パフォーマンスなどは Pot.js のイテレータとほとんど同じです

ループ処理は非同期で実行されます。

使い方は Pot.js のイテレータ と同じですが、Deferred を使っていないので
引数にコールバックを指定する点が違います。

動作環境

主な Web ブラウザで動作します
  • Mozilla Firefox *
  • Internet Explorer 6+
  • Safari *
  • Opera *
  • Google Chrome *
また、以下の環境でも動作します
  • Greasemonkey (userscript)
  • Mozilla Firefox Add-On (on XUL)
  • Node.js
  • Other non-browser environment

lazyIter.js は、CommonJS などに対応しています

使い方

lazyiter.js を読み込みます (Webページの場合)

<script src="lazyiter.js"></script>

読み込むと、lazyIter というオブジェクトが定義されます (定義されるのは lazyIter だけです)。

lazyIter.forEach :

オブジェクトや配列に対してループ実行します

// forEach:
//
// lazyIter.forEach(object, func, callback [, speed [, context]])

// Array.
lazyIter.forEach(['a', 'b', 'c'], function(val, i) {
  console.log(i + ':' + val);
}, function() {
  console.log('End loop');
});
// result:
//   '0:a'
//   '1:b'
//   '2:c'
//   'End loop'

// Object.
lazyIter.forEach({a: 1, b: 2, c: 3}, function(val, key) {
  console.log(key + ':' + val);
}, function() {
  console.log('End loop');
});
// result:
//   'a:1'
//   'b:2'
//   'c:3'
//   'End loop'

lazyIter.repeat :

指定の数だけループ実行します

// repeat:
//
// lazyIter.repeat(max, func, callback [, speed [, context]])

// Specify as number.
lazyIter.repeat(10, function(i) {
  console.log(i);
}, function() {
  console.log('End loop');
});
// result:
//   0
//   1
//   2
//   3
//   4
//   5
//   6
//   7
//   8
//   9
//   'End loop'

// for文のように 初め(begin)、ステップ(step)、終わり(end) を指定
lazyIter.repeat({begin: 0, step: 5, end: 30}, function(i) {
  console.log(i);
}, function() {
  console.log('End loop');
});
// result:
//   0
//   5
//   10
//   15
//   20
//   25
//   'End loop'

lazyIter.forEver :

lazyIter.StopIteration が throw されるまでループし続けます

// forEver:
//
// lazyIter.forEver(func, callback [, speed [, context]])

var end = 10;
lazyIter.forEver(function(i) {
  if (i === end) {
    throw lazyIter.StopIteration;
  }
  console.log(i);
}, function() {
  console.log('End loop');
});
// result:
//   0
//   1
//   2
//   3
//   4
//   5
//   6
//   7
//   8
//   9
//   'End loop'

throw lazyIter.StopIteration; により各イテレートを止めることができます。
引数 'context' は、コールバック関数の 'this' として使われます (省略可)。
引数 'speed' を指定することで速度の調節ができます (省略可)。
  • 'ninja' : もっと速い
  • 'rapid' : 速い
  • 'fast' : 速め
  • 'normal' : 通常 (default)
  • 'slow' : 遅め
  • 'doze' : 遅い
  • 'limp' : もっと遅い

速度の指定 (rapid, ninja など) は、Pot.js と同じです

Download

ダウンロード

動作テスト (サンプル)

レポジトリ



その他、なにか問題・バグ・感想・指摘などあれば、
コメントやメールまたは @polygon_planet まで送っていただけるとうれしいです。



2012年4月24日

JavaScriptで文字コード変換ライブラリ作ってみた

文字コード変換ライブラリを JavaScript で作りました。
文字列ではなく配列 or TypedArray で処理します。

追記

↓動作サンプルを作りました
文字コード変換 動作サンプル

Unicode の変換が可能になりました。
文字コード配列から URLエンコード/デコード が可能になりました。
あと説明とサンプルも少し載せました。。(説明不足でごめんなさい)

こないだの 「JavaScriptだけでzipファイルの解凍 - Unzipper.js」が
SJIS ファイルとかだと表示で文字化けするので、ついつい。。

動作確認は、zip ファイル解凍のデモページでわかると思います。
zip の中に SJIS や EUC-JP のファイル (ファイル名) がある場合でも
UTF-8 表示で化けなければ問題なしです。


↑のデモページを開いて、デスクトップなどから zip ファイルをドロップすると
解凍して結果のテキストを表示します。

※ JavaScript だけで動いていて、どっかのサーバなどに送信したり保存したりしてません


今回はぜんぶ同期で書いちゃったのですが、
バイナリファイルとかが対象だとちょっと重たくなりがち。。
気が向いたら非同期も可能にしてみたいです。

あと、Unzipper.js と同じレポジトリにしちゃったけど
これもできれば別にしたいところ。
追記→ 作りました

使い方:
// <script src="encoding.js"></script>
//
// Encoding というオブジェクトがグローバルに定義されます
// 配列に対して変換または判別します

// 文字コード変換
var utf8Array = new Uint8Array(...) or [...] or Array(...);
var sjisArray = Encoding.convert(utf8Array, 'SJIS', 'UTF8');

// 文字コード自動判別で変換
var sjisArray = Encoding.convert(utf8Array, 'SJIS');
// or  
var sjisArray = Encoding.convert(utf8Array, 'SJIS', 'AUTO');


// 文字コード判別 (戻り値は下の「共通の文字コード値」のいずれか)
var encoding = Encoding.detect(utf8Array);
if (encoding === 'UTF8') {
    alert('UTF8です');
}

// 特定の文字コードかどうか判別
var isSJIS = Encoding.detect(sjisArray, 'SJIS');
if (isSJIS) {
    alert('SJISです');
}

// convert, detect 共通の文字コード値:
//  - 'UTF32'   (detect only)
//  - 'UTF16'   (detect only)
//  - 'BINARY'  (detect only)
//  - 'ASCII'   (detect only)
//  - 'JIS'
//  - 'UTF8'
//  - 'EUCJP'
//  - 'SJIS'
//  - 'UNICODE' (JavaScript Unicode String/Array)
//
// ※ (detect only) は Encoding.detect() でのみ有効 (変換はできない)
// ※ 'UNICODE' は JavaScript の Unicode コード値 (0xFF 以上の数値になりえる)
//

サンプル:
// EUCJPの文字コード配列 (中身は 'こんにちは、ほげ☆ぴよ')
var eucjpArray = [
    164, 179, 164, 243, 164, 203, 164, 193, 164, 207, 161,
    162, 164, 219, 164, 178, 161, 249, 164, 212, 164, 232
];
// UTF-8に変換
var utf8Array = Encoding.convert(eucjpArray, 'UTF8', 'EUCJP');
console.log( utf8Array );
// output: [
//   227, 129, 147, 227, 130, 147, 227, 129, 171,
//   227, 129, 161, 227, 129, 175, 227, 128, 129,
//   227, 129, 187, 227, 129, 146, 226, 152, 134,
//   227, 129, 180, 227, 130, 136
// ]
//   => 'こんにちは、ほげ☆ぴよ'

// ---------------------------------------------
// 文字コード自動判別で変換
//

// SJISの文字コード配列 (中身は 'こんにちは、ほげ☆ぴよ')
var sjisArray = [
    130, 177, 130, 241, 130, 201, 130, 191, 130, 205, 129,
     65, 130, 217, 130, 176, 129, 153, 130, 210, 130, 230
];
// Unicodeに変換
var unicodeArray = Encoding.convert(sjisArray, 'UNICODE', 'AUTO');
// 文字列にして表示
// codeToStringは、文字コード配列を文字列に変換(連結)して返す関数
console.log( Encoding.codeToString(unicodeArray) );
//
// output: 'こんにちは、ほげ☆ぴよ'
//

// ---------------------------------------------
// 文字コードの配列をURLエンコード/デコード
var sjisArray = [
  130, 177, 130, 241, 130, 201, 130, 191, 130, 205, 129,
   65, 130, 217, 130, 176, 129, 153, 130, 210, 130, 230
];
var encoded = Encoding.urlEncode(sjisArray);
console.log(encoded);
// output:
//   '%82%B1%82%F1%82%C9%82%BF%82%CD%81A%82%D9%82%B0%81%99%82%D2%82%E6'

var decoded = Encoding.urlDecode(encoded);
console.log(decoded);
// output: [
//   130, 177, 130, 241, 130, 201, 130, 191, 130, 205, 129,
//    65, 130, 217, 130, 176, 129, 153, 130, 210, 130, 230
// ]

ダウンロード



レポジトリ



その他、なにか問題等あれば
コメントやメールまたは @polygon_planet まで送っていただけるとうれしいです。



2012年4月22日

JavaScriptライブラリ Pot.js 関連リンクまとめ

Pot.js 関連のリンクまとめ

JavaScript ライブラリ Pot.js に関する記事やサンプルの紹介です。
Pot.js は CPU に負荷をかけることなく JavaScript の実行を可能とするユーティリティライブラリです。
MOONGIFT さんの記事 で紹介されたのもあって、せっかくなのでまとめてみました。



Pot.js / PotLite.js

Pot.js は CPU に負荷をかけることなく JavaScript の実行を可能とするユーティリティライブラリです。
PotLite.js は Pot.js の非同期な部分だけを抽出したライトバージョンです。

ダウンロード

マニュアル

その他の情報についてはマニュアル/マニュアルからのリンク から参照ください。

レポジトリ




その他、なにか問題・バグ・感想・指摘などあれば、
コメントやメールまたは @polygon_planet まで送っていただけるとうれしいです。



2012年4月15日

JavaScriptライブラリPot.js 1.18 リリース。strict modeになりました




Pot.js 1.18 と PotLite.js 1.35 リリースしました。





変更点など

  • 'use strict'つけてライブラリ全体が strict mode になりました
  • DropFile が Firefox で不具合出てたので修正
  • minify したときの最適化

などで、とくに機能の変更はありません。
ねんがんの strict mode にできたので、'use strict' 内で取り込まれても問題なく動きます。
全体を strict mode にするためにソースコードの表現はけっこう変わってますが
動作はむしろ速くなって、サイズがちょっとだけ減りました。


リファレンスのほうは、定義してある関数をとりあえず全部メニューに載せるようにしました。
でもメニューだけで中身の html が無いので、はやいとこ作りたいなと思ってます。




Pot.js / PotLite.js

Pot.js は CPU に負荷をかけることなく JavaScript の実行を可能とするユーティリティライブラリです。
PotLite.js は Pot.js の非同期な部分だけを抽出したライトバージョンです。

ダウンロード

マニュアル

その他の情報についてはマニュアル/マニュアルからのリンク から参照ください。

レポジトリ




その他、なにか問題・バグ・感想・指摘などあれば、
コメントやメールまたは @polygon_planet まで送っていただけるとうれしいです。



2012年4月5日

並列処理のWorkerぽいWorkeroid実装。JavaScriptライブラリPot.js 1.17 リリース




Pot.js 1.17 と PotLite.js 1.34 リリースしました。





Web Workers

バックグラウンド処理、並列処理ができる HTML5 API の Web Workers は、
主要なブラウザですでに実装されています。

var worker = new Worker('child.js');
worker.onmessage = function(event) {
    alert(event.data); // 'hogefuga' がアラートされる
};
worker.postMessage('hoge');
child.js
onmessage = function(event) {
    postMessage(event.data + 'fuga');
};
上のような感じで使うのですが、基本的に 'child.js' のようにファイル名を渡さないといけないんです。
なので JavaScript が 2 ファイルになります。

並列で 4 スレッドとか 6 スレッドとかしてたら どんどん増えてファイルの管理がめんどいです。

あと、ブラウザによって動作 (子Worker のスコープ) が若干異なっていて
せっかく並列処理がネイティブにできるのに、なかなか扱いにくいものになってしまってます。

そこで、ネイティブ Worker が利用できたら ネイティブで
なければ Worker エミュレータとして、
クロスブラウザに動作する Workeroid (Pot.Workeroid) を実装しました。

Pot.Workeroid

  • 基本的にネイティブ Worker と同じに並列処理ができる
  • ネイティブ Worker がなければエミュレートする
  • クロスブラウザ
  • 子 Worker スレッドで importScripts しなくても Pot.js が使える
  • エミュレート時でも terminate で中断/終了できる
  • ファイル名だけじゃなく関数オブジェクトを子 Workerとして渡せる

ネイティブの Worker は
子Worker プロセス内では 親 window のライブラリなどが使えません。
importScripts('hoge.js') で読み込まないと使えないのですが、
Pot.Workeroid はデフォルトで Pot.js が利用できます。
なので、Pot.Deferred.forEach などを利用して CPU 負荷を抑えた処理が可能です。

最大の特徴として、
コンストラクタに ファイル名のほか、関数を渡すことが可能なことです。

以下は function で Worker スレッドを生成する例です。

var worker = new Pot.Workeroid(function(data) {
    //                                          
    // このスコープの中が 子Worker スレッド     
    //                                          
    var add = 1;                                
    postMessage(data + add);                    
});

// メッセージを受信した時
worker.onmessage = function(data) {
    alert(data);
};

// エラー時の設定 (任意)
worker.onerror = function(err) {
    alert(err);
};

// 1 を 子 Worker に送信 => 2 が alert される
worker.postMessage(1);

をつけたスコープ内が 子 Worker として別スレッドになります。
子 Worker 空間内は、別ファイルに書いてる気持ちでコーディングするとわかりやすいかもです。
たくさんのスレッド作る場合でも、少しの短いコードでも 別ファイルにしなくて済みます。

これで楽に並列処理ができるとうれしい!


Pot.Workeroid の例を含む詳細は Pot.Workeroid リファレンス から参照できます。

あと、簡単な動作テストを jsFiddle に置いてあります。

更新したところ


Pot.js / PotLite.js

Pot.js は CPU に負荷をかけることなく JavaScript の実行を可能とするユーティリティライブラリです。

PotLite.js は Pot.js の非同期な部分だけを抽出したライトバージョンです。

ダウンロード

マニュアル

その他の情報についてはマニュアル/マニュアルからのリンク から参照ください。

レポジトリ




その他、なにか問題・バグ・感想・指摘などあれば、
コメントやメールまたは @polygon_planet まで送っていただけるとうれしいです。



2012年3月12日

jsFiddleだらけ-JavaScriptライブラリPot.js+PotLite.jsリリースノート



Pot.js 1.15 1.16PotLite.js 1.32 1.33 リリースしました。

2012-03-13 追記:
Pot.js 1.15 と PotLite.js 1.33 はバギーなためアップデートしました。詳細
2012-03-13 時点の最新は Pot.js 1.16PotLite.js 1.34 です。最新にアップデートお願いします。。


このバージョンでは、
  • 文字列処理の高速化
  • 重くなりそうな処理を非同期化
などを適応しました。

文字列処理の高速化

先日、ふと思って String.fromCharCode を呼ばずに
U+0000 - U+FFFF の配列をあらかじめ作成して インデックスに対応させたらどうなのかなって
ベンチマークとってみました。


結果として、String.fromCharCode(c) のような
apply で配列を使わない場合、かなり高速化できました。
メモリ消費も文字列などは気にするほどじゃなかった。
String.fromCharCode.apply(null, [...]); のような場合は逆に遅くなるので
従来通り String.fromCharCode を使用しています。

(この件はあほなミスをしてて@gochoさんにつっこまれて助かりました)

重くなりそうな処理を非同期化

重くなりそうな処理とは、巨大な文字列が渡される可能性のある関数や、
ループ回数が未知の処理などです。

途中で「応答のないスクリプト」警告なんぞ でてしまったらめんどうです。
そういった対処や負荷軽減も含めて 関数オブジェクトに deferred というメソッドを持たせました。

例えば Pot.md5(string); が同期実行に対して
Pot.md5.deferred(string); は、非同期で実行します。


圧縮・解凍

文字列を LZ77 アルゴリズムをベースに圧縮・解凍する
Pot.Archive.AlphamericString も同様に非同期化しています。

その他のサンプル

他にもいくつかテスト用にサンプルがあったので紹介します。



Pot.js / PotLite.js

Pot.js は CPU に負荷をかけることなく JavaScript の実行を可能とするユーティリティライブラリです。

PotLite.js は Pot.js の非同期な部分だけを抽出したライトバージョンです。

ダウンロード

マニュアル

その他の情報についてはマニュアル/マニュアルからのリンク から参照ください。

レポジトリ




その他、なにか問題・バグ・感想・指摘などあれば、
コメントやメールまたは @polygon_planet まで送っていただけるとうれしいです。


2012年2月24日

同期処理を非同期に変換-JavaScriptライブラリPot.js+PotLite.jsリリースノート






Pot.js 1.14PotLite.js 1.31 リリースしました。



その前に Pot.js + PotLite.js リファレンス が完成しました (ということにしたい) ので、
お時間あるときにでも覗いてみてください。

結局 /test-for-new-design/ に置いてあるのはファイル消すのも嫌だし
全部リダイレクトさせるようにしました。



Pot.js 1.14 と PotLite.js 1.31 では、Pot.deferreed() ていう関数つくりました。

deferreed は、関数の中の for, for-in, for-of, do, while 等の同期ループを
Pot.Deferred.forEach() などの非同期イテレータに置き換えて関数を再定義します。

前の記事「JavaScript whileループとPot.Deferred.forEverイテレータでCPU使用率を比較」 でも書いてますが、
Pot.js ライブラリのイテレータは CPU の負荷なく実行できます。

ループを置き換えることで、巨大な文字列を扱ったり、ループ回数が未知な処理をする際に
重さについて考えなくて済むようにと、作ってみまみた。

もともとは deferrize() という関数から派生しています。

関数内のコードをトークン単位に分解して、
単純にトークンの数が多いループブロックをメインループとして解析して、
その文に見合った Pot.Deferred イテレータを割り当ててるんですが
ぜんぜん思ったよりたいへんで まだ複雑なものは対応できてないです。
とくに三項演算子 (?:) と function のコンボとか、E4X とかまでは今後の課題。。 にしたいけど、
そこまでしてたらライブラリの方向性違うんじゃないのって思ってしかたないです。

deferreed の 1 コールで zip.deflate 圧縮とかファイル規模への md5 とか非同期化できたら楽そう。

例えば以下のような関数が

// 文字列を charCode からなる配列にして返す関数
var toCharCode = function(string) {
    var result = [];
    for (var i = 0; i < string.length; i++) {
        result.push(string.charCodeAt(i));
    }
    return result;
};

// deferreed により for 文を非同期イテレータにした関数を生成
var toCharCodeDefer = Pot.deferreed(toCharCode);

// 例えば toCharCodeDefer は内部で以下のような感じに変換される
//
//  function(string) {
//      var result = [];
//      return Pot.Deferred.repeat(string.length, function(i) {
//          result.push(string.charCodeAt(i));
//      }).then(function() {
//          return result;
//      });
//  };
//

// Pot.Deferred インスタンスが返るため then() などで繋げられる
toCharCodeDefer('abc').then(function(res) {
    Pot.debug(res); // [97, 98, 99]
});

// この変化により、巨大な文字列でも負荷を分散させ実行できる
var largeString = new Array(100000).join('abcdef');

// 'slow' などの指定が可能
toCharCodeDefer.slow(largeString).then(function(res) {
    Pot.debug(res.length); // 599994
});

あと、プラグインみたいなことしたくて Pot.Plugin ての作ったんですが
なにか違うような気がしてならないです。

プラグイン = ただのオブジェクト
になってしまった。

Pot.addPlugin() という関数で任意の関数とかオブジェクトを登録。
Pot.removePlugin() で削除。
Pot.hasPlugin() で確認、Pot.listPlugin() で列挙。

addPlugin した関数は Pot.xxx でアクセスできる。
・・・それって Pot.myFunc = function() {}; でいいじゃん! てなったので、
以下のようにしてみました。
var string = '\t abc\n \t ';

// オリジナルの Pot.trim()
debug(Pot.trim(string)); // 'abc'

// プラグイン関数の登録により Pot.trim() を上書き
Pot.addPlugin('trim', function(s) {
    return s.replace(/^ +| +$/g, ''); // スペース (U+0020) だけ削除するようにする
});

// 登録した Pot.trim()
debug(Pot.trim(string)); // '\t abc\n \t'

// プラグインの trim を削除
Pot.removePlugin('trim');

// 元に戻る
debug(Pot.trim(string)); // 'abc'
元に戻せる (むしろ消えない) ので、
一時的に挙動を変えたい時とか便利かもしれないです。
あと、Pot.globalize() したときに プラグイン関数もグローバル化します。
Pot.globalize() しなくても with (Pot) {...} でもいい気がしてきました。

プラグインの詳細は
Pot.Plugin リファレンス あたりから参照ください。

マニュアルは英語版も含めてちょっとずつ進めています。


以下はレポジトリとかのリンクなど。

Pot.js / PotLite.js

Pot.js は CPU に負荷をかけることなく JavaScript の実行を可能とするユーティリティライブラリです。
PotLite.js は Pot.js の非同期な部分だけを抽出したライトバージョンです。

ダウンロード

マニュアル

その他の情報についてはマニュアル/マニュアルからのリンク から参照ください。



レポジトリ



その他、なにか問題・バグ・感想・指摘などあれば、
コメントやメールまたは @polygon_planet まで送っていただけるとうれしいです。