前回のブログ投稿 不可能とは言わないで、nodejs にスリープを実装してみよう で、nodejs アドオンの使用法を紹介しました。今日のテーマはやはりアドオンで、c/c の機能を引き続き探索し、nodejs の弱点を補います。
nodejs のパフォーマンスの問題については何度も言及しました。実際、言語自体に関する限り、nodejs のパフォーマンスはほとんどの静的言語ほどではありませんが、他の動的言語と比べて大きな差はなく、速度の利点は明らかです。 。しかし、nodejs は CPU を大量に使用するシナリオに対応できないとよく言われるのはなぜでしょうか?シングルスレッドの性質により、CPU を大量に使用するシナリオでは CPU を完全に活用できないためです。コンピューターサイエンスには有名なアムダールの法則があります:
総作業負荷 W が 2 つの部分に分解できると仮定します。Ws は逐次的にのみ計算でき、Wp は並列計算が可能です。そして、p個のCPUの並列計算の場合、高速化により性能を向上させることができる。アムダールの法則は、並列処理で何ができるか、何ができないかを説明します。これは理想的な状況ですが、実際の状況はさらに複雑になります。たとえば、同時実行によりリソースの競合が発生する可能性があり、さまざまなロックの追加が必要になり、並列処理が待機状態になることがよくあります。また、同時実行により、オペレーティング システムがスレッド スケジューリングを切り替えるために追加の時間オーバーヘッドが発生し、Ws が増加します。ただし、タスク内の Wp が Ws よりもはるかに大きく、複数の CPU コアが使用できる場合、並列処理によってもたらされるパフォーマンスの向上は大幅です。
さて、nodejs に戻ります。計算シナリオを想像してみましょう。4,000,000 以内の素数の数を計算します。このシナリオをプログラムする場合、主に除算演算が使用され、メモリやオブジェクトなどの演算は関与しません。理論的には、nodejs が比較的高速に実行され、c に比べて大幅に遅れることがないため、便利です。比較。
JavaScript で素数を見つける方法は このブログ で提供されているので、直接コピーしてください:
別の C 言語バージョンを作成します:
bool zhishu(int num){
If (num == 1) {
return false;
}
If (num == 2) {
true を返します;
}
for (int i = 2; i
If (num % i == 0) {
return false;
}
}
true を返します;
};
nodejs では、1 から 4000000 までのループを使用して素数を取得します。C 言語では、いくつかのスレッドを設定し、count を 4000000 として定義します。各スレッドは次の処理を行います: count が 0 より大きい場合は、それを取り出します。 count の値が素数かどうかを計算し、count を 1 減算します。この考え方によれば、JavaScript のバージョンは次のように簡単に記述できます:
for (j = 1; j
If(zhishu(j)){
カウント ;
}
}
最大の難点は、C 言語でのマルチスレッド プログラミングです。 c/c の初期の頃は、並列コンピューティングの必要性が考慮されていなかったため、標準ライブラリではマルチスレッドのサポートが提供されていませんでした。通常、オペレーティング システムが異なれば実装も異なります。この問題を回避するために、スレッドを処理するために pthread を使用します。
pthread の最新バージョンをダウンロードします。私は gyp に詳しくないので、リンクの依存関係 lib を修正するのに時間がかかりました。結局、私の方法は、pthread のソース コードをプロジェクト ディレクトリに直接置き、そのソース コードに pthread.c を追加することでした。 binding.gyp のリストを参照し、プロジェクトのコンパイル時に pthread を 1 回コンパイルします。変更された binding.gyp は次のようになります:
もちろん、私の方法は非常に面倒ですが、ライブラリへの参照を追加し、pthread にディレクトリを含めるだけで、依存関係の問題がない場合は、私の方法を使用する必要はありません。
次に、C/C マルチスレッドについて詳しく説明し、スレッド処理関数を定義しましょう。
void *thread_p(void *null){
int num, x=0;
する{
pthread_mutex_lock(&lock);
num=count--;
pthread_mutex_unlock(&lock);
If(数値>0){
If(zhishu(num))x ;
}その他{
休憩;
}
}while(true);
std::cout<
pthread_exit(NULL);
return null;
}
スレッド間で count 変数は互いに競合します。同時に 1 つのスレッドだけが count 変数を操作できるようにする必要があります。 pthread_mutex_t lock; を介してミューテックス ロックを追加します。 pthread_mutex_lock(&lock); が実行されると、スレッドはロック状態をチェックし、ロックされている場合は待機して再度チェックし、ロックが解放されている場合はロックして後続のコードを実行します。同様に、 pthread_mutex_unlock(&lock); はロック状態を解除します。
コンパイラーはコンパイル中にコンパイルの最適化を実行するため、ステートメントが明確に何も行わず、他のステートメントの実行に影響を与えない場合、そのステートメントはコンパイラーによって最適化されなくなります。上記のコードでは、素数の数をカウントするコードを追加しました。そうでない場合は、次のようになります。
アドオンを追加する書き方を導入しました。javascriptからスレッド数を示すパラメータを受け取り、cに指定した数のスレッドを作成して素数取得を完了します。完全なコード:
int count=4000000;
pthread_t tid[MAX_THREAD];
pthread_mutex_t ロック;
void *thread_p(void *null){
int num, x=0;
する{
pthread_mutex_lock(&lock);
num=count--;
pthread_mutex_unlock(&lock);
if(num>0){
if(zhishu(num))x ;
}その他{
休憩;
}
}while(true);
std::cout<<' '<
null を返す;
}
NAN_METHOD(志書){
NanScope();
pthread_mutex_init(&lock,NULL);
double arg0=args[0]->NumberValue();
int c=0;
for (int j = 0; j < arg0 && j
}
for (int j = 0; j < arg0 && j
}
NanReturnUnknown();
}
void Init(ハンドル<オブジェクト> エクスポート){
exports->Set(NanSymbol("zhishu"), FunctionTemplate::New(Zhishu)->GetFunction());
}
NODE_MODULE(こんにちは、初期化);
phread_create は、参加可能であることが認められている接続プロセスを確立できます。このとき、子回線プロセスは主回線によって制限されます。phread_join は、子回線プロセスが退出するまで、主回線の参加を待機します。したがって、すべての回線プロセスに対してスレッドジョインが実行され、すべての回線プロセスが退出した後、メイン 回線プロセスが続行される可能性があります。
完善一下nodejs 脚本:
zhishu_c(100);
console.timeEnd("c");
var count=0;
for (j = 1; j
if(zhishu(j)){
カウント ;
}
}
console.log(カウント);
console.timeEnd("js");
テスト結果を見てみましょう:
シングルスレッドでは、C/C の実行速度は、nodejs の 181% ですが、この結果は動的言語では依然として非常に優れていると思います。デュアル スレッドを使用すると速度の向上が最も顕著になります。これは、私のコンピューターにはデュアル コア 4 スレッドの CPU が搭載されており、現時点では 2 つのコアが処理に使用されている可能性があるためです。このときの速度は4スレッドで最大になりますが、これ以上スレッド数を増やしても速度は向上しません。上記のアムダールの法則では、p は上限の 4 に達しています。スレッドを追加すると、オペレーティング システムのプロセス スケジューリング時間とロック時間が増加しますが、CPU 時間の競合も増加しますが、全体的には W の増加がより顕著になり、パフォーマンスが低下します。この実験をアイドル状態のマシンで実行すると、データはより良くなるはずです。
この実験から、CPU を大量に使用する操作については、計算に多くのメモリ、文字列、配列、再帰などが含まれる場合、静的言語に任せると効率が大幅に向上するという結論を導き出すことができます。 (後述)、性能の向上はさらにすごいです。同時に、マルチスレッドを合理的に使用すると処理効率が効果的に向上しますが、スレッドは多いほど良いとは限りません。マシンの状態に応じて適切に構成する必要があります。
Nodejs 自体は確かに CPU を大量に使用するタスクを処理するのが得意ではありませんが、この記事の経験があれば、この障害を克服することは不可能ではないと思います。