setTimeout は node.js でどのように実装されるか 質問する

setTimeout は node.js でどのように実装されるか 質問する

誰か知っている人はいませんか?タイムアウトの設定node.jsで実装されています。これはV8の一部ではないとどこかで読んだと思います。すぐに実装を探しましたが、ソース(BIG)では見つけられませんでした。たとえば、これを見つけましたタイマーファイルにリンクされ、例えばタイマーラップ.ccしかし、これらのファイルは私の質問のすべてに完全に答えているわけではありません。

  • V8 にはsetTimeout実装がありますか? ソースからも、答えはノーだと思います。
  • どのようsetTimeoutに実装されていますか? JavaScript、ネイティブ、あるいはその両方の組み合わせですか? timers.js から、私はその両方に似たものを想定しています:

    var Timer = process.binding('timer_wrap').Timer;`
    
  • 複数のタイマーを追加する場合 (setTimeout)、node.js はどのタイマーを最初に実行するかをどのようにして知るのでしょうか? すべてのタイマーをコレクション (ソート済み) に追加しますか? ソートされている場合、実行する必要があるタイムアウトを見つけるには O(1) と挿入の場合は O(log n) が必要ですか? しかし、timers.js ではリンクリストが使用されているようですね?

  • しかし、タイマーをたくさん追加してもまったく問題ないのでしょうか?
  • このスクリプトを実行すると:

    var x = new Array(1000),
        len = x.length;
    
    /**
     * Returns a random integer between min and max
     * Using Math.round() will give you a non-uniform distribution!
     */
    function getRandomInt (min, max) {
        return Math.floor(Math.random() * (max - min + 1)) + min;
    }
    
    var y = 0;
    
    for (var i = 0; i < len; i++) {
        var randomTimeout = getRandomInt(1000, 10000);
    
        console.log(i + ', ' + randomTimeout + ', ' + ++y);
        setTimeout(function () {
            console.log(arguments);
        }, randomTimeout, randomTimeout, y);
    }
    

    CPU 使用率は少し高くなりますが、それほど高くはありませんか?

  • これらすべてのコールバックをソートされたリストに 1 つずつ実装すると、パフォーマンスが向上するかどうか疑問に思っています。

ベストアンサー1

すでにほとんどの作業は完了しています。V8 は ECMAScript の一部ではないため、実装を提供していませんsetTimeout。使用する関数は timers.js に実装されており、C クラスのラッパーであるオブジェクトのインスタンスを作成しますTimeout

ソースには、タイマーの管理方法を説明するコメントがあります。

// Because often many sockets will have the same idle timeout we will not
// use one timeout watcher per item. It is too much overhead.  Instead
// we'll use a single watcher for all sockets with the same timeout value
// and a linked list. This technique is described in the libev manual:
// http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod#Be_smart_about_timeouts

これは、リンクされた記事の #4 にある二重リンク リストを使用していることを示しています。

リクエストが 1 つではなく、何千 (何百万...) もあり、すべてが同じタイムアウト値を持つ何らかのタイムアウトを採用している場合は、さらに良い方法があります。

タイムアウトを開始するときは、タイムアウト値を計算し、タイムアウトをリストの最後に配置します。

次に、リストの先頭のタイムアウトが発生すると予想されるときに ev_timer を使用して起動します (たとえば、テクニック #3 を使用します)。

何らかのアクティビティがあった場合は、リストからタイマーを削除し、タイムアウトを再計算して、リストの最後に再度追加し、リストの先頭から取得された場合は ev_timer を必ず更新します。

この方法では、タイマーの開始、停止、更新に O(1) 時間で無制限のタイムアウトを管理できますが、大きな複雑さを伴い、一定のタイムアウトを使用する必要があります。一定のタイムアウトにより、リストがソートされた状態を維持できます。

Node.js は非同期操作を中心に設計されており、setTimeoutその重要な部分です。私は、トリッキーなことをしようとはせず、提供されているものを使用するだけにします。特定のケースでそれがボトルネックであることが証明されるまで、十分に高速であると信頼してください。時期尚早な最適化にこだわらないでください。

アップデート

基本的に、トップ レベルにタイムアウトの辞書があるため、100 ミリ秒のタイムアウトはすべてグループ化されます。新しいタイムアウトが追加されるか、最も古いタイムアウトがトリガーされると、リストに追加されます。つまり、最も古いタイムアウト (最も早くトリガーされるもの) がリストの先頭になります。このリストには 1 つのタイマーがあり、リストの最初の項目が期限切れになるまでの時間に基づいて設定されます。

同じタイムアウト値で 1000 回呼び出すとsetTimeout、呼び出した順にリストに追加されsetTimeout、並べ替えは必要ありません。これは非常に効率的な設定です。

おすすめ記事