0

I have a setup where I am sending data (1 byte at a time) to a Raspberry Pi from a Windows laptop via serial or Ethernet connection. The Pi then sends the data back to the laptop via serial or Ethernet, and I log the transmission for the round trip time. The Raspberry Pi server is coded in C++.

Right now I am trying to measure the execution time of receiving and sending on the Raspberry Pi side. However, I am noticing that the same function (receiving on the serial port) has different execution times depending on whether UDP or TCP is used to send back the data. This doesn't make sense to me, I would think that receiving on the serial port will be the same regardless.

I can post the actual code if necessary. Can anyone help explain what's going on here? Is it the inaccuracy of the timer? I am using the chrono library for millisecond timing.

Here is the code:

void send_client_udp( char *s, int mysock, int myport){
int i;
for(i=0;i<BACKLOG;i++){

        if(htons(udparray[i].sin_port) == myport){
            sendto(mysock,s,1,0,(struct sockaddr*)&udparray[i], sizeof udparray[i]);
        }

}
}

 void send_message_all_udp(char *s, int mysock){
int k;
for(k=0;k<BACKLOG;k++){

        sendto(mysock,s,1,0,(struct sockaddr*)&udparray[k], sizeof udparray[k]);

}
}
 void send_client_tcp( char *s, int mysock){
int i;
for(i=0;i<BACKLOG;i++){
    if(tcparray[i]){
        if(tcparray[i] == mysock){
            send(tcparray[i], s, 1,0);
        }
    }
}
} 

/* Send message to all clients */
void send_message_all_tcp(char *s){
int k;
for(k=0;k<BACKLOG;k++){
    if(tcparray[k]){
        send(tcparray[k], s, 1,0);
    }
}
}

void *sr_es_udp(void *thissock)
{
  int mysock = *(int*)thissock;
 int counter = 1;
int ardint;
char ardchar;
char *mesg;
while (ardrun)

{
if (serialDataAvail (serfd) == -1)
{
    fprintf(stdout, "(Serial) No data able to be received: %s\n", strerror (errno));
    exit(EXIT_FAILURE);
} 
auto recv1 = chrono::high_resolution_clock::now();
ardint = serialGetchar(serfd) ;
auto recv2 = chrono::high_resolution_clock::now();
   if (ardint > 0)
{

 ardchar = char(ardint);
 mesg = &ardchar;

 send_message_all_udp(mesg, mysock); 
 auto sendcli = chrono::high_resolution_clock::now();
 std::chrono::duration<double,std::milli> elapscli = (sendcli - recv2);
 std::chrono::duration<double,std::milli> elapsrecv = (recv2 - recv1);
 outfile << counter << "\t" << elapsrecv.count() << "\t"<< elapscli.count() << "\t" << ardint << "\n";
 counter++;
}

}
cout << "exit" << endl;
exitard= true;
}


 void *er_es_udp(void *pnewsock)
{
int mysock = *(int*)pnewsock;
char client_msg[MAXLEN];
int read_size;

int clientint;
int myport;
 int counter = 1;  
 while(looprun){
  auto recv1 = chrono::high_resolution_clock::now();                      
  read_size = recvfrom(mysock, client_msg, 1, 0, (struct sockaddr*)&their_addr, &size);
  auto recv2 = chrono::high_resolution_clock::now();

 clientint = int(*client_msg);

 myport = htons(their_addr.sin_port);
if (clientint >0)
{



serialPuts (serfd, &(*client_msg)) ;
auto sendser = chrono::high_resolution_clock::now();

send_client_udp(client_msg,mysock,myport); 
auto sendcli = chrono::high_resolution_clock::now();
std::chrono::duration<double,std::milli> elapsrecv = recv2-recv1;
std::chrono::duration<double,std::milli> elapscli = sendcli-recv2;
std::chrono::duration<double,std::milli> elapsser = sendser-recv2;
//outfile << counter << "\t" << elapsrecv.count() << "\t" << elapsser.count() << "\t" << clientint << "\n";
outfile << counter << "\t" << elapsrecv.count() << "\t" << elapscli.count() << "\t" << elapsser.count() << "\t" << clientint << "\n";
counter++;
}


}
 cout << "exit " << endl;


}

