Matthias Kohles
Matthias Kohles

Reputation: 29

UDP data transmission slower than TCP

I'm currently writing a prototype application in C#/.Net4 where i need to transfer an unknown amount of data. The data is read in from a text file and then serialized into a byte array. Now i need to implement both transmission methods, UDP and TCP. The transmission in both ways does work fine but i have some struggleing with UDP. I assumend that the transmission using UDP have to be much faster than using TCP but in fact my tests proved that the UDP transmission is about 7 to 8 times slower than using TCP. I tested the transmission with a 12 megabyte file and the TCP transmission took about 1 second whereas the UDP transmission took about 7 seconds. In the application i use simple sockets to transmit the data. Since UDP does only allow a maximum of 65535kb per message, i splitted the serialized the byte array of the file into several parts where each part has the size of the socker SendBufferSize and then i transfer each part using Socket.Send() method call.

Here is the code for the Sender part.

while (startOffset < data.Length)
{
    if ((startOffset + payloadSize) > data.Length)
    {
        payloadSize = data.Length - startOffset;
    }
    byte[] subMessageBytes = new byte[payloadSize + 16];
    byte[] messagePrefix = new UdpMessagePrefix(data.Length, payloadSize, messageCount, messageId).ToByteArray();
    Buffer.BlockCopy(messagePrefix, 0, subMessageBytes, 0, 16);
    Buffer.BlockCopy(data, startOffset, subMessageBytes, messageOffset, payloadSize);
    messageId++;
    startOffset += payloadSize;
    udpClient.Send(subMessageBytes, subMessageBytes.Length);
    messages.Add(subMessageBytes);
}

This code does simply copy the next part to be send into an byte array and then call the send method on the socket. My first guess was, that the splitting/copying of the byte arrays was slowing down the performance, but i isolated and tested the splitting code and the splitting took only a few milliseconds, so that was not causing the problem.

int receivedMessageCount = 1;
Dictionary<int, byte[]> receivedMessages = new Dictionary<int, byte[]>();
while (receivedMessageCount != totalMessageCount)
{
    byte[] data = udpClient.Receive(ref remoteIpEndPoint);
    UdpMessagePrefix p = UdpMessagePrefix.FromByteArray(data);
    receivedMessages.Add(p.MessageId, data);
    //Console.WriteLine("Received packet: " + receivedMessageCount + " (ID: " + p.MessageId + ")");
    receivedMessageCount++;
    //Console.WriteLine("ReceivedMessageCount: " + receivedMessageCount);
}
Console.WriteLine("Done...");
return receivedMessages;

This is the server side code where i receive the UDP messages. Each message has some bytes as a prefix where the total number of messages is stored and the size. So i simply call socket.Receive in a loop until i received the amount of messages which were specified in the prefix.

My assumption here is that i may have implemented the UDP transmission code not "efficiently" enough... Maybe one of you guys already sees a problem in the code snippets or have any other suggestion or hint for me why my UDP transmission is slower than TCP.

thanks in advance!

Upvotes: 2

Views: 6659

Answers (2)

usr
usr

Reputation: 171178

I think you should measure CPU usage and network throughput for the duration of the test.

If the CPU is pegged, this is your problem: Turn on a profiler.

If the network (cable) is pegged this is a different class of problems. I wouldn't know what to do about it ;-)

If neither is pegged, run a profiler and see where most wall-clock time is spent. There must be some waiting going on.

If you don't have a profiler just hit break 10 times in the debugger and see where it stops most often.

Edit: My response to your measurement: We know that 99% of all execution time is spent in receiving data. But we don't know yet if the CPU is busy. Look into task-manager and look which process is busy.

My guess is it is the System process. This is the windows kernel and probably the UDP component of it.

This might have to do with packet fragmentation. IP packets have a certain maximum size like 1472 bytes. Your UDP packets are being fragmented and reassembled on the receiving machine. I am surprised that is taking so much CPU time.

Try sending packets of total size of 1000 and 1472 (try both!) and report the results.

Upvotes: 0

Nikolai Fetissov
Nikolai Fetissov

Reputation: 84151

While UDP datagram size can be up to 64K, the actual wire frames are usually 1500 bytes (normal ethernet MTU). That also has to fit an IP header of minimum 20 bytes and a UDP header of 8 bytes, leaving you with 1472 bytes of usable payload.

What you are seeing is the result of your OS network stack fragmenting the UDP datagrams on the sender side and then re-assembling them on the receiver side. That takes time, thus your results.

TCP, on the other hand, does its own packetization and tries to find path MTU, so it's more efficient in this case.

Limit your data chunks to 1472 bytes and measure again.

Upvotes: 4

Related Questions