2016-04-25 4 views
0

Betrachten Sie den folgenden Code, der pi annähert:Sind die Kindprozesse von Knoten "viel" langsamer?

// filename: pi.js 
function approx_pi(from, to) { 
    var pi = 0; 
    var dx = 0.0000000005; 
    for(var x = from; x < to; x += dx) { 
     pi += 4/(1 + x * x); 
    } 
    return pi * dx; 
} 

var min = Infinity; 
var max = -1; 
var avg = 0; 

for(var itr = 0; itr < 10; ++itr) { 
    var start = process.hrtime(); 
    var pi = approx_pi(0, 1); 
    var diff = process.hrtime(start); 

    var duration = diff[0] + diff[1] * 1e-9; 

    min = (duration <= min) ? duration : min; 
    max = (duration >= max) ? duration : max; 
    avg += duration; 
} 

avg /= 10; 

min = min.toFixed(3); 
max = max.toFixed(3); 
avg = avg.toFixed(3); 

console.log("Time: min = %ss, max = %ss , avg = %ss", min, max, avg); 

Auch diesen Code betrachten, der die Datei über Prozesse in n Kind läuft, während n durch Befehlszeilenargumente gegeben:

//filename: children.js 
var cp = require('child_process'); 

var n = parseInt(process.argv[2]); 

for(var k = 0; k < n; ++k) { 
    cp.fork('pi.js'); 
} 

Beim Laufen node pi.js die Ausgang:

Time: min = 19.113s, max = 22.220s , avg = 21.152s 

Ein ähnliches Ergebnis kommt node children.js 1 aus läuft (was ich s erwartet)

Time: min = 17.323s, max = 21.465s , avg = 19.979s 

Stuff beginnt seltsam (zumindest meine eigenen Erwartungen), wenn mehr als ein Kind gegabelt wird. Hier ist die Ausgabe nach node children.js 2 ausgeführt wird:

Time: min = 29.824s, max = 41.050s , avg = 35.136s 
Time: min = 30.036s, max = 40.791s , avg = 35.246s 

jedes Kind nahm mindestens 14 Sekunden als die einzelnen Versionen. Es kam noch schlimmer auf Lauf 4 Kinder mit node children.js 4, das jeweils etwa 37 Sekunden länger dauerte bis zum Ende:

Time: min = 55.878s, max = 68.047s , avg = 58.845s 
Time: min = 52.760s, max = 69.168s , avg = 58.880s 
Time: min = 57.151s, max = 69.113s , avg = 58.956s 
Time: min = 50.790s, max = 70.344s , avg = 59.546s 

Diese Tests auf einer Maschine mit Core i5 2410M CPU @ 2.30GHz (4 Kernen sichtbar für das o) liefen, und ein 4GB RAM, mit beiden: Windows 7 mit Knoten v4.2.3 und Ubuntu14.04 mit Knoten v5.4.1. Jedes Betriebssystem zeigte, dass jedes der Kinder auf seinem eigenen Kern lief und jeder dieselbe Menge an zugewiesenem Speicher hatte (etwa 9 MB).

Entsprechend der Node-Dokumentation here: "Jeder Prozess hat seinen eigenen Speicher mit eigenen V8-Instanzen". So würde man erwarten, dass jedes Kind so gut abschneiden sollte wie der einzelne Prozess.

Sind meine Erwartungen fehlgeleitet und dies ist das erwartete Verhalten oder was ist los?

+0

Ihr Test sollte wirklich mindestens 10 mal wiederholt werden und dann die Min-, Max- und Durchschnittswerte melden. Dies gibt V8 Zeit, um Ihren Code zu kompilieren (der erste Lauf wird immer der langsamste sein). Außerdem ist Date.now() nicht sehr präzise (so wird es die Zeit tendenziell überschätzen). – JDB

+0

@JDB Ich gemittelt über 10 Iterationen wie Sie hingewiesen und verwendet 'process.hrtime' anstelle von' Date.now'. Die Daten unterschieden sich jedoch nicht sehr. – m0stafa

Antwort

2

Sie laufen 4 Prozesse parallel auf einer CPU mit 2 cores (4 threads) und messen dann die tatsächliche Zeit statt der CPU-Zeit. Natürlich wird jeder Prozess länger dauern ... Sie können nicht eine unendliche Anzahl von Prozessen auf einer begrenzten Anzahl von Kernen ausführen und erwarten, dass die Laufzeit identisch mit der Ausführung eines einzelnen Prozesses auf mehreren Kernen ist. (Wenn jemand herausfindet, wie man das macht, dann ist Cybersecurity Toast.)

Sie könnten erwarten, dass 2 Prozesse, die auf 2 Kernen ausgeführt werden, gleichzeitig laufen wie ein einzelner Prozess (der auf einem einzelnen Kern ausgeführt werden muss). aber die Wirklichkeit ist oft nicht ganz dasselbe wie die Theorie. Es ist sehr wahrscheinlich, dass Ihre 2 Prozesse tatsächlich Zeit auf demselben Kern teilen. Es gibt keinen einfachen Weg, um sicherzustellen, dass jeder Prozess seinen eigenen Kern bekommt, da diese Art von Details fast immer von Ihrer Kontrolle abstrahiert werden.

+0

Ich habe das Hyper-Threading auf meinem Computer deaktiviert, sodass die zwei Kerne, die für das Betriebssystem sichtbar sind, eigentlich zwei Kerne sind, nicht zwei Threads auf demselben Kern. Ich lief dann 'node children.js 1' und' node chidren.js2', der Unterschied war diesmal +10s (ein bisschen weniger als mit Hyper-threading, was +14s war), aber es ist immer noch viel!Ich habe die gleiche Aufgabe mit C++ mit MPI versucht und der Unterschied war ca. 3.3s! denkst du, dass die extra + 6.7s in js nur auf C++ geschwindigkeit gegenüber js zurückzuführen ist? – m0stafa

+0

Sie können immer noch nicht garantieren, dass die Prozesse jeweils auf einem separaten Kern ausgeführt wurden. Das ist der entscheidende Faktor ... laufen sie auf separaten Kernen (wirklich parallel) oder teilen sie sich einen Kern (die Geschwindigkeit jedes einzelnen reduzieren)? Die Tatsache, dass ein einzelner untergeordneter Prozess zur gleichen Zeit wie der übergeordnete Prozess ausgeführt wird, würde bedeuten, dass untergeordnete Prozesse nicht langsamer sind. Die Geschwindigkeitsdifferenz mit C++ scheint für mich nicht relevant zu sein ... Ich würde vermuten, dass C++ sowieso schneller läuft, da es in nativen Bytecode kompiliert wird, während Node einen JIT Compiler verwendet, so dass der Prozentsatz schneller wäre als der rohe Zahlen. – JDB

+0

Nun, nach dem OS-Task-Manager laufen sie auf separaten Kernen. Wenn ich einen der Prozesse abbringe, sinkt die Auslastung% eines Kerns, während der andere bei 100% bleibt. Aber die Sache ist, dass wenn ein einzelnes Kind läuft, es einen ganzen Kern für sich selbst braucht, die os Prozesse und der Elternprozess teilen sich den anderen Kern. Wenn zwei Kinder auf beiden Kernen laufen, können sie nicht alle für sich selbst haben, sie teilen sie mit den anderen Betriebssystemen. Das erklärt, warum sie länger brauchen, aber das Ausmaß des Unterschieds ist es, was mich nervt! – m0stafa