2017-06-22 1 views
2

J'ai essayé de chronométrer combien de temps il faut pour une invocation de popen pour terminer. popen initialise un processus qui crée ensuite un tube, forge et invoque le shell. Dans mon cas particulier, j'utilise l'appel pour lire une autre sortie de programmes stdout. Le problème : Je m'attends à l'appel que je fais pour retourner la bonne durée d'exécution du programme (environ 15 secondes pour un programme de test). Ce que je reçois, c'est que le programme n'a pas pris le temps de finir (0,000223s). Malgré toutes les diverses fonctions que j'ai essayées, je semble incapable de chronométrer l'appel correctement.Impossible de chronométrer l'exécution de la commande dans C (popen)?

Voici un exemple reproductible de mon problème. Il est composé du programme de calendrier et un programme d'enfants que le programme de synchronisation fonctionne (l'enfant prend environ 15 secondes pour courir sur mon système):

#include <stdio.h> 
#include <stdlib.h> 
#include <time.h> 
#include <sys/time.h> 
#ifdef __MACH__ 
#include <mach/clock.h> 
#include <mach/mach.h> 
#endif 

#define MAXBUF 10 

static void gettime (struct timespec *t) { 
#ifdef __MACH__ 
    clock_serv_t cclock; 
    mach_timespec_t mts; 
    host_get_clock_service(mach_host_self(), REALTIME_CLOCK, &cclock); 
    clock_get_time(cclock, &mts); 
    mach_port_deallocate(mach_task_self(), cclock); 
    t->tv_sec = mts.tv_sec; 
    t->tv_nsec = mts.tv_nsec; 
#else 
    clock_gettime(CLOCK_REALTIME, t); 
#endif 
} 

int main (void) { 
    FILE *fp; 
    struct timespec tic, toc; 
    char *executableName = "./a.out"; 
    char answer[MAXBUF]; 

    gettime(&tic); 
    if ((fp = popen(executableName, "r")) == NULL) { 
     fprintf(stderr, "The file couldn't be opened.\n"); 
     return 1; 
    } 
    gettime(&toc); 

    fgets(answer, MAXBUF, fp); 
    double elapsed = (double)(toc.tv_nsec - tic.tv_nsec)/1E9; 
    fprintf(stdout, "The program says %s, and took %fs to run!\n", answer, elapsed); 

    pclose(fp); 
    return 0; 
} 

Voici le programme des enfants:

#include <stdio.h> 
#include <stdlib.h> 

int timeWastingFunction (long long n) { 
    if ((n % 2) == 0) { 
     return 1; 
    } 
    for (int i = 1; i < (n/2); i += 2) { 
     if ((n % i) == 0) { 
      return 1; 
     } 
    } 
    return 0; 
} 

int main (void) { 
    int start = 687217000; 

    while (start--) { 
     timeWastingFunction(start); 
    } 
    fprintf(stdout, "Hello!"); 
    return 0; 
} 

Cette peut sembler un peu exagéré, mais j'avais précédemment essayé d'utiliser clock_t, (une fonction de synchronisation basée sur CPU) pour faire le chronométrage, et obtenu les mêmes réponses. J'ai donc essayé la solution this que vous voyez ci-dessus. J'ai choisi: CLOCK_REALTIME comme il semblait approprié pour le travail. Je n'ai malheureusement pas l'option de spécifier si cette horloge est au niveau d'un processus ou d'un thread par thread (je souhaite que ce processus soit indépendant du processus).

Note: Je n'ai pas essayé d'utiliser gettimeofday encore, mais je ne veux pas depuis son apparence inappropriée pour chronométrer cette façon, en fonction de la date du système en utilisant, et étant éliminé en faveur de clock_gettime.

Edit: Pour être clair, ce qui se passe quand je lance est que le programme appelant popen sera effectivement décrochage pour les 15 secondes, l'autre programme prend pour exécuter, avant d'imprimer le temps « mauvais ». Il n'imprime pas immédiatement le temps ce qui implique qu'il n'a pas attendu l'appel pour terminer.

+4

Votre utilisation de 'clock_gettime' est correcte [et préféré à 'gettimeofday']. Votre problème est que 'popen' renvoie [presque] immédiatement. Il n'attend pas que le processus cible se termine. Essayez: 'gettime (&tic); popen(); ...; pclose(); gettime (&toc);' –

+0

@CraigEstey Merci! Si je veux utiliser le fichier que j'ai ouvert, cela nécessite que je ré-ouvre (exécuter dans ce cas – Micrified

+1

Notez que macOS Sierra (10.12.x) supporte en fait 'clock_gettime()' maintenant, mais si vous ne le faites pas, vous pouvez l'utiliser sans le temps de réouverture. vous compilez du code sur Sierra pour l'exécuter sur des systèmes plus anciens, vous ne pouvez pas l'utiliser encore (et il n'est pas disponible si vous compilez sur des versions antérieures de Mac OS X donc le transfert vers Sierra ne peut pas (facilement) l'utiliser –

Répondre

3

popen() seulement une fourche et ouvrir un tuyau. Votre test affiche uniquement le temps qui prend popen() pour créer l'enfant et le tuyau.

Une façon simple de résoudre votre problème est d'obtenir le temps après votre pclose(), notez que ne sera pas parfait parce que quand vous lisez le retour de données par votre enfant, il pourrait se terminer avant votre appel à pclose()

plus votre solution pour obtenir le résultat est cassé, vous ne faites que la différence entre nanoseconde, j'ai trouvé une solution sur git:

void timespec_diff(struct timespec *start, struct timespec *stop, 
        struct timespec *result) 
{ 
    if ((stop->tv_nsec - start->tv_nsec) < 0) { 
     result->tv_sec = stop->tv_sec - start->tv_sec - 1; 
     result->tv_nsec = stop->tv_nsec - start->tv_nsec + 1000000000; 
    } else { 
     result->tv_sec = stop->tv_sec - start->tv_sec; 
     result->tv_nsec = stop->tv_nsec - start->tv_nsec; 
    } 

    return; 
} 

la dernière chose est que CLOCK_REALTIME doit être utilisé si vous voulez la date. Ici vous voulez juste une durée. Donc, vous devriez utiliser CLOCK_MONOTONIC s'il est disponible sur votre système, car CLOCK_REALTIME peut restaurer. (REALTIME_CLOCK de host_get_clock_service() coutures monotones aussi).

CLOCK_MONOTONIC: Horloge qui ne peut pas être réglée et représente le temps monotone depuis un point de départ non spécifié.

REALTIME_CLOCK: Service d'horloge à résolution modérée qui suit (généralement) le temps écoulé depuis le dernier démarrage du système. Ainsi, le code de travail

pourrait ressembler à ça:

int main (void) { 
    FILE *fp; 
    struct timespec tic, toc; 
    char *executableName = "./a.out"; 
    char answer[MAXBUF]; 

    gettime(&tic); 
    if ((fp = popen(executableName, "r")) == NULL) { 
     fprintf(stderr, "The file couldn't be opened.\n"); 
     return 1; 
    } 

    fgets(answer, MAXBUF, fp); 

    pclose(fp); 
    gettime(&toc); 
    struct timespec result; 
    timespec_diff(&tic, &toc, &result); 
    fprintf(stdout, "The program says %s, and took %lld.%.9lds\n", answer, (long long)result.tv_sec, result.tv_nsec); 

    return 0; 
} 

Crédit: