Node.jsと.Netのパフォーマンス


183

Node.jsが高速で大量の負荷に対応できることについては、たくさん読んだことがあります。これと他のフレームワーク、特に.Netの実際の証拠は誰にもありますか?私が読んだほとんどの記事は逸話的であるか、.Netとの比較がありません。

ありがとう


1
私たちが話しているシナリオの種類をもっと正確に教えていただけますか?
MarcusGranström12年

1
IISで実行されている同等のWebアプリケーションの.NetとNode.jsのパフォーマンス比較に興味があります。
David Merrilees

1
パフォーマンスの高いWebサイトを構築している人は想像できません。.Net以外の要件。遭遇する最も基本的な問題は、パフォーマンスが高いため、ライセンスに関してコスト効率がよくないということです。通常、サイトではスケールアウトが必要です。そして、私は.Net嫌いではありません。.Netが請求書を支払います。
シェーンコートリル

4
Node / express / mongoと新しい.net webapi / mongoを使用して小さなREST APIの内部テストを行う必要があり、クライアントの要求に基づいてパフォーマンスの違いがありましたが、結局のところ、差。独自のシナリオに基づいて独自のテストを開発する必要があります。両方の言語で異なるAPIを書くのに3日かかり、テストを適切にセットアップするのにさらに数日かかりました。リモートで深刻なことを行うことを計画している場合は、要件に基づいてテストを設定し、負荷に適した方を自分で決定することをお勧めします。
AlexGad

5
@ShaneCourtrille .Net(フレームワーク)とWindows(オペレーティングシステム)が混同しています。それらは非常に異なるものであり、.Netのライセンス要件はありません(これはLinuxでMonoとして非常にうまく動作します)。
Rainabba 2013年

回答:


366

ビーイングFASTのと取り扱いたくさんLOADは、二つの異なるものです。1秒あたり1つの要求を処理するのに本当に高速なサーバーは、1秒あたり500要求を送信すると(LOADの下で)完全に混雑する可能性があります。

また、静的(およびキャッシュ)ページと動的ページを考慮する必要があります。静的ページについて心配している場合、IISはおそらくノードを打ち負かすでしょう。これは、IISがカーネルモードキャッシュを使用しているためです。

ASP.NETとノードの比較を探していると思います。この戦いでは、すべてがコンパイル/解釈された後、おそらくパフォーマンスがかなり近くなります。たぶん、.NETの小さなFASTERまたは多分、ノード少しだFASTERが、それはおそらく、あなたが気にしないことを十分に近いです。私は.NETに賭けますが、確かではありません。

ノードが本当に説得力のある場所は、LOADを処理するためです。ここでテクノロジーが本当に異なります。ASP.NETは、そのスレッドプールからの各要求にスレッドを割り当てます。ASP.NETが使い果たされると、使用可能なすべてのスレッド要求がキューに入れられ始めます。@shankarの例のような「Hello World」アプリを提供している場合、スレッドはブロックされず、多くのリクエストを処理できるため、これはそれほど重要ではありません。スレッドが不足します。ASP.NETモデルの問題は、スレッドをブロックするI / O要求(DBへの呼び出し、サービスへのhttp要求、ディスクからのファイルの読み取り)を開始するときに発生します。これらのブロッキング要求は、スレッドプールからの貴重なスレッドが何も実行していないことを意味します。ブロッキングが多いほどあなたのASP.NETアプリがロードできるようになるだろうLOAD

このブロッキングを防ぐには、応答を待つ間、スレッドを保持する必要がないI / O完了ポートを使用します。ASP.NETはこれをサポートしていますが、残念ながら.NETの一般的なフレームワーク/ライブラリの多くはサポートされていません。たとえば、ADO.NETはI / O完了ポートをサポートしますが、Entity Frameworkはそれらを使用しません。したがって、純粋に非同期で多くの負荷を処理するASP.NETアプリを構築できますが、同期のアプリを構築するほど簡単ではないため、ほとんどの人はそうではありません。お気に入りのパーツの一部を使用できない場合があります。フレームワーク(エンティティへのlinqなど)の場合。

