Was hat meine verstrichene Zeit viel länger verursacht als die Benutzerzeit?

8

Ich vergleiche einige R-Aussagen (siehe Details hier ) und festgestellt, dass meine verstrichene Zeit viel länger ist als meine Benutzerzeit.

%Vor%

Könnte mir jemand helfen zu verstehen, welche Faktoren (R oder Hardware) den Unterschied zwischen Benutzerzeit und verstrichener Zeit bestimmen und wie ich sie verbessern kann? Falls es hilft: Ich führe data.table Datenmanipulation auf einem Macbook Air 1.7Ghz i5 mit 4GB RAM durch.

Update: Mein grobes Verständnis ist, dass Benutzerzeit ist, was es meine CPU braucht, um meine Arbeit zu verarbeiten. Die verstrichene Zeit ist die Länge ab der ich einen Job einreiche, bis ich die Daten zurückbekomme. Was musste mein Computer nach 8 Sekunden noch tun?

Update: wie im Kommentar vorgeschlagen, ich ein paar Mal auf zwei data.table laufen: Y, mit 104 Spalten (sorry, ich füge mehr Spalten als Zeit vergeht), und X als eine Untermenge von Y mit nur 3 Schlüssel. Unten sind die Updates. Bitte beachten Sie, dass ich diese beiden Prozeduren nacheinander ausgeführt habe, also sollte der Speicherzustand ähnlich sein.

%Vor%

Hier sehen Sie die Größe der zwei einzigen Objekte in meinem Arbeitsbereich (Kommas hinzugefügt). :

%Vor%

Danke

    
AdamNYC 03.12.2012, 17:46
quelle

1 Antwort

14

Die Benutzerzeit gibt an, wie viele Sekunden der Computer Ihre Berechnungen ausgeführt hat. Die Systemzeit gibt an, wie viel Zeit das Betriebssystem damit verbracht hat, auf die Anforderungen Ihres Programms zu reagieren. Abgelaufene Zeit ist die Summe dieser beiden, plus was auch immer "warten" um Ihr Programm und / oder das OS zu tun hatte. Es ist wichtig zu beachten, dass diese Zahlen die Summe der aufgewendeten Zeit sind. Ihr Programm wird möglicherweise für 1 Sekunde berechnet, wartet dann eine Sekunde auf dem Betriebssystem, wartet dann 3 Sekunden auf der Festplatte und wiederholt diesen Zyklus mehrmals, während es ausgeführt wird.

Aufgrund der Tatsache, dass Ihr Programm so viel Systemzeit benötigt wie die Benutzerzeit, war es eine sehr IO-intensive Sache. Vieles von der Festplatte lesen oder viel auf die Festplatte schreiben. RAM ist ziemlich schnell, normalerweise einige hundert Nanosekunden. Wenn also alles in RAM passt, ist die verstrichene Zeit normalerweise nur ein bisschen länger als die Benutzerzeit. Die Suche auf der Festplatte kann jedoch einige Millisekunden dauern, und die Antwort auf die Daten dauert länger. Das ist um einen Faktor von einer Million langsamer.

Wir haben festgestellt, dass Ihr Prozessor "Sachen macht" für ~ 8 + ~ 8 = ~ 16 Sekunden. Was hat es für die anderen ~ 54 - ~ 16 = ~ 38 Sekunden gemacht? Warten auf die Festplatte, um die angeforderten Daten zu senden.

UPDATE1:

Matthew hatte einige ausgezeichnete Punkte gemacht, dass ich Annahmen mache, die ich wahrscheinlich nicht machen sollte. Adam, wenn Sie eine Liste aller Zeilen in Ihrer Tabelle veröffentlichen möchten (Datentypen sind alles, was wir brauchen), können wir eine bessere Vorstellung davon bekommen, was vor sich geht.

Ich habe gerade ein kleines Do-nothing-Programm ausgearbeitet, um meine Annahme zu bestätigen, dass die Zeit, die nicht im Userspace und im Kernel-Space verbracht wird, wahrscheinlich für IO ausgegeben wird.

%Vor%

Wenn ich das resultierende Programm und die Zeit starte, sehe ich etwas wie folgt:

