Page 1 of 1

slow IP stack on Raspian?

Posted: Mon Jul 23, 2012 11:09 am
by Nigel Day
I have a little test app, part of which sets up a UDP port and then pings small messages back and forth to get an idea of the IP stack performance. On the Debian squeeze release, I was getting over 15,000 round trips per second, but on Raspian this drops down to just over 9,000. Anyone got any ideas why this may be so, please?

Re: slow IP stack on Raspian?

Posted: Mon Jul 23, 2012 11:35 am
by dom
Can you try switching to kernel_cutdown.img ?

You can add:
kernel=kernel_cutdown.img
to config.txt.

Squeeze used to use the more cutdown kernel, and Wheezy uses the more debug one.

Re: slow IP stack on Raspian?

Posted: Mon Jul 23, 2012 11:55 am
by Nigel Day
That made a huge difference, thanks! using the reduced kernel the performance is now back up to almost where it was on Debian squeeze: 14143 rather than 15802 round trips per second. (I had hoped Raspian would be at least as fast, though!)

Re: slow IP stack on Raspian?

Posted: Mon Jul 23, 2012 12:35 pm
by asb
Perhaps you could share your simple test app? Maybe someone is interested in seeing if there's a particular kernel option which results in this performance degradation.

Re: slow IP stack on Raspian?

Posted: Mon Jul 23, 2012 1:31 pm
by Nigel Day
sure! here it is:

Code: Select all

// -----------------------------------------------------------------------------------
// bounce time tester
// 
// a simple little test program that sees how fast a short message can be sent from 
// one hand to another and back again, via UDP sockets. The aim is to get an idea 
// of the efficiency of the IP stack on a machine.
// 
// the code has been extracted from a database performance test suite, and in its
// current incarnation has only been tested on Linux (on a raspberry pi)
// --------------------------------------------------------------------------------

// --------------------
// pick up some headers
// --------------------

#if defined(WIN32)
    #include <winsock.h>
    #include <windows.h>
    #undef errno
    #define errno    WSAGetLastError()
    #define close(s) closesocket(s)
    #pragma comment(lib, "winmm.lib")
    #pragma comment(lib, "wsock32.lib")
    #pragma comment(lib, "advapi32.lib")
#else
    #undef INET_SKIP_PROTOTYPES
    #include <unistd.h>
    #include <errno.h>
    #include <string.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #if !defined(OSE)
        #include <netinet/in.h>
        #include <arpa/inet.h>
    #endif
    #include <netdb.h>
#endif

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

// ---------------
// some clock code
// ---------------

#include <time.h>

// on certain platforms, the CLOCKS_PER_SEC might not be set right, so
// define TICKTIME = CLOCKS_PER_SEC here, so that it can be redefined in
// platform-specific fashion where appropriate.

#define TICKTIME CLOCKS_PER_SEC

#if defined(WIN32)
    #define USE_CLOCK YES
    #if defined(WINCE)
        #define clock() GetTickCount()
    #endif
#elif defined(OSE)
    #define USE_CLOCK YES
#endif

/* define the_tick.
*/

#if defined(USE_CLOCK)

// the clock function is supposed to return the time
// taken by the current process, but on some systems
// just measures the time since the process (or
// machine) started - which is actually just what we
// we want, as we need to include the time taken in
// the database and in comms delays.

double the_tick ()
{
    return(double) (clock ()) / TICKTIME;
}

#else

#if defined(INTEGRITY)
extern "C"
{
#endif
#include <sys/times.h>
#if defined(INTEGRITY)
}
#endif

#ifndef CLK_TCK
int CLK_TCK = 0;
#endif

double the_tick ()
{
    tms tmsbuff;
    #ifndef CLK_TCK
    if (CLK_TCK == 0) CLK_TCK = sysconf(_SC_CLK_TCK);
    #endif
    return (double) (times (&tmsbuff)) / CLK_TCK;
}
#endif


/* how many seconds did an operation take?
   add in a guard against -ve zeros!
*/
double HowLong (double start)
{
    double timetaken = the_tick () - start;
    return (timetaken>0.0) ? timetaken : 0.0;
    //if (timetaken >= 0.0) return timetaken;
    //if (Verbose) printf ("\t\t(anomalous time difference noted: %lf)\n", timetaken);
    //return 0.0;
}


// -------
// Init_IO
// -------


#if defined(WIN32)

// on Windows, we have to call some extra initialisation to set the scene
// for the UDP-based 'bounce' test.

  int Init_IO ()
  {
      WSAData data;
      int res;

      if ((res = ::WSAStartup(MAKEWORD(2, 2), &data)) != 0)
          {
          printf("WARNING: failed to startup Winsock %d\n", res);
      }
      return res;
  }

  void End_IO ()
  {
    ::WSACleanup();

  }