問題は、ASP.NET(および.NET Framework)が非同期I / Oについて意見を表明しないように作成されたことです。.NETは、同期コードと非同期コードのどちらを作成してもかまいません。そのため、この決定は開発者に委ねられます。これの一部は、非同期操作でのスレッド化とプログラミングが「難しい」と考えられていたためであり、.NETはすべての人を幸せにしたいと考えていました(初心者や専門家)。.NETは非同期を実行するために3〜4の異なるパターンで終わったので、さらに困難になりました。.NET 4.5は、.NETフレームワークを元に戻して、非同期IOに関する説得力のあるモデルを作成しようとしていますが、関心のあるフレームワークが実際にそれをサポートするまでにはしばらくかかる場合があります。

一方、nodeの設計者は、すべてのI / Oを非同期にする必要があるという独断的な選択をしました。この決定により、ノードの設計者は、ノードの各インスタンスをシングルスレッド化してスレッドの切り替えを最小限に抑え、1つのスレッドがキューに入れられていたコードを実行するという決定を下すこともできました。これは新しいリクエストの場合もあれば、DBリクエストからのコールバックの場合もあれば、作成したhttp restリクエストからのコールバックの場合もあります。ノードは、スレッドコンテキストの切り替えを排除することにより、CPU効率を最大化しようとします。ノードはすべてのI / Oが非同期であるというこの根拠のある選択をしたため、すべてのフレームワーク/アドオンがこの選択をサポートすることも意味します。ノードで100%非同期のアプリを作成する方が簡単です(ノードが非同期のアプリを作成する必要があるため)。

繰り返しになりますが、何らかの方法で証明できる具体的な数値はありませんが、ノードは通常のWebアプリのLOAD競争に勝つと思います。高度に最適化された(100%非同期).NETアプリは、同等のnode.jsアプリにお金を稼ぐ可能性がありますが、すべての.NETとすべてのノードアプリの平均を取った場合、平均して、ノードはおそらくより多くを処理します負荷。

お役に立てば幸いです。


39
ASP.NETは長い間非同期リクエストハンドラーをサポートしており、MVC4を使用すると非常に簡単に使用できるようになりました。
fabspro

12
「これらのブロッキングリクエストは、スレッドプールからの貴重なスレッドが何も実行していないことを意味します。ブロッキングが多いほど、ASP.NETアプリが処理できるLOADが少なくなります。」 フロント(着信要求)でキューイングするか、バックエンド(実際の作業スレッド)でキューイングするかが重要なのはなぜですか?とにかく、クライアント要求は応答を待っています。この論争のなかで見落としているのは「スループット」だと思います。サーバーが同時に保持できる接続の数についてではなく、各要求にサーバーがどれだけ速く応答できるかについてです。
sjdirect 2013年

19
//コメントの編集を許可しないため、ここに私が言いたいことがありました。//@sjdirect-スループットは応答時間と同じではありません。応答時間を気にするのは正しいことですが、それはキュー時間+応答時間、または単に応答時間の間の選択です。リクエストの処理は両方のシナリオで同じくらい長くかかります(同期的に実行してもDBリクエストの実行が速くなることはありません)が、リクエストスレッドがブロックされている場合、キュー時間もリクエストに追加されます前のリクエストが完了するまでリクエストの処理を開始することさえできないからです。
Matt Dotson

6
これは本当に有益でした、ありがとう!ただし、Entity Framework 6(現在はRC1)は、.NET 4.5からの非同期パターンをサポートすることに注意してください。msdn.microsoft.com/en-us/data/jj819165
議会

4
これは非常に投機的です!データがあるといいですね。通常、それがパフォーマンスのトピックの進め方を決定する方法です。
kingPuppy

50

nodejsとIISの間で初歩的なパフォーマンステストを行いました。IISは、「こんにちは、世界!」を提供する場合、nodejsよりも約2.5倍高速です。以下のコード。

私のハードウェア:Dell Latitude E6510、Core i5(デュアルコア)、8 GB RAM、Windows 7 Enterprise 64ビットOS

ノードサーバー

runs at http://localhost:9090/
/// <reference path="node-vsdoc.js" />
var http = require("http");
http.createServer(function (request, response) {
response.writeHead(200, { "Content-Type": "text/html" });
response.write("<p>hello, world!</p>");
response.end();
}).listen(9090);

default.htm

hosted by iis at http://localhost/test/
<p>hello, world!</p>

タスク並列ライブラリを使用した自分のベンチマークプログラム:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using System.Diagnostics;

namespace HttpBench
{
class Program
{
    private int TotalCount = 100000;
    private int ConcurrentThreads = 1000;
    private int failedCount;
    private int totalBytes;
    private int totalTime;
    private int completedCount;
    private static object lockObj = new object();