%Vor%

Wie Sie an der Inspektion sehen können, arbeitet das Programm nicht wirklich und als solches fordert es den Kernel nicht auf, etwas zu tun, um es in den Arbeitsspeicher zu laden und es zu starten. Also fast die ganze "echte" Zeit wird als "Benutzer" Zeit verbracht.

Nun ein Kernel-schweres Do-Nothing-Programm (mit ein paar weniger Iterationen, um die Zeit vernünftig zu halten):

%Vor%

Wenn ich das hier ausführe, sehe ich etwas mehr so:

%Vor%

Wieder ist es leicht zu sehen, dass das Programm wenig mehr als bittet, dem Kernel zufällige Bytes zu geben. / dev / urandom ist eine nicht blockierende Quelle der Entropie. Was bedeutet das? Der Kernel verwendet einen Pseudozufallszahlengenerator, um schnell "zufällige" Werte für unser kleines Testprogramm zu generieren. Das bedeutet, dass der Kernel einige Berechnungen durchführen muss, aber er kann sehr schnell zurückkehren. Also wartet dieses Programm meistens darauf, dass der Kernel es berechnet, und wir können sehen, dass sich dies in der Tatsache widerspiegelt, dass fast die ganze Zeit für sys verbraucht wird.

Jetzt werden wir eine kleine Veränderung vornehmen. Anstatt von / dev / urandom zu lesen, das nicht blockierend ist, lesen wir von / dev / random, das blockiert. Was bedeutet das? Es macht nicht viel Rechnen, aber es wartet darauf, dass Dinge auf Ihrem Computer passieren, die die Kernel-Entwickler empirisch ermittelt haben, ist zufällig. (Wir werden auch viel weniger Iterationen machen, da das Zeug viel länger braucht)

%Vor%

Und wenn ich diese Version des Programms laufe und teste, sehe ich folgendes:

%Vor%

Es dauerte 41 Sekunden, um zu laufen, aber unermesslich kleine Mengen an Zeit auf Benutzer und Real. Warum das? Die ganze Zeit wurde im Kernel verbracht, aber keine aktive Berechnung. Der Kernel wartete nur darauf, dass etwas passierte. Sobald genug Entropie gesammelt wurde, würde der Kernel wieder aufwachen und die Daten an das Programm zurücksenden. (Beachten Sie, dass es möglicherweise weniger oder viel mehr Zeit benötigt, um auf Ihrem Computer ausgeführt zu werden, je nachdem, was alles vor sich geht). Ich argumentiere, dass der Unterschied zwischen Benutzer + System und Real IO ist.

Was bedeutet das alles? Es beweist nicht, dass meine Antwort richtig ist, weil es andere Erklärungen dafür geben könnte, warum du das Verhalten siehst, das du bist. Aber es zeigt die Unterschiede zwischen der Rechenzeit des Benutzers, der Rechenzeit des Kernels und dem, was ich behaupte, ist die Zeit, die ich mit IO verbracht habe.

Hier ist meine Quelle für den Unterschied zwischen / dev / urandom und / dev / random: Ссылка

UPDATE2:

Ich dachte, ich würde versuchen, Matthews Vorschlag zu adressieren, dass möglicherweise L2-Cache-Misses die Ursache des Problems sind. Der Core i7 hat eine 64-Byte-Cache-Zeile. Ich weiß nicht, wie viel Sie über Caches wissen, also werde ich einige Details zur Verfügung stellen. Wenn Sie nach einem Wert aus dem Speicher fragen, erhält die CPU nicht nur diesen einen Wert, sondern alle 64 Bytes. Das bedeutet, wenn Sie auf Speicher in einem sehr vorhersagbaren Muster zugreifen - wie zB Array [0], Array [1], Array [2], usw. - dauert es eine Weile, um den Wert 0 zu erhalten, aber dann 1, 2, 3, 4 ... sind viel schneller. Bis Sie zur nächsten Cache-Zeile kommen. Wenn dies ein Array von Ints wäre, wäre 0 langsam, 1..15 wäre schnell, 16 wären langsam, 17..31 wäre schnell, usw.

