wo bekommt top die speichernutzung her?
-
was ist es dann was dort drin steht?
gruss
msp
-
/proc ist nur ein Interface um diverse Informationen vom Kernel zu bekommen bzw. diese dem Kernel zur Laufzeit zu übergeben; das sind also keine richtigen Dateien sondern nur ein Pseudo-Dateisystem als Schnittstelle zum Kernel.
Die Manpage zu proc verrät ein bisschen mehr.
-
Deshalb wirst du in deiner /etc/fstab sehen, dass der Mountpoint /proc kein Filesystem wie /dev/hdXXX oder /dev/sdXXX hat, sondern einfach nur "proc" hat, weil diese Pseude-Dateien eigentlich in keiner Festplatte gespeichert werden.
-
Ich habe die starke Vermutung, dass top nur deshalb so ineffizient auf Linux ist, weil es die Informationen aus dem procfs parsen muss. In dieser Hinsicht ist es leider so, dass solche Informationen über ein Pseudodateisystem bereitgestellt werden, anstatt, dass man direkt Funktionen anbietet.
-
Ponto schrieb:
Ich habe die starke Vermutung, dass top nur deshalb so ineffizient auf Linux ist, weil es die Informationen aus dem procfs parsen muss.
Inwiefern ist top ineffizient?
Btw, schau Dir einfach mal die Sourcen wenn Du Dir nicht sicher bist; ist ja alles offen.In dieser Hinsicht ist es leider so, dass solche Informationen über ein Pseudodateisystem bereitgestellt werden, anstatt, dass man direkt Funktionen anbietet.
Was ist hier am Pseudo-FS schlechter?
-
nman schrieb:
Ponto schrieb:
Ich habe die starke Vermutung, dass top nur deshalb so ineffizient auf Linux ist, weil es die Informationen aus dem procfs parsen muss.
Inwiefern ist top ineffizient?
Btw, schau Dir einfach mal die Sourcen wenn Du Dir nicht sicher bist; ist ja alles offen.top verbraucht 2-5% CPU Leistung auf einem Celeron 600. Beim Start sind es bis zu 20%. Das ist genauso viel wie artsd benötigt um mp3 Dateien abzuspielen.
In den Sourcen sehe ich mehrere sscanf welche die Dateien parsen. Ich bin jedoch nicht 100% sicher, dass dies die einzige Methode ist, die die Tools anwenden.
nman schrieb:
In dieser Hinsicht ist es leider so, dass solche Informationen über ein Pseudodateisystem bereitgestellt werden, anstatt, dass man direkt Funktionen anbietet.
Was ist hier am Pseudo-FS schlechter?
Die Tatsache, dass man die Daten erst parsen muss. Einerseits muss der Kernel aus integralen Werten Strings machen, die ausgelesen werden können. Dann muss das Anwendungsprogramm aus den Strings wieder integrale Werte machen. Das sind zwei Umwandlungen zu viel und kosten entsprechend CPU-Zeit.
Ein Beispiel ist die Frage von mcr bezüglich der verbratenen CPU-Zeit in einem Thread. Man kann dies herausfinden indem man aus der Datei /proc/self/tasks/<tid>/stat die 13. Zahl nach dem Programmnamen nimmt. Gegenüber einem Aufruf von times() oder gerusage() (die nur für den ganzen Prozess funktionieren) ist das wesentlich ineffizienter. Will man die Laufzeit kleiner Funktionen messen, so kann man dies wegen des Overheads getrost vergessen.
Pseudo-FS an sich sind nicht schlecht. Schlecht ist, dass es für viele Informationen keine schnelle C-API gibt.
-
Ponto schrieb:
top verbraucht 2-5% CPU Leistung auf einem Celeron 600. Beim Start sind es bis zu 20%.
Kann ich absolut nicht nachvollziehen; auf meinem alten 580er Athlon hat top nie über 1.5-2% gebraucht und auf meinem 2000er braucht es nie über 0.2%.
Ich finde eigentlich nicht, dass man statt /proc besser APIs haben sollte, die ein klitzekleines bisschen schneller und dafür wesentlich weniger transparent sind.
-
nman schrieb:
Ponto schrieb:
top verbraucht 2-5% CPU Leistung auf einem Celeron 600. Beim Start sind es bis zu 20%.
Kann ich absolut nicht nachvollziehen; auf meinem alten 580er Athlon hat top nie über 1.5-2% gebraucht und auf meinem 2000er braucht es nie über 0.2%.
Ich finde eigentlich nicht, dass man statt /proc besser APIs haben sollte, die ein klitzekleines bisschen schneller und dafür wesentlich weniger transparent sind.
Wir wollen mal messen wie klitzeklein der Geschwindigkeitsunterschied ist. Man sieht auch direkt, dass der Code zum Parsen nicht gerade trivial ist:
extern "C" { #include <time.h> #include <sys/time.h> #include <sys/times.h> #include <sys/resource.h> #include <errno.h> #include <unistd.h> #include <pthread.h> #include <errno.h> #include <asm/unistd.h> #include <sys/syscall.h> #include <linux/unistd.h> #include <sys/types.h> #include <fcntl.h> #include <sys/stat.h> } #include <sstream> #include <iostream> static int fd; static int pos; _syscall0(pid_t,gettid) void Timer_Init() { std::stringstream ss; ss << "/proc/self/task/" << gettid() << "/stat"; fd = open(ss.str().c_str(), O_RDONLY); if (fd < 0) { std::cerr << "Could not open stat file\n"; exit(-1); } char buffer[1000]; pos = read(fd, buffer, 1000); if (pos < 0) { std::cerr << "Could not read stat file\n"; exit(-1); } while (buffer[--pos] != ')'); pos += 4; } double get_cpuusage() { lseek(fd, pos, SEEK_SET); char buffer[1000]; read(fd, buffer, 1000); int curpos = 0; for (int i = 0; i != 10; ++i, ++curpos) { while (buffer[curpos] != ' ') ++curpos; } unsigned long long utime, stime; sscanf(buffer+curpos, "%Lu %Lu", &utime, &stime); return (double) (utime+stime) / 100.0; } double get_cpuusage_times() { static long long clockspersec = sysconf(_SC_CLK_TCK); struct tms tm; times(&tm); return (double) (tm.tms_utime + tm.tms_stime) / clockspersec; } static double cputime = 0.0; void func() { double cpuTime0 = get_cpuusage(); for (int i = 0; i != 100; ++i); double cpuTime1 = get_cpuusage(); cputime += cpuTime1 - cpuTime0; } // void func() // { // double cpuTime0 = get_cpuusage_times(); // for (int i = 0; i != 100; ++i); // double cpuTime1 = get_cpuusage_times(); // cputime += cpuTime1 - cpuTime0; // } extern "C" void * execute(void *) { Timer_Init(); for (int i = 0; i != 1000000; ++i) func(); std::cout << "CPU Time: " << cputime << std::endl; return 0; } int main() { pthread_t tid; pthread_create(&tid, 0, execute, 0); pthread_join(tid, 0); }
Der Code oben misst die Zeit, die in der Funktion func() verbraten wurde. Diese wird 1000000 mal aufgerufen und macht fast nichts. Um die CPU-Zeit herauszukriegen müssen wir aus der Datei /proc/self/task/<gettid()>/stat den 14. und 15. Eintrag addieren. Da dieser Eintrag keine feste Position hat, müssen wir erst die Position suchen. Dann können wir die Werte in integrale Werte umwandeln und als Zeitpunkte verwenden. Der Code hat auf viele Fehlerbehandlungen verzichtet, um halbwegs lesbar zu bleiben.
Wenn wir die Zeitmessung durch times() ersetzen, was normalerweise für threaded Code nicht korrekte Werte liefert erhalten wir:
ponto@preston:~/tmp/threads/clock> g++ diff.C -pthread -O3 -Wall -W -Wno-long-long ponto@preston:~/tmp/threads/clock> time ./a.out CPU Time: 0.34 real 0m0.672s user 0m0.199s sys 0m0.473s
In diesem Fall sind die Werte brauchbar, da der Main Thread nichts macht.
Nun nehmen wir die Methode, die /proc analysiert:
ponto@preston:~/tmp/threads/clock> g++ diff.C -pthread -O3 -Wall -W -Wno-long-long ponto@preston:~/tmp/threads/clock> time ./a.out CPU Time: 8.58 real 0m17.599s user 0m1.306s sys 0m16.251s
Dies verbraucht unnötigerweise 17 Sekunden CPU-Zeit, die zum größten Teil im Kernel verbraten werden. Der Kernel muss ja jeden Eintrag in /proc/.../stat generieren, obwohl uns nur zwei Werte interessieren. Weiterhin entspricht die gemessene Zeit in keinster Weise dem Verbrauch in der gemessenen Funktion.
In diesem Fall haben wir also klitzekleine 17 Sekunden länger gebraucht wie eine Funktion, die die Werte ohne Konvertierung aus dem Kernel ausliest.
-
lol
Klar, sehr praxisnah sowas
Erwartest Du, dass solche Beiträge ernst genommen werden?
-
nman schrieb:
lol
Klar, sehr praxisnah sowas
Erwartest Du, dass solche Beiträge ernst genommen werden?Eigentlich schon. Weil kein Profiler vernünftige Werte herausgegeben hat, hab ich bereits Funktionen gemessen, die mehrere Millionen mal aufgerufen wurden aber nur wenig länger waren. Wenn dann die Zeitmessung so ungenau ist und so einen Overhead hat, ist man aufgeschmissen.
-
Nichts für ungut, aber auch wenn Du scheinbar keinen Profiler bedienen kannst, bin ich nicht der Ansicht, dass es stört, wenn eine Million(!) Aufrufe Deiner Funktion ganze sieben Sekunden brauchen.