    /// <summary>
    /// main entry point
    /// </summary>
    static void Main(string[] args)
    {
        Program p = new Program();
        p.Run(args);
    }

    /// <summary>
    /// actual execution
    /// </summary>
    private void Run(string[] args)
    {
        // check command line
        if (args.Length == 0)
        {
            this.PrintUsage();
            return;
        }
        if (args[0] == "/?" || args[0] == "/h")
        {
            this.PrintUsage();
            return;
        }

        // use parallel library, download data
        ParallelOptions options = new ParallelOptions();
        options.MaxDegreeOfParallelism = this.ConcurrentThreads;
        int start = Environment.TickCount;
        Parallel.For(0, this.TotalCount, options, i =>
            {
                this.DownloadUrl(i, args[0]);
            }
        );
        int end = Environment.TickCount;

        // print results
        this.Print("Total requests sent: {0}", true, this.TotalCount);
        this.Print("Concurrent threads: {0}", true, this.ConcurrentThreads);
        this.Print("Total completed requests: {0}", true, this.completedCount);
        this.Print("Failed requests: {0}", true, this.failedCount);
        this.Print("Sum total of thread times (seconds): {0}", true, this.totalTime / 1000);
        this.Print("Total time taken by this program (seconds): {0}", true, (end - start) / 1000);
        this.Print("Total bytes: {0}", true, this.totalBytes);
    }

    /// <summary>
    /// download data from the given url
    /// </summary>
    private void DownloadUrl(int index, string url)
    {
        using (WebClient client = new WebClient())
        {
            try
            {
                int start = Environment.TickCount;
                byte[] data = client.DownloadData(url);
                int end = Environment.TickCount;
                lock (lockObj)
                {
                    this.totalTime = this.totalTime + (end - start);
                    if (data != null)
                    {
                        this.totalBytes = this.totalBytes + data.Length;
                    }
                }
            }
            catch
            {
                lock (lockObj) { this.failedCount++; }
            }
            lock (lockObj)
            {
                this.completedCount++;
                if (this.completedCount % 10000 == 0)
                {
                    this.Print("Completed {0} requests.", true, this.completedCount);
                }
            }
        }
    }

    /// <summary>
    /// print usage of this program
    /// </summary>
    private void PrintUsage()
    {
        this.Print("usage: httpbench [options] <url>");
    }

    /// <summary>
    /// print exception message to console
    /// </summary>
    private void PrintError(string msg, Exception ex = null, params object[] args)
    {
        StringBuilder sb = new System.Text.StringBuilder();
        sb.Append("Error: ");
        sb.AppendFormat(msg, args);
        if (ex != null)
        {
            sb.Append("Exception: ");
            sb.Append(ex.Message);
        }
        this.Print(sb.ToString());
    }

    /// <summary>
    /// print to console
    /// </summary>
    private void Print(string msg, bool isLine = true, params object[] args)
    {
        if (isLine)
        {
            Console.WriteLine(msg, args);
        }
        else
        {
            Console.Write(msg, args);
        }
    }

}
}

と結果:

IIS: httpbench.exe http://localhost/test

Completed 10000 requests.
Completed 20000 requests.
Completed 30000 requests.
Completed 40000 requests.
Completed 50000 requests.
Completed 60000 requests.
Completed 70000 requests.
Completed 80000 requests.
Completed 90000 requests.
Completed 100000 requests.
Total requests sent: 100000
Concurrent threads: 1000
Total completed requests: 100000
Failed requests: 0
Sum total of thread times (seconds): 97
Total time taken by this program (seconds): 16
Total bytes: 2000000

nodejs: httpbench.exe http://localhost:9090/

Completed 10000 requests.
Completed 20000 requests.
Completed 30000 requests.
Completed 40000 requests.
Completed 50000 requests.
Completed 60000 requests.
Completed 70000 requests.
Completed 80000 requests.
Completed 90000 requests.
Completed 100000 requests.
Total requests sent: 100000
Concurrent threads: 1000
Total completed requests: 100000
Failed requests: 0
Sum total of thread times (seconds): 234
Total time taken by this program (seconds): 27
Total bytes: 2000000

