2

I´m trying to obtain the performance in a Client (PC i7 3.4 GHz Kubuntu 15.04) - Server (RaspberryPi 700 Mhz Raspbian Jessie) architecture with sockets, measuring the spent time sending and receiving with at least microseconds precision.

Concretely, I have a client (PC) that makes 100 times:

  • Send UDP frame to server (Ta)
  • Receive UDP frame from server (Tb)
  • Spent time is: T=Tb-Ta

and a server (RaspberryPi) that makes 100 times:

  • Receive UDP frame from client(T1)
  • Send UDP frame from client(T2)
  • Spent time is: T'=T2-T1

I´m measuring time before send and after receive in client and subtracting it the time taken in server to receive and send, so the total time would be: Tt=T-T' but sometimes I get negative measures... :S

I´m measuring with this macro, where in 'X' I placed my code (C:sendto()-recvfrom() or S:recvfrom()-sendto()):

float timef=0.0;
#define TIME_MEASURE(X)                                        \
{                                                              \
  struct timespec ts1, ts2;                                    \
  clock_gettime( CLOCK_REALTIME, &ts1 );                       \
  X;                                                           \
  clock_gettime( CLOCK_REALTIME, &ts2 );                       \
  timef=(float) ( 1.0*(1.0*ts2.tv_nsec - ts1.tv_nsec*1.0)*1e-9 \
    + 1.0*ts2.tv_sec - 1.0*ts1.tv_sec ) );                     \
}

//Client pseudo-code example

main(){
   TIME_MEASURE(
       while(i<100)
       {
           sendto(...);
           recvfrom(..);
       }
   );
}

I have to measure time in any case, so another solution to obtain performance is not valid for me.

Can anyone suggest what can I do to solve this problem?

Than you so much.

(V2. EDITED WITH 'int64_t' and 'double' VALUES)

Here is my full code.

It can be downloaded from:
V1 - https://www.dropbox.com/sh/gb4mhn3amkyqhc1/AAC6FxKCfDxXQYgzRsjArvXQa?dl=0

V2 - https://www.dropbox.com/sh/byipa75qruc71gj/AAD2ExMlnQSaUk_Pbc5xR2lZa?dl=0

server.c:

    /****************************************
 * Servidor UDP. Recibe como argumento
 * en linea de comandos el numero de puerto. El servidor
 * se ejecuta en bucle
 ***************************************/
#include <sys/types.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <pthread.h>
#define TAMANO 256

#include <sys/time.h>
#include <math.h>
#include <time.h>

void * ack();
void error_fatal(char *); /* Imprime un mensaje de error y sale del programa */




int sock, length, fromlen, n, i, j, port ;
struct sockaddr_in server;
struct sockaddr_in from;
char buffer[TAMANO];

float timeArr;
float timeVector[101];
double timed;

#define TIME_MEASURE(X)                                             \
{                                                                   \
    struct timespec ts1, ts2, diff;                                 \
    clock_gettime( CLOCK_REALTIME, &ts1 );                          \
    X;                                                              \
    clock_gettime( CLOCK_REALTIME, &ts2 );                          \
    timeArr= (float) ( 1.0*(1.0*ts2.tv_nsec - ts1.tv_nsec*1.0)*1e-9 \
     + 1.0*ts2.tv_sec - 1.0*ts1.tv_sec );                           \
     diff.tv_sec = ts2.tv_sec - ts1.tv_sec;                          \
     diff.tv_nsec = ts2.tv_nsec - ts1.tv_nsec;                       \
     if( diff.tv_nsec < 0 ) {                                        \
         diff.tv_sec--;                                              \
         diff.tv_nsec += 1000000000LL;                               \
         }                                                               \
         printf( "Elapsed: %u.%09lld secs\n", diff.tv_sec, diff.tv_nsec );\
         const int64_t NANO = 1000000000LL;                              \
         int64_t nsec = (int64_t)(ts2.tv_sec - ts1.tv_sec) * NANO;       \
         nsec += (int64_t)(ts2.tv_nsec - ts1.tv_nsec);                   \
         printf( "Elapsed nsec: %lld.%09lld secs\n", nsec / NANO, nsec % NANO );\
         timed = (double)nsec * 1e-9;                                    \
         printf("nsec=%ld\n");                                           \
         }\

