2

私のアプリケーションでは、CPS スタイルに従っている JavaScript コードを生成しています。私はそのような「継続」を「使用していません」。非同期動作なし、一時停止と再開なし、コールバックなし。

コードが継続渡しスタイルのプログラミングに従っているだけです。

機能には多くの段階があり、各段階で処理が行われ、結果が継続に渡されます。

私が見つけたのは、CPS スタイルのコードのパフォーマンスが非常に悪いということです。ダイレクト スタイルで記述されたコードは、CPS スタイルのコードよりもほぼ 150 倍高速です。

以下のコードを確認してください。
以下のコードは両方とも同等です

var res = data.store.bookshelf.book.author;

ダイレクト スタイル コード:

var data = { store : { bookshelf : {book : {author:"Douglas Crockford"}}}};
var t1 = new Date().getTime();
for(var i = 0; i < 1000*1000*100; i+=1){      
  var temp0 = data;
  var temp1 = temp0.store;
  var temp2 = temp1.bookshelf;
  var temp3 = temp2.book;
  var temp4 = temp3.author;
  var res = temp4;
}
var t2 = new Date().getTime();
console.log(t2-t1);

上記のコードは、ほぼ 95 ミリ秒で実行されます。

CPS スタイルのコード:

var data = { store : { bookshelf : {book : {author:"Douglas Crockford"}}}};

// return the variable to the continuation
function cps_VARREF(x,f){
  return f(x);
}
// get the value of the property from the variable and pass it to the continuation
function cps_CHILD(x,child,f){
  return f(x[child]);
}
// simply return the input value, essentially closing the continuation chain
function ret_(x){
  return x;
}

var t1 = new Date().getTime();
for(var i = 0; i < 1000*1000*100; i+=1){
 var res = function(c_){
    return cps_VARREF(data,function(x1){
    return cps_CHILD(x1,"store",function(x2){
    return cps_CHILD(x2,"bookshelf",function(x3){
    return cps_CHILD(x3,"book",function(x4){
    return cps_CHILD(x4,"author",c_);});});});});}(ret_);
}
var t2 = new Date().getTime();
console.log(t2-t1);

上記の CPS スタイルのコードは 15000 ミリ秒で実行されます

CPS スタイルのコードを改善するためにできることはありますか? または、JavaScript は本質的に CPS スタイルのコードには適していませんか?

上記のテストは、node.js バージョン 0.6.12 で行われます。

誰かがこの問題に光を当てることができますか?

ありがとう、

4

2 に答える 2

1

劇的な減速には、少なくとも 2 つの潜在的な原因があります。1 つ目は、「ネイティブ」プロパティ ルックアップを動的ルックアップに置き換えていることです。

V8 は可能な限りオブジェクトを最適化するため、プロパティへのアクセスが高速になります。ハッシュ テーブルを使用して名前でプロパティを検索する代わりに、内部の「クラス」を追跡するため、既知のアドレスからプロパティを検索できます。そのdata.storeため、オブジェクトが期待される型であり、インデックス付きポインターの読み込みであることを確認するための簡単なポインター比較です。

ただし、cps_CHILD関数では、どのプロパティが事前にアクセスされるかわからないため(そして呼び出すたびに変更されるため)、その最適化を行うことはできません。動的ルックアップは、V8 をハッシュ テーブル ルックアップにフォールバックさせます。これは、最適化された静的ルックアップよりも低速です。

もう 1 つの問題は、関数呼び出しによるオーバーヘッドです。ネストされた各関数は、次の関数に渡されるたびに再作成する必要があります。毎回コンパイルする必要はありませんが、新しいコンテキストで作成する必要があります。

于 2012-08-24T23:35:21.073 に答える
0

ループに入力した無名関数など、実行時に解析されるものがあることを知っておく必要があります。新しい「i」ごとに、新しい「コンストラクター」が作成され、各匿名関数のプロトタイプが作成されます。それが遅い理由です。ここで新しいテストです。無名関数ごとに実際の関数を定義し、それらをループの外にネストするようにしてください。これにより、プログラムのパフォーマンスが向上するはずです。

これがコードです。CPS スタイルのコードと同じように深くなりますが、関数が定義されるのは 1 回だけです。

var data = { store : { bookshelf : {book : {author:"Douglas Crockford"}}}};

function getValueFrom(data){
    return data;
}

function getAuthorFrom(data){
    return getValueFrom(data);
}

function getBookFrom(data){
    return getAuthorFrom(data["book"]);
}

function getBookShelfFrom(data){
    return getBookFrom(data["bookshelf"]);
}

function getStoreFrom(data){
    return getBookShelfFrom(data["store"]);
}

function getAuthor(data){
    return getAuthor(data);
}

var t1 = new Date().getTime();
for(var i = 0; i < 1000*1000*100; i+=1){
 var res = getStoreFrom(data);
}
var t2 = new Date().getTime();
console.log(t2-t1);

4 ~ 5 倍高速に実行されます。JSエンジンは関数プロトタイプを探す必要があるため、それでも遅いので、実行のためにヒープに置くことができます. 最初のケース (非 CSP スタイル) では、ドット アクセスをプロパティ JS エンジンに使用すると、キー (JS オブジェクト プロパティ) によってハッシュのみをクエリしてその値を取得します。メモリ参照のみを処理する必要があるため、はるかに高速に動作します。

于 2012-08-24T23:10:03.417 に答える