2013-05-26 9 views
6

Ich habe schon eine Weile an einem Hobby-Projekt gearbeitet (geschrieben in C) und es ist noch lange nicht abgeschlossen. Es ist sehr wichtig, dass es schnell geht, also habe ich kürzlich beschlossen, ein Benchmarking durchzuführen, um zu überprüfen, dass meine Art der Lösung des Problems nicht ineffizient wäre.Wie kann ich die CPU-Frequenz des neu erzeugten Prozesses erhöhen?

Ich habe Teile des Programms neu entworfen, um unnötige Operationen zu entfernen, Speicher-Cache-Misses und Verzweigungsfehlvorhersagen zu reduzieren. Das wunderbare Callgrind Tool zeigte mir immer mehr beeindruckende Zahlen. Der Großteil des Benchmarking wurde ohne externe Prozesse durchgeführt.

$ time ./old --dry-run 
real 0m00.75 
user 0m00.28 
sys 0m00.24 

$ time ./new --dry-run 
real 0m00.15 
user 0m00.12 
sys 0m00.02 

Offensichtlich habe ich zumindest etwas richtig gemacht. Aber das Programm für real zu führen, erzählte eine andere Geschichte.

$ time ./new 
real 2m00.29 
user 0m53.74 
sys 0m36.22 

Wie Sie vielleicht bemerkt haben, hängt die Zeit hauptsächlich von den externen Prozessen ab. Ich weiß nicht, was die Regression verursacht hat. Es ist nichts wirklich komisches daran; nur ein traditioneller vfork/execve/waitpid, der von einem einzigen Thread ausgeführt wird und dieselben Programme in der gleichen Reihenfolge ausführt.

Irgendetwas musste dafür sorgen, dass Forking langsam war, also machte ich einen kleinen Test (ähnlich dem unten), der nur die neuen Prozesse hervorbringen würde und keinen Overhead mit meinem Programm haben würde. Das musste natürlich am schnellsten sein.

#define _GNU_SOURCE 
#include <fcntl.h> 
#include <sys/wait.h> 
#include <unistd.h> 

int main(int argc, const char **argv) 
{ 
    static const char *const _argv[] = {"/usr/bin/md5sum", "test.c", 0}; 

    int fd = open("/dev/null", O_WRONLY); 
    dup2(fd, STDOUT_FILENO); 
    close(fd); 

    for (int i = 0; i < 100000; i++) 
    { 
     int pid = vfork(); 
     int status; 
     if (!pid) 
     { 
      execve("/usr/bin/md5sum", (char*const*)_argv, environ); 
      _exit(1); 
     } 
     waitpid(pid, &status, 0); 
    } 
    return 0; 
} 

$ time ./test 
real 1m58.63 
user 0m68.05 
sys 0m30.96 

Ich denke nicht.

Zu dieser Zeit habe ich beschlossen, die Leistung für den Gouverneur zu wählen, und die Zeiten noch besser geworden:

$ for i in 0 1 2 3 4 5 6 7; do sudo sh -c "echo performance > /sys/devices/system/cpu/cpu$i/cpufreq/scaling_governor";done 
$ time ./test 
real 1m03.44 
user 0m29.30 
sys 0m10.66 

Es scheint, wie jeder neue Prozess auf einem separaten Kern geplant wird, und es dauert eine Weile, um es zu einem wechseln höhere Frequenz. Ich kann nicht sagen, warum die alte Version schneller lief. Vielleicht war es ein Glück. Vielleicht hat es (wegen seiner Ineffizienz) die CPU früher dazu veranlasst, eine höhere Frequenz zu wählen.

Ein schöner Nebeneffekt der Änderung des Governors war, dass die Kompilierzeiten ebenfalls verbessert wurden. Offenbar erfordert das Kompilieren viele neue Prozesse. Es ist jedoch keine praktikable Lösung, da dieses Programm auf anderen Desktops (und Laptops) ausgeführt werden muss.

Der einzige Weg, ich war es, die ursprünglichen Zeiten zu verbessern, fand das Programm (und Kindprozesse) zu einer einzigen CPU zu beschränken, indem Sie diesen Code am Anfang:

cpu_set_t mask; 
CPU_ZERO(&mask); 
CPU_SET(0, &mask); 
sched_setaffinity(0, sizeof(mask), &mask); 

Was war eigentlich die schnellste trotz Anwendung der Standard „On-Demand“ Gouverneur:

$ time ./test 
real 0m59.74 
user 0m29.02 
sys 0m10.67 

es ist nicht nur eine hackish Lösung, aber es das gestartete Programm verwendet mehrere Threads bei nicht gut funktioniert. Das kann mein Programm nicht wissen.

Hat jemand eine Idee, wie die erzeugten Prozesse mit hoher CPU-Taktfrequenz laufen können? Es muss automatisiert werden und darf keine besonderen Anforderungen erfüllen. Obwohl ich dies bisher nur auf Linux getestet habe, beabsichtige ich, dies auf mehr oder weniger alle gängigen und unpopulären Desktop-Betriebssysteme zu portieren (und es wird auch auf Servern laufen). Jede Idee auf jeder Plattform ist willkommen.

+0

Es ist fast sicher etwas anderes passiert hier, dass Sie nicht in Betracht ziehen. Die aktiven CPU-Regler würden diesen Leistungsunterschied auf keinem realen Computer erreichen. Was ist dein Programm? Was macht es, wie macht es das, und wie testen Sie es? – duskwuff

+0

Bitte versuchen Sie den Testcode. Für mich läuft es in 1m14s mit Ondemand Governor und 32s mit Leistung (auf einem Core i7-2600). Ich bin genauso überrascht wie du. – torso

+0

Ich meinte den Leistungsunterschied in './New'. – duskwuff

Antwort

1

Die CPU-Frequenz wird (von den meisten Betriebssystemen) als Systemeigenschaft angesehen. Daher können Sie es ohne Root-Rechte nicht ändern.Es gibt einige Nachforschungen über Erweiterungen, um eine Annahme für spezifische Programme zu ermöglichen. Da sich das Energie-/Leistungsmodell jedoch auch für die gleiche allgemeine Architektur unterscheidet, werden Sie kaum eine allgemeine Lösung finden.

Beachten Sie außerdem, dass der Linux-Scheduler die Ausführungszeit von perent und child für die erste Epoche des untergeordneten Elements teilt, um Fairness zu gewährleisten. Dies könnte sich auf Ihr Problem auswirken.