int main(int argc, char *argv[])
{

    timeArr=0.1;
    i=0;
    pthread_t recv;

    FILE *f;




    if (argc < 2) 
    {
        fprintf(stderr, "ERROR, no se ha indicado el puerto \n");
        exit(1);
    }

    /* obtain port number */
    if (sscanf(argv[1], "%d", &port) <= 0)
    {
        fprintf(stderr, "%s: error: wrong parameter: port\n", argv[0]);
    return -2;
    }

    /* (1) creacion del socket del servidor*/
    sock=socket(AF_INET, SOCK_DGRAM, 0);
    if (sock < 0) 
        error_fatal("Creando el socket");

    length = sizeof(server);
    memset(&server,0,length); /*limpio la direccion del socket del servidor*/

    /* (2) vinculo la direccion IP y puerto local al socket creado anteriormente */
    server.sin_family=AF_INET;
    server.sin_addr.s_addr=INADDR_ANY;
    server.sin_port=htons(port);

    if (bind(sock,(struct sockaddr *)&server,length)<0)
        error_fatal("binding");

    fromlen = sizeof(struct sockaddr_in);

    /* (3) bucle principal. Pongo a recibir y responder mensajes a traves del socket*/
    printf("Servidor listo y esperando tramas UDP en el puerto %d...\n", port);



    while (1) 
    {
        //el cliente manda una trama de prueba para no contar el tiempo de espera en el servidor
        //client sends init frame and it is not counted
        if(i==0)
        {
             //Recibo paquete de prueba para empezar a contar, desecho este tiempo
            n = recvfrom(sock,buffer,TAMANO,0,(struct sockaddr *)&from,&fromlen);
            pthread_create (&recv, NULL,ack, &fromlen);
            i++;            

        }
        else
        {

            TIME_MEASURE(

                n = recvfrom(sock,buffer,TAMANO,0,(struct sockaddr *)&from,&fromlen);

                if (n < 0) 
                    error_fatal("recvfrom");
                    /*datagrama recibido*/

                pthread_create (&recv, NULL,ack, &fromlen);
            );


            timeVector[i]=(float)timeArr;

            printf("timevector[%d] = %f\n", i, timeArr);

            if(i==100)
            {
                f=fopen("dataserver.txt", "w+");
                printf("Se han recibido 100 tramas UDP\n\n\n");

                for(j=0;j<=100;j++)
                {

                    fprintf(f,"%f\n",timeVector[j]);
                    //printf("valores: %f\n",timeVector[j]);
                }
                i=0;
                fclose(f);
                memset(timeVector,0,100);
            }
            else
            {
                i++;   
            }
        }

    }
    return 0;
}

void *ack(void *ptr){

    int *fl;

    fl= (int *)ptr;
    buffer[n]='\0'; /* para poder imprimirlo con prinft*/
    //printf("Recibido en el servidor: %s\n", buffer);

    /*enviar respuesta*/
    float t;

    t=(float)timeArr;

    //It is like an ACK, now I am not using this value
    n = sendto(sock,&t,sizeof(float),0,(struct sockaddr *)&from,fromlen);

    memset(buffer,0,TAMANO); 
    if (n < 0) 
         error_fatal("sendto"); 

    pthread_exit(NULL);
}

void error_fatal(char *msg)
{
 perror(msg);
 exit(1);
}

client.c

/*******************************************************************
 * Cliente de eco remoto sobre UDP:
 *
 * cliente dir_ip_maquina puerto
 ********************************************************************/
#include <sys/types.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#define TAMANO 256 /* tamano del buffer de recepcion */

#include <sys/time.h>
#include <math.h>
#include <time.h>

void error_fatal(char *); /* Imprime un mensaje de error y sale del programa */

float timeArr;
float timeVector[101];
int i;
double timed;



