Search code examples
csocketsrecvstrcat

Maximizing efficiency of char concatenation operation for socket recv in C


I'm trying to maximize efficiency when taking a buffer, which is full of data, received from a socket and copy it into a global char array xmlResponce[35000].

    char buf[2048];
    if (sendResponce != -1)
    {
        int bytes;
        memset(buf, '\0', 2048);
        while((bytes = recv(sockfd, buf, 2047, 0)) > 0)
        {
            buf[bytes] = '\0';
            strcat(xmlResponce, buf);
        }
    }

I'm especially concerned about minimizing memory fragmentation as I'm on a Linux Kernel 2.6.27 which has some issues in that department. Is there a more effect way to do this? Is using strcat the right choice for copying from the buffer into the master char array?


Solution

  • Firstly, I think you may be overthinking this. Kernel memory fragmentation (as indicated by your reference to the kernel) is fragmentation of physical RAM, meaning that the buddy algorithm can't allocate physically contiguous pages. However, as your memory is virtual memory, that doesn't matter, as virtually contiguous memory doesn't need to be physically contiguous. Virtual memory fragmentation is down to how you allocate the pages in user space (libc or whatever).

    If you are really concerned about virtual memory fragmentation, then allocate a large array using mmap( ... MAP_ANON ... ). Make it large than you are ever likely to need (it won't actually consume physical memory until you write to or read from each page), then mremap it to twice the size if you ever need to expand this. If you look at man mallopt (specifically at M_MMAP_THRESHOLD) you'll see that malloc() will do all the work for you if you make the original allocation large enough. So I'd suggest you just allocate a large buffer (35000 bytes if you like), then if you are going to overrun it, double the size.

    Now, how do you write the bytes into the buffer?

    Don't use strcat. Just write them straight into the buffer. No need to copy stuff at all. Here's some untested code.

    ssize_t offset = 0;
    
    ....
    
    if (sendResponce != -1)
    {
        ssize_t bytes;
        while((bytes = recv(sockfd, xmlResponse+offset, 65536, 0)) > 0)
        {
            offset += bytes;
        }
    }
    
    xmlResponse[offset] = 0;
    

    Note, for efficiency, I have increased the size of the recv to 65536 bytes. That would mean normally you'd get it in one call.

    Don't forget to check you don't write beyond the end of the buffer. I left that out for clarity.