Ссылка

Um das auszuprobieren, habe ich zwei Programme gemacht. Sie haben beide ein Array von Strukturen mit 1024 * 1024 Elementen. In einem Fall hat die Struktur ein einzelnes Doppel, in der anderen hat sie 8 Doppel. Ein Double ist 8 Bytes lang, so dass wir im zweiten Programm auf den Speicher in der schlechtesten Weise für einen Cache zugreifen. Die erste wird den Cache schön benutzen.

%Vor%

Wenn ich dieses Programm starte, sehe ich diese Ausgabe:

%Vor%

Hier ist das Programm mit den großen Strukturen, die jeweils 64 Byte Speicher belegen, nicht 8.

%Vor%

Und wenn ich es laufe, sehe ich das:

%Vor%

Das zweite Programm - dasjenige, das Cache-Fehler verursacht hat - dauerte fünfmal so lange, um für genau die gleiche Anzahl von Speicherzugriffen zu laufen.

Bemerkenswert ist auch, dass in beiden Fällen die gesamte aufgewendete Zeit in Benutzer und nicht in System verbracht wurde. Das bedeutet, dass das Betriebssystem die Zeit zählt, die Ihr Programm auf Daten auf Ihrem Programm warten muss, nicht auf das Betriebssystem. Angesichts dieser beiden Beispiele denke ich, dass es unwahrscheinlich ist, dass Cache-Fehler dazu führen, dass Ihre verstrichene Zeit wesentlich länger ist als Ihre Benutzerzeit.

UPDATE3:

Ich habe gerade dein Update gesehen, dass die wirklich abgespeckte Tabelle ungefähr 10x schneller lief als die normale Größe. Das würde mir auch zeigen, dass Ihnen (wie auch ein anderer Matthew sagte) der RAM ausgeht.

Sobald Ihr Programm versucht, mehr Speicher zu verwenden, als Ihr Computer tatsächlich installiert hat, beginnt es mit dem Austausch auf die Festplatte. Das ist besser als Ihr Programm abstürzt, aber es ist viel langsamer als RAM und kann erhebliche Verlangsamungen verursachen.

Ich werde versuchen, ein Beispiel zusammenzustellen, das morgen die Swap-Probleme zeigt.

UPDATE4:

Okay, hier ist ein Beispielprogramm, das dem vorherigen sehr ähnlich ist. Aber jetzt ist die Struktur 4096 Bytes, nicht 8 Bytes. Insgesamt wird dieses Programm 2 GB Speicher anstelle von 64 MB verwenden. Ich ändere auch die Dinge ein wenig und sorge dafür, dass ich die Dinge nach dem Zufallsprinzip anstatt nach Element-für-Element zugreife, damit der Kernel nicht schlau wird und anfängt, meine Programmanforderungen zu antizipieren. Die Caches werden durch Hardware gesteuert (die nur durch einfache Heuristiken gesteuert wird), aber es ist durchaus möglich, dass kswapd (der Kernel-Swap-Daemon) wesentlich intelligenter ist als der Cache.

%Vor%

Von dem Programm, das ich cache_hits zu cache_misses aufgerufen habe, haben wir gesehen, wie die Speichergröße um 8x erhöht und die Ausführungszeit um 5x erhöht wurde. Was erwarten Sie, wenn wir dieses Programm ausführen? Es verwendet 32x so viel Speicher wie cache_misses, hat aber die gleiche Anzahl von Speicherzugriffen.

%Vor%

Es dauerte 8x so lange wie cache_misses und 40x so lange wie cache_hits. Und das ist auf einem Computer mit 4 GB RAM. Ich habe 50% meines Arbeitsspeichers in diesem Programm verwendet, im Gegensatz zu 1,5% für cache_misses und 0,2% für cache_hits. Es wurde wesentlich langsamer, obwohl es nicht den gesamten RAM meines Computers verbraucht. Es war genug, um signifikant zu sein.

Ich hoffe, das ist ein guter Grund, wie man Probleme mit langsam laufenden Programmen diagnostizieren kann.

    
Mike Sandford 06.12.2012 21:23
quelle

Tags und Links