#define TIME_MEASURE(X)                                             \
{                                                                   \
    struct timespec ts1, ts2, diff;                                 \
    clock_gettime( CLOCK_REALTIME, &ts1 );                          \
    X;                                                              \
    clock_gettime( CLOCK_REALTIME, &ts2 );                          \
    timeArr= (float) ( 1.0*(1.0*ts2.tv_nsec - ts1.tv_nsec*1.0)*1e-9 \
     + 1.0*ts2.tv_sec - 1.0*ts1.tv_sec );                           \
     diff.tv_sec = ts2.tv_sec - ts1.tv_sec;                          \
     diff.tv_nsec = ts2.tv_nsec - ts1.tv_nsec;                       \
     if( diff.tv_nsec < 0 ) {                                        \
         diff.tv_sec--;                                              \
         diff.tv_nsec += 1000000000LL;                               \
         }                                                               \
         printf( "Elapsed: %u.%09lld secs\n", diff.tv_sec, diff.tv_nsec );\
         const int64_t NANO = 1000000000LL;                              \
         int64_t nsec = (int64_t)(ts2.tv_sec - ts1.tv_sec) * NANO;       \
         nsec += (int64_t)(ts2.tv_nsec - ts1.tv_nsec);                   \
         printf( "Elapsed nsec: %lld.%09lld secs\n", nsec / NANO, nsec % NANO );\
         timed = (double)nsec * 1e-9;                                    \
         printf("nsec=%ld\n");                                           \
         }\



/*
 * 
 *    const int64_t NANO = 1000000000LL;                              \
    int64_t nsec = (int64_t)(ts2.tv_sec - ts1.tv_sec) * NANO;       \
    nsec += (int64_t)(ts2.tv_nsec - ts1.tv_nsec);                   \
    printf( "Elapsed: %lld.%09lld secs\n", nsec / NANO, nsec % NANO );\
                                                                    \

                                                                    */

int main(int argc, char *argv[])
{
    int sock; /* descriptor del socket del cliente */
    int length, n, j, port;
    struct sockaddr_in server, from; /* direcciones del socket del servidor y cliente */
    struct hostent *hp; /* estructura para el nombre del servidor (ver gethostbyname) */
    char buffer[TAMANO]; /* buffer de recepcion y envio del mensaje */
    char buffercpy[TAMANO];
    float bufferf[7];

    FILE *f = fopen("data.txt", "w+");
    struct timespec spec;
    clockid_t clk_id;

    clk_id=CLOCK_REALTIME;
    i=0;


    if (argc != 3) 
    { 
        fprintf(stderr,"Uso: ./client server port\n");
        exit(1);
    }

    /* obtain port number */
    if (sscanf(argv[2], "%d", &port) <= 0)
    {
    fprintf(stderr, "%s: error: wrong parameter: port\n", argv[0]);
    return -2;
    }
    printf("El cliente va a mandar por el puerto %d\n", port);

    /* (1) creacion del socket UDP del cliente */
    sock= socket(AF_INET, SOCK_DGRAM, 0); 
    if (sock < 0)   
        error_fatal("socket");    
    server.sin_family = AF_INET; /*dominio de Internet*/

    /* (2) averigua la direccion IP a partir del nombre del servidor*/
    hp = gethostbyname(argv[1]);
    if (hp==0) 
        error_fatal("Host desconocido");

    /* (3) copia la IP resuelta anteriormente en la direccion del socket del servidor */
    memcpy((char *)&server.sin_addr,(char *)hp->h_addr,hp->h_length);

   if ( clock_getres( clk_id, &spec) == -1 )
   {
      perror( "clock get resolution" );
      return EXIT_FAILURE;
    }

    //printf ("CLOCK_REALTIME: %ld s, %ld ns\n", spec.tv_sec,spec.tv_nsec); 

    /* (4) copia el puerto destino en la direccion del socket del servidor */
    server.sin_port = htons(port);
    length=sizeof(struct sockaddr_in);

    printf("Por favor, introduce el mensaje: ");
        memset(buffer,0,TAMANO); /*limpio el buffer*/
        fgets(buffer,TAMANO-1,stdin);
        strcpy(buffercpy, buffer);//Copio el mensaje a un nuevo buffer para que se repita en el while


    while(i<=100)
    {       
        //client sends init frame and it is not counted
        if(i==0)
        {

            /* (5) envia al socket del servidor el mensaje almacenado en el buffer*/
            n=sendto(sock,buffer,strlen(buffer),0,(struct sockaddr *) &server,length);
            if (n < 0) 
                error_fatal("Sendto");

            /* (6) lee del socket el mensaje de respuesta del servidor*/
            n = recvfrom(sock,bufferf,sizeof(float),0,(struct sockaddr *) &from, &length);

            if (n < 0) 
                error_fatal("recvfrom");
            i++;

        }
        else
        {




            TIME_MEASURE(
                memset(buffer, '\0', sizeof(buffer));
                strcpy(buffer, buffercpy);

                /* (5) envia al socket del servidor el mensaje almacenado en el buffer*/
                n=sendto(sock,buffer,strlen(buffer),0,(struct sockaddr *) &server,length);
                if (n < 0) 
                    error_fatal("Sendto");

                /* (6) lee del socket el mensaje de respuesta del servidor*/
                n = recvfrom(sock,bufferf,sizeof(float),0,(struct sockaddr *) &from, &length);

                if (n < 0) 
                    error_fatal("recvfrom");

            );

            timeVector[i]=(float)timeArr;
            printf("Tiempo de procesamiento cliente - timeVector [%d]: %f\n", i,timeArr);

            //timeVector[i]+=(float)(0.0 - *bufferf);
            //printf("timeVector negativo[%d]= %f\n", i, timeVector[i]);
            buffer[n]='\0'; /* para poder imprimirlo con printf*/
            bufferf[n]='\0'; /* para poder imprimirlo con printf*/
            //printf("Recibido en el cliente: %f ACK(%d)%c\n",*bufferf,i,'\0');


            printf("timeVector TOTAL= %f\n\n", timeVector[i]);
            //printf("buffer=%s%c\n",buffer,'\0');

            if(i==100){
                for(j=1;j<=100;j++){
                    fprintf(f,"%f\n",timeVector[j]);
                        //printf("valores: %f\n",timeVector[j]);
                }
                printf("Se han enviado 100 tramas UDP \n\n\n");

            }
            i++;
        }
    }

    fclose(f);
    /*cerramos el socket*/
    if(close(sock) < 0) 
        error_fatal("close");
}