/* TCP data handling */
void *sr_es_tcp(void *)
{

int counter = 1;
int ardint;
char ardchar;
char *mesg;
while (run)

{
if (serialDataAvail (serfd) == -1)
{
    fprintf(stdout, "(Serial) No data able to be received: %s\n", strerror (errno));
    exit(EXIT_FAILURE);
} 
 auto recv1 = chrono::high_resolution_clock::now();       
 ardint = serialGetchar (serfd) ;
 auto recv2 = chrono::high_resolution_clock::now();
 if (ardint > 0)
 {
    ardchar = char(ardint);
  mesg = &ardchar;


 send_message_all_tcp(mesg);
auto sendcli = chrono::high_resolution_clock::now();
std::chrono::duration<double,std::milli> elapscli = (sendcli - recv2);
 std::chrono::duration<double,std::milli> elapsrecv = (recv2 - recv1);
 outfile << counter << "\t" << elapsrecv.count() << "\t"<< elapscli.count() << "\t" << ardint << "\n";
 counter++;

}

}
cout << "exit" << endl;
exitard=true;
}

/* handle the connections from client */
void *er_es_tcp(void *pnewsock)
{
int mysock = *(int*)pnewsock;


 char client_msg[MAXLEN];

 int read_size;

 int clientint;

  int myport;
  int counter = 1;

  while(looprun){   

  read_size = recv(mysock, client_msg, 1, 0);

  auto recv2 = chrono::high_resolution_clock::now();
   clientint = int(*client_msg);


    if (clientint > 0)
   {
client_msg[read_size] = '\0';


 serialPuts(serfd, &(*client_msg)) ;
  auto sendser = chrono::high_resolution_clock::now();

 send_client_tcp(client_msg,mysock); //was send_message

 auto sendcli = chrono::high_resolution_clock::now();


 std::chrono::duration<double,std::milli> elapsrecv = recv2-recv1;
std::chrono::duration<double,std::milli> elapscli = sendcli-recv2;
std::chrono::duration<double,std::milli> elapsser = sendser-recv2;


 outfile << counter  << "\t" << elapscli.count() << "\t" << elapsser.count() << "\t" << clientint << "\n";
  counter++;
  }


  }
  cout << "exit " << endl;



}

For the sr_es functions, the serialGetChar execution time changes depending on whether send_message_all is UDP or TCP. serialGetChar is from WiringPi serial library. Additionally, the send_message_client_tcp in Eth_recv_eth_send() is different for the send_message_all_tcp in Ser_recv_eth_send() for TCP specifically when serialGetChar is receiving at 115200 bps compared to 9600 bps. Why is this the case? There is only one client, so send_message_client_tcp() and send_message_all_tcp() should be the same. Shouldn't sending tcp be consistent regardless of the serial port baud rate, and why would serial receiving time change because of the internet protocol?

Here are the avg stats and standard deviation for a couple different tests (10 trials of 1000 data points each):

Receive/Send via Ethernet

  1. UDP receive: 0.5 +/- 0.37 ms, UDP send: 0.30 +/- 0.17 ms
  2. TCP receive: 0.44 +/- 0.4 ms, TCP send: 0.39 +/- 0.15 ms

Receive Serially, send via Ethernet

  1. serial baud 9600 receive: 4.3 +/- 0.1 ms, UDP send: 0.25 +/- 0.11 ms
  2. serial baud 9600 receive: 3.9 +/- 0.1 ms, TCP send: 0.77 +/- 0.19 ms -> interesting point is that the first 30 or so points are at 0.35, after that it shoots up to ~0.75-1 ms
  3. serial baud 115200 receive: 0.3 +/- 0.09 ms, UDP send: 0.23 +/- 0.11 ms
  4. serial baud 115200 receive: 1 +/- 0.15 ms, TCP send: 0.34 +/- 0.1 ms