結論:IISはnodejsよりも約2.5倍高速です(Windowsの場合)。これは非常に初歩的なテストであり、決して決定的なものではありません。しかし、これは良い出発点だと思います。Nodejsは他のWebサーバーや他のプラットフォームではおそらくより高速ですが、WindowsではIISが勝っています。ASP.NET MVCをnodejsに変換することを検討している開発者は、先に進む前に一時停止してよく考える必要があります。

更新(2012年5月17日)Tomcat(Windows上)は、静的なHTMLを提供する際にIISの約3倍の速さでIISの手を打ち負かすようです。

トムキャット

index.html at http://localhost:8080/test/
<p>hello, world!</p>

tomcatの結果

httpbench.exe http://localhost:8080/test/
Completed 10000 requests.
Completed 20000 requests.
Completed 30000 requests.
Completed 40000 requests.
Completed 50000 requests.
Completed 60000 requests.
Completed 70000 requests.
Completed 80000 requests.
Completed 90000 requests.
Completed 100000 requests.
Total requests sent: 100000
Concurrent threads: 1000
Total completed requests: 100000
Failed requests: 0
Sum total of thread times (seconds): 31
Total time taken by this program (seconds): 5
Total bytes: 2000000

更新された結論:ベンチマークプログラムを複数回実行しました。Tomcatは、WINDOWS上の静的HTMLを提供する最速のサーバーのようです。

更新(2012年5月18日)以前は、合計で100,000のリクエストがあり、10,000の同時リクエストがありました。私はそれを1,000,000の総requesと100,000の同時リクエストに増やしました。IISは最悪の勝利を収め、Nodejsは最悪の事態を招きました。以下の結果を表にまとめました。

WINDOWSで静的HTMLを提供するNodeJS vs IIS vs Tomcat


56
リンゴと猫を比較しています。Node.jsとASP.NET MVCを比較してください。ほとんどの場合、IISの方が静的ファイルの提供が高速ですが、それさえ真剣に疑っています。
alessioalex

12
@alessioalex:この比較が有効でない理由がわかりません。静的htmlの応答時間を比較しています。IISはdefault.htmから静的htmlを提供していますが、nodejsサーバーは同じ文字列を提供しており、IISが先行しています。ASP.NET MVCアプリケーションを比較するには、より多くの労力と時間が必要ですが、後で行う予定です。
シャンカー

28
OK、IISはノードよりもWindows上の静的ファイルの提供に優れていると言います。IISは静的ファイルなど(ApacheやNGINXなど)のみを提供しますが、Nodeはそれ以上の役割を果たします。ASP.NET MVCをノードと比較する必要があります(データベースのクエリ、外部サービスからのデータの取得など)。ASP.NET MVC上のNodeを使用すると、パフォーマンスが大幅に向上します。
alessioalex

27
これを行う場合は、少なくともノードの性質を理解してください。1つのノードプロセスで使用できるコアは1つだけです。したがって、比較しているのは、1つのコアで実行されているノードプロセスとIIS、および複数のコアを使用したTomcatプロセスです。正しく比較するには、クラスター化されたノードを実行する必要があります。使いやすいクラスターソリューションについては、nodejs.org / api / cluster.htmlをご覧ください。ただし、経験から言うと、ノードと非同期のc#の違いは、何をしているのかに応じて、どちらの方法でも10〜15%です。
AlexGad 2013

14
また、ノードとIISおよびTomcatで静的ファイルをテストしても意味がありません。まず、ノードは静的ファイルには適していませんが、実際にはそうではありません(適切なジョブに適切なツールを使用してください)。静的ファイルの速度が心配な場合は、とにかくCDNを使用する必要があります。
AlexGad 2013

26

NIOサーバー(Node.jsなど)は、BIOサーバーよりも高速になる傾向があります。(IISなど)。私の主張を裏付けるために、TechEmpowerはWebフレームワークベンチマークに特化した会社です。それらは非常にオープンで、すべてのフレームワークをテストする標準的な方法があります。

ラウンド9テストは現在最新です(2014年5月)。テストされたIISフレーバーは多数ありますが、aspnet-strippedは最も速いIISバリアントのようです。

1秒あたりの応答の結果を次に示します(高いほど良い)。

  • JSONシリアル化
    • nodejs: 228,887
    • aspnet-stripped: 105,272
  • 単一クエリ
    • nodejs-mysql: 88,597
    • aspnet-stripped-raw: 47,066
  • 複数のクエリ
    • nodejs-mysql: 8,878
    • aspnet-stripped-raw: 3,915
  • 平文
    • nodejs: 289,578
    • aspnet-stripped: 109,136

