2013-05-26 1 views
6

Ho lavorato per un po 'a un progetto di hobby (scritto in C), ed è ancora lontano dal completamento. È molto importante che sia veloce, quindi ho deciso di fare qualche benchmarking per verificare che il mio modo di risolvere il problema non sarebbe inefficiente.Come aumentare la frequenza della CPU del nuovo processo generato

$ time ./old 
real 1m55.92 
user 0m54.29 
sys 0m33.24 

ho ridisegnato le parti del programma per rimuovere in modo significativo le operazioni inutili, ridotto i fallimenti della cache di memoria e delle previsioni sbagliate avvengono di filiale. Il meraviglioso strumento Callgrind mi stava mostrando numeri sempre più impressionanti. La maggior parte del benchmarking è stata eseguita senza creare processi esterni.

$ 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 

Chiaramente stavo almeno facendo qualcosa di giusto. Tuttavia, eseguire il programma per davvero ha raccontato una storia diversa.

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

Come avrete notato, il tempo dipende in gran parte dai processi esterni. Non so cosa abbia causato la regressione. Non c'è niente di veramente strano in questo; solo un tradizionale vfork/execve/waitpid fatto da un singolo thread, eseguendo gli stessi programmi nello stesso ordine.

Qualcosa doveva essere causa di biforcazione lento, quindi ho fatto un piccolo test (simile a quello qui sotto) che avrebbe generato solo i nuovi processi e non ha alcun sovraccarico associato al mio programma. Ovviamente questo doveva essere il più veloce.

#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 

Immagino di no.

A questo punto ho deciso di votare le prestazioni per il governatore, e gli orari sono migliorate:

$ 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 

Sembra che ogni nuovo processo viene programmato su un nucleo separato e ci vuole un po 'per passare a un frequenza più alta. Non posso dire perché la vecchia versione fosse più veloce. Forse è stato fortunato. Forse (a causa della sua inefficienza) ha indotto la CPU a scegliere una frequenza più alta prima.

Un piacevole effetto collaterale del cambio di governatore era che anche i tempi di compilazione erano migliorati. Apparentemente la compilazione richiede la forking di molti nuovi processi. Tuttavia, non è una soluzione praticabile, poiché questo programma dovrà essere eseguito sui desktop di altre persone (e laptop).

L'unico modo che ho trovato per migliorare i tempi originario era quello di limitare il programma (e processi figli) per una singola CPU aggiungendo questo codice all'inizio:

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

che in realtà era il più veloce nonostante l'uso il valore di default "ondemand" governatore:

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

non solo è una soluzione hacker, ma non funziona bene nel caso in cui il programma lanciato utilizza più thread. Non c'è modo per il mio programma di saperlo.

Qualcuno ha qualche idea su come far funzionare i processi generati ad alta frequenza di clock della CPU? Deve essere automatizzato e non richiedere su privilegi. Anche se finora l'ho testato solo su Linux, intendo portarlo su più o meno tutti i più diffusi e impopolari sistemi operativi desktop (e verrà eseguito anche sui server). Qualsiasi idea su qualsiasi piattaforma è benvenuta.

+0

C'è quasi sicuramente qualcosa che sta succedendo qui che non si sta prendendo in considerazione. I governatori attivi della CPU non farebbero quel livello di differenza nelle prestazioni su nessun computer reale. Qual è il tuo programma? Che cosa sta facendo, come lo sta facendo e come lo stai testando? – duskwuff

+0

Si prega di provare il codice di prova. Per me funziona in 1m14s con OnDemand Governor e 32s con prestazioni (su un Core i7-2600). Sono sorpreso quanto te. – torso

+0

Intendevo la differenza di prestazioni mostrata in './New'. – duskwuff

risposta

1

La frequenza della CPU è vista (dalla maggior parte dei sistemi operativi) come una proprietà di sistema. Pertanto, non è possibile cambiarlo senza i diritti di root.Esiste qualche ricerca sulle estensioni per consentire l'adozione di programmi specifici; tuttavia dal momento che il modello di energia/prestazioni differisce anche per la stessa architettura generale, difficilmente troverete una soluzione generale.

Inoltre, tenere presente che, al fine di garantire l'equità, lo scheduler di Linux condivide il tempo di esecuzione dei processi perent e child per la prima epoca del bambino. Questo potrebbe avere un impatto sul tuo problema.