Nigel Day
Posts: 22
Joined: Sat Jun 16, 2012 2:15 pm

slow IP stack on Raspian?

Mon Jul 23, 2012 11:09 am

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?

dom
Raspberry Pi Engineer & Forum Moderator
Raspberry Pi Engineer & Forum Moderator
Posts: 5331
Joined: Wed Aug 17, 2011 7:41 pm
Location: Cambridge

Re: slow IP stack on Raspian?

Mon Jul 23, 2012 11:35 am

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.

Nigel Day
Posts: 22
Joined: Sat Jun 16, 2012 2:15 pm

Re: slow IP stack on Raspian?

Mon Jul 23, 2012 11:55 am

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!)

asb
Forum Moderator
Forum Moderator
Posts: 853
Joined: Fri Sep 16, 2011 7:16 pm
Contact: Website

Re: slow IP stack on Raspian?

Mon Jul 23, 2012 12:35 pm

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.

Nigel Day
Posts: 22
Joined: Sat Jun 16, 2012 2:15 pm

Re: slow IP stack on Raspian?

Mon Jul 23, 2012 1:31 pm

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

Return to “Raspbian”