すべての場合において、Node.jsはIISの2倍以上高速になる傾向があります。


1
複数クエリテストを除き、ASPNETには2つのエントリ(aspnet-stripped-rawおよびaspnet-mysql-raw)があり、どちらも最上位のnjsエントリであるnodejs-mysqlに勝っています。
GalacticCowboy

4
まあ、複数のクエリのテストはサーバーの速度を正確にテストしているわけではありません。主にMySQLドライバーの速度をテストしています。NodeJSは主に、MongoDB、CouchDBなどのNO-SQLデータベースを使用します。MySQLドライバーが最適化されていない可能性があります。Jsonのシリアライゼーションとプレーンテキストのテストでは、純粋なサーバーの速度が得られる傾向があります。
てっきん

IISノードを使用するとどうなりますか?私のパフォーマンスが低下するか、同じになります。
Umashankar

3
ベンチマークページへのリンクをありがとう。ただし、答えは更新が必要になる可能性があります。.NETCore 2.1の登場により、状況はかなり変化した可能性があります。たとえば、2018年のJSONシリアル化ベンチマークでは、ASP.NET Coreが971,122リクエスト/秒、Node.jsが561,593リクエスト/秒であるため、現在のところ、ASP.NET Coreはその点でNode.jsのほぼ2倍の速度になっています。
stakx-2018年

13

私はマーカス・グランストロームに同意する必要があります。シナリオはここで非常に重要です。

正直に言うと、アーキテクチャに大きな影響を与えているように思えます。私のアドバイスは、懸念のある領域を分離し、検討しているスタック間で「ベイクオフ」を行うことです。

結局のところ、あなたが決定の責任を負うので、言い訳はないと思います。


1
私が上司を含め、人々を説得するために何かを探しています。MVC.netWebサイトの代わりとして検討する価値があります。これまでに見つけたのは、より多くの負荷をサポートでき、パフォーマンスが向上するという逸話的な記述です。実際にこれを証明した人はいますか?
David Merrilees、

17
しかし、MVC Webサイトの何が問題になっていますか?なぜ代替案を見つけようとしているのですか?これが最も重要なQです。もし問題が重い同時負荷で非常に遅い場合は、async.netを使用していることを確認してください。それでも本当に遅い場合は、コードを分解して、ボトルネックがどこにあるかを把握する必要があります。私の経験では、REAL WORLDシナリオでは、ノードと非同期ネットの間に大きな違いはありません。プラットフォームは変更できますが、コードのボトルネック/頭痛のセットを別のコードのボトルネック/頭痛のセットに変更するだけの可能性があります。
AlexGad 2013

1

私が目にする主な違いは、node .jsは動的プログラミング言語(型チェック)であるため、型は実行時に派生する必要があるということです。C#.NETのような強く型付けされた言語は、理論的にはノード.js(およびPHPなど)との戦いに勝つ可能性がはるかに高くなります。ちなみに、.NETは、ノード.jsよりもC / C ++とのネイティブな相互運用が優れている必要があります。


4
JSでの「弱い」タイピングが遅くなるというあなたの提案は間違っていて無関係であり、それはApplesとStonesを比較しています(Orangesでもあなたが提案しているものよりも似ています)。
Rainabba 2013年

7
@rainabbaある種の計算(xのフィボナッチなど)を比較すると、彼は完全に正しいです。
スタン

5
@steve実際には、Zが与えられたとしても、JSは言語であり、.Netはフレームワークであるため、まだそうは言えません。それらは完全に異なるものです。.Netランタイムは特定のプロセッサアーキテクチャ用にコンパイルされているため、1つのハードウェアで特定のコードチャンクのパフォーマンスを大幅に変更することはできません。V8が示しているように、JSは解釈および実行され、速度が非常にさまざまであり、ある日、JSで記述されたフィボナッチコードがCLRを介して実行されるコードほど高速に実行されないと考える理由はありません(おそらく、もっと早く)。リンゴと石; 私の言ったように。
Rainabba 2013

1
あなたの言う通りかもしれませんが、私の目には他の国はわかりません。中国では、EFやLinq to
Sqlと聞いた

1
JSにも同じことが言えます。JSがフィボナッチに追いついている間、あなたは本当に.NETがそれが待っている場所に留まると本当に思いますか?
quanben
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.