void error_fatal(char *msg)
{
    perror(msg);
    exit(1);
}

I get warnings in number formats, so I think that it is probably that printed values wouldn't be correct.

These are warnings which I get:

server.c: In function ‘main’:
server.c:50:18: warning: format ‘%u’ expects argument of type ‘unsigned int’, but argument 2 has type ‘__time_t {aka long int}’ [-Wformat=]
          printf( "Elapsed: %u.%09lld secs\n", diff.tv_sec, diff.tv_nsec );\
                  ^
server.c:122:13: note: in expansion of macro ‘TIME_MEASURE’
             TIME_MEASURE(
             ^
server.c:50:18: warning: format ‘%lld’ expects argument of type ‘long long int’, but argument 3 has type ‘__syscall_slong_t {aka long int}’ [-Wformat=]
          printf( "Elapsed: %u.%09lld secs\n", diff.tv_sec, diff.tv_nsec );\
                  ^
server.c:122:13: note: in expansion of macro ‘TIME_MEASURE’
             TIME_MEASURE(
             ^
server.c:54:18: warning: format ‘%lld’ expects argument of type ‘long long int’, but argument 2 has type ‘long int’ [-Wformat=]
          printf( "Elapsed nsec: %lld.%09lld secs\n", nsec / NANO, nsec % NANO )
                  ^
server.c:122:13: note: in expansion of macro ‘TIME_MEASURE’
             TIME_MEASURE(
             ^
server.c:54:18: warning: format ‘%lld’ expects argument of type ‘long long int’, but argument 3 has type ‘long int’ [-Wformat=]
          printf( "Elapsed nsec: %lld.%09lld secs\n", nsec / NANO, nsec % NANO )
                  ^
server.c:122:13: note: in expansion of macro ‘TIME_MEASURE’
             TIME_MEASURE(
             ^
server.c:56:17: warning: format ‘%ld’ expects a matching ‘long int’ argument [-Wformat=]
          printf("nsec=%ld\n");                                           \
                 ^
server.c:122:13: note: in expansion of macro ‘TIME_MEASURE’
             TIME_MEASURE(
             ^
ampu
  • 21
  • 1
  • 4
  • I suggest using `double` and not `float`, seeing as the range of number is so great between seconds and nanoseconds. `timed = 1e-9 * (ts2.tv_nsec - ts1.tv_nsec) + ts2.tv_sec - ts1.tv_sec;` – Weather Vane Mar 23 '16 at 12:48

1 Answers1

0

You might be getting problems with converting these numbers to floating point. Try computing the difference into a suitably large integer datatype. At least to begin with, and then work back from there.

const int64_t NANO = 1000000000LL;
int64_t nsec = (int64_t)(ts2.tv_sec - ts1.tv_sec) * NANO;
nsec += (int64_t)(ts2.tv_nsec - ts1.tv_nsec);
printf( "Elapsed: %lld.%09lld secs\n", nsec / NANO, nsec % NANO );

If you wanted a float out of that, beware that single-precision is pretty limited. You may be able to clamp it to microseconds, but for longer intervals you might not get microsecond resolution.

timef = (float)(nsec / 1000LL) * 1e-6f;

Better to use double:

double timef;
timef = (double)nsec * 1e-9;

Of course, you could just use a timespec itself:

struct timespec diff;
diff.tv_sec = ts2.tv_sec - ts1.tv_sec;
diff.tv_nsec = ts2.tv_nsec - ts1.tv_nsec;
if( diff.tv_nsec < 0 ) {
    diff.tv_sec--;    
    diff.tv_nsec += 1000000000LL;
}
printf( "Elapsed: %u.%09lld secs\n", diff.tv_sec, diff.tv_nsec );
paddy
  • 60,864
  • 6
  • 61
  • 103
  • Yup, I tried the code, and the only time I get negative result, is if I erroneously print the float value as an int : `printf("%d", timef)` There is no need for floating point here, it just leads so such silly mistakes. – Gábor Buella Mar 23 '16 at 12:52
  • My complete code write measures into a files (dataserver.txt and data.txt) and then I process them with Matlab, but is easy to see in files without processing that there are measures in server file which are bigger than measures in client file, making negative the subtraction Tt= Tclient-Tserver. Maybe the error is writing to file... When I back to home I am going to paste the full code here. Thank you guys! – ampu Mar 23 '16 at 14:05
  • With this code I get bigger client measures so I never get negative values when subtract server measure, but I am not sure what does the format number in both printf, because I get warnings and I have never seen %u.%09lld format before – ampu Mar 23 '16 at 18:53
  • The printf format string is displaying two values: `%u` is an `unsigned int`, `%09lld` is a `long long`, displayed with a width of 9 characters zero-filled to the left. This is just a trick to show a number with a fixed-sized decimal part without using floating point values. It doesn't use machine locale, however, and so is always showing a period to separate the whole part fractional part. Don't get too caught up on that. What warnings do you get? – paddy Mar 23 '16 at 20:53
  • I have posted a message with time format warnings which I get – ampu Mar 28 '16 at 18:29
  • Then adjust the modifiers accordingly. The warning clearly tells you that on your system both of these types are aliased to `long int`. So use `%ld.%09ld`. Like I said, don't get caught up on my `printf` statement, as that wasn't the important part of my answer. – paddy Mar 28 '16 at 21:46
  • I have changed printf statements according to my data type (%ld.%09ld) and I get the same measures than my original code so it gives me negative measures again.... what is happening? Please help!! – ampu Mar 29 '16 at 19:09
  • Maybe [this is related](http://stackoverflow.com/q/3523442/1553090). Otherwise print out the actual values of the two `timespec` stamps and work out yourself what is happening. – paddy Mar 29 '16 at 21:53
  • @paddy I have tested with CLOCK_MONOTONIC like the related post, but I get the same results. This line: diff.tv_nsec += 1000000000LL; within condition if(diff.tv_nsec < 0) works with long int? – ampu Apr 01 '16 at 07:11