nt387
  • 53
  • 2
  • 9
  • TCP and UDP are very different protocols that have different processing requirements. – Some programmer dude Apr 12 '17 at 16:51
  • TCP has a significant overhead to ensure in-order delivery. UDP does not ensure in-order or even that a given packet will get delivered. More info - http://www.diffen.com/difference/TCP_vs_UDP – Michał Apr 12 '17 at 16:52
  • See this Q&A: [Difference between TCP and UDP](http://stackoverflow.com/q/5970383/335858) – Sergey Kalinichenko Apr 12 '17 at 16:54
  • I understand why the TCP and UDP would have different execution times, but I don't get why that would change the Serial receiving time? – nt387 Apr 12 '17 at 17:07
  • Recommend adding the test code you are using. – user4581301 Apr 12 '17 at 17:32
  • I've added my code in – nt387 Apr 12 '17 at 18:07
  • *"I am noticing that the same function ... has different execution times"* -- Share those numbers; is the "difference" 10% or 100%? Your questions are argumentative, and seem to be based on faulty premises. *"Shouldn't sending tcp be consistent regardless of the serial port baud rate"* -- Apparently you don't understand what the baud rate represents. – sawdust Apr 12 '17 at 22:12
  • @sawdust I added in stats for some of the tests. Still don't understand how changing the baud rate of the serial port would affect the transmission time for TCP, while UDP is unchanged. – nt387 Apr 13 '17 at 20:41
  • You're presenting numbers without units. Is #1 *"0.30"* seconds or msec? – sawdust Apr 14 '17 at 01:07
  • @sawdust It is ms. I have written the units. – nt387 Apr 14 '17 at 03:23
  • *"It is ms:* -- Then you're not measuring what you think you're measuring. At 9600 baud, a character requires a millisecond of time to be transmitted on the wire. For measurement #3 `serial baud 9600, UDP send: 0.25 +/- 0.11 ms`, a quarter millisecond is only the transmission time of a few bits, not a full byte, certainly not a complete message. IOW you don't understand what you are measuring. – sawdust Apr 14 '17 at 07:45
  • 1
    @sawdust Let me clarify that those times were only for the TCP or UDP sending portion of the code. I did not include the time for receiving the character, but I have added it in now. Those serial receiving times also seem to vary depending on the internet protocol which confuses me. – nt387 Apr 14 '17 at 13:05
  • I still don't understand what you're doing, and I suspect you're making flawed assumptions. It looks like you want to measure I/O time, but all you can measure are syscalls, which are not the same thing. I/O is performed asynchronously by the kernel and independent from user space. Note that UDP will send data when the datagram is issued. But TCP will build a packet to send socket data based on its criteria, so UDP will have less latency (i.e. UDP will transmit the data "as soon as possible", unlike TCP). – sawdust Apr 14 '17 at 20:12
  • 1
    @sawdust I have measured round trip times to send one byte from my laptop to the Raspberry Pi and back. Here I am trying to measure the intermediate steps for that process, within the Raspberry PI server. If it isn't possible to accurately measure the intermediate steps because of the asynchronous processes, then I guess I'll have to be satisfied with knowing the round trip times. – nt387 Apr 18 '17 at 01:41
  • Round-trip time is meaningful because you have identified the correct starting and ending points. But that's not the case for the half-leg "measurements". For measuring the "send" operation, the start point is easy to identify, but what is the proper end point? Is it the return from the syscall? Is the sycall blocking or non-blocking? Is it when the kernel initiates the output? Is it when the data is received on the other end? There are similar questions for the "receive" operation. One method to measure events at two computers is with a logic analyzer, and toggling GPIO pins. – sawdust Apr 19 '17 at 00:35

0 Answers0