#else
  int Init_IO ()
  {
      return 0;
  }

  void End_IO ()
  {
  }
#endif


// -----------------------------
// main body of the bounce test.
// -----------------------------


const char test_message[] = "this is the message to be pinged";
const int  buffer_length  = 100; // must be >= strlen(test_message)


int udp_send (int fd)
//
// send the standard message over the indicated socket
//
{
    int res;
    res = send (fd, test_message, strlen(test_message), 0);
    if (res < 0)
       printf ("send() returned %d.\n", res);
    return res;
}


int udp_receive (int fd)
//
// read the standard message from the indicated socket
{
    int len = 0;
    char b[buffer_length];

    if (recvfrom (fd, b, strlen(test_message), 0, NULL, 0) == strlen(test_message))
       return 0;
    printf ("Failed to receive valid message %d %d\n", errno, len);
    return -1;
}


double udp_pings (int s1, int s2, int ct)
//
// ping back and forth between two sockets
//
{
    //fprintf (stderr, "\t\tudp_pings (s1, s2, %d) called.\n", ct);

    int     i, res;
    double  start = the_tick ();

    for (i=0; i<ct; i++)
    {
        res = udp_send (s2);
        if (res < 0) return res;
        res = udp_receive (s1);
        if (res < 0) return -1;
        //printf(".");

        // now again, to emulate the round trip.
        res = udp_send (s2);
        if (res < 0) return res;
        res = udp_receive (s1);
        if (res < 0) return -1;
        //printf(":");
    }
    return HowLong (start);
}


int udp_bounce_test (int portno)
{
    int result = -1;

    // first set up the 'public' socket, known port,
    // to which we shall send messages.

    struct hostent *hp;
    if ((hp = gethostbyname("127.0.0.1")) == NULL)
    {
        fprintf(stderr, "cannot get local host info\n");
        return result;
    }
    //printf ("\tlocalhost found.\n");

    int s1 = socket (hp->h_addrtype, SOCK_DGRAM, 0);
    if (s1 < 0)
    {
        printf ("Failed to create 1st socket %d %d\n", s1, errno);
        return result;

    }
    //printf ("\t\t1st socket created.\n");

    sockaddr_in addr1;
    memset (&addr1, 0, sizeof (addr1));
    addr1.sin_family      = hp->h_addrtype;
    addr1.sin_addr.s_addr = INADDR_ANY;
    addr1.sin_port        = htons (portno);
    if (bind (s1, (sockaddr *)&addr1, sizeof (addr1)) != 0)
    {
        printf ("Failed to bind 1st socket, error code %d\n", errno);
    }
    else
    {
        // first socket set up OK, and bound.
        // now lets try setting up the sending socket...

        int s2 = socket (AF_INET, SOCK_DGRAM, 0);
        if (s2 < 0)
        {
            printf ("Failed to create 2nd socket %d %d\n", s2, errno);
        }
        else
        {
            sockaddr_in addr2;
            memset (&addr2, 0, sizeof (addr2));
            addr2.sin_family      = AF_INET;
            addr2.sin_addr.s_addr = inet_addr("127.0.0.1");
            addr2.sin_port        = htons(portno);
            if (connect(s2, (sockaddr *)&addr2, sizeof (addr2)) != 0)
            {
                printf ("Failed to connect 2nd socket, error code %d\n",
                        errno);
            }
            else
            {
                //printf ("\t\tready to ping...\n");

                // time how long it takes to do 64, 128, 256, ... pings,
                // doubling each time until we get a test run that takes
                // over a second.

                int    i       = 32;
                double pingres = 0.5;
                do
                {
                    i *= 2;
                    pingres = udp_pings (s1, s2, i);
                    //printf ("\n\t\tudp bounce %d %g\n", i, pingres);
                }   while (pingres >= 0 && pingres < 1);

                // calculate round trips per second.
                result = (int)( (double)i / (pingres) );
            }
            close (s2);
        }
    }
    close (s1);
    return result;
}


// --------------
// define main ()
// --------------


int main ()
{
    Init_IO ();
    int bres = udp_bounce_test (8001);
    if (bres < 0)
    {
        printf("\t\tfailed to determine UDP bounce rate.\n");
    }
    else
    {
        printf ( "using UDP, sent %d round-trip messages per second.\n",
                 bres);
    }
    return 0;
}


// -----------------------
//   e n d   o f   f i l e 
// -----------------------
just copy it to your pi - as, say, bouncetest.cpp, and then issue the commands

make bouncetest
./bouncetest