views:

138

answers:

2

Basically I set up a test to see which method is the fastest way to get data from another computer my network for a server with only a few clients(10 at max, 1 at min).

I tried two methods, both were done in a thread/per client fashion, and looped the read 10000 times. I timed the loop from the creation of the threads to the joining of the threads right after. In my threads I used these two methods, both used standard read(2)/write(2) calls and SOCK_STREAM/AF_INET:

In one I polled for data in my client reading(non blocking) whenever data was available, and in my server, I instantly sent data whenever I got a connection. My thread returned on a read of the correct number of bytes(which happened every time).

In the other, my client sent a message to the sever on connect and my server sent a message to my client on a read(both sides blocked here to make this more turn-based and synchronous). My thread returned after my client read.

I was pretty sure polling would be faster. I made a histogram of times to complete threads, and, as expected, polling was faster by a slight margin, but two things were not expected about the read/write method. Firstly, the read/write method gave me two distinct time spikes. I.E. some event sometimes occurred which would slow the read/write down by about .01 microseconds. I ran this test on a switch initially, and thought this may be a collision of packets, but then I ran the server and client on the same computer and still got these two different time spikes. Anyone know what event may be occurring?

The other, my read function returned too many bytes sometimes, and some bytes were garbage. I know streams don't guarantee you'll get all the data correctly, but why would the read function return extra garbage bytes?

A: 

I know streams don't guarantee you'll get all the data correctly, but why would the read function return extra garbage bytes?

Actually, streams do guarantee you will get all the data correctly, and in order. Datagrams (UDP) are what you were thinking of, SOCK_DGRAM, which is not what you are using. Within AF_INET, SOCK_STREAM means TCP and TCP means reliable.

Heath Hunnicutt
They guarantee you'll get them at your endpoint, but not that you will read all the bytes correctly (you could miss some from the beginning/end)
Bobby Hashemi
A: 

Seems you are confusing the purpose of these two alternatives:

  • Connection per thread approach does not need polling (unless your protocol allows for random sequence of messages either way, which would be very confusing to implement). Blocking reads and writes will always be faster here since you skip one extra system call to select(2)/poll(2)/epoll(4).
  • Polling approach allows to multiplex I/O on many sockets/files in single-threaded or fixed-number-of-threads setup. This is how web-servers like nginx handle thousands of client connections in very few threads. The idea is that wait on any given file descriptor does not block others - wait on all of them.

So I would say you are comparing apples and goblins :) Take a look here:

As for the spikes - check if TCP gets into re-transmission mode, i.e. one of the sides is not reading fast enough to drain receive buffers, play with SO_RCVBUF and SO_SNDBUF socket options.

Too many bytes is definitely wrong - looks like API misuse - check if you are comparing signed and unsigned numbers, compile with high warning level.

Edit:

Looks like you are between two separate issues - data corruption and data transfer performance. I would strongly recommend focusing on the first one before tackling the second. Reduce the test to a minimum and try to figure out what you are doing wrong with the sockets. i.e. where's that garbage data comes from. Do you check return values of the read(2) and write(2) calls? Do you share buffers between threads? Paste the reduced code sample into the question (or provide a link to it) if really stuck.

Hope this helps.

Nikolai N Fetissov
So a poll is going to be just as slow as a read or write call here? I am pretty new at this stuff, and pretty much just had some old code dropped on me that was done with a thread/per client and a poll after. I assumed that for some reason poll didn't take as long as a blocking read. So in this scenario, you think it would be best to hop in a thread and do a blocking read while having the server do a write on an incoming connection? Is there any fear that my data will get to me out of sync? I don't really know how big/long lasting of a buffer you get for incoming data through Ethernet.
Bobby Hashemi
@Bobby, you are working against TCP, not ethernet. Read some intro into sockets API and TCP/IP stack in general (I recommend Stevens UnP book: http://kohala.com/start/unpv22e/unpv22e.html). I'm adding some more to the answer.
Nikolai N Fetissov
looks like it was the buffer-length. I had thought that, but I didn't know you could set up a larger buffer. I have ran into that book a few times, I think I am going to pick it up from my universities library, thanks for the help.
Bobby Hashemi
There's a third edition actually that also covers SCTP and IPv6 - see if that interests you.
Nikolai N Fetissov