2

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!

Mike Two
  • 44,935
  • 9
  • 80
  • 96
  • I hope you realize that raw UDP is unreliable and thus unsuitable for file transmission by itself... – Cameron Feb 15 '12 at 20:12
  • Seems like a duplicate of http://stackoverflow.com/questions/47903/udp-vs-tcp-how-much-faster-is-it and UDP is not a useful file transfer method as UDP delivery is not guaranteed. – tawman Feb 15 '12 at 20:13
  • yes, i know that UDP does not gurantee that the send packets will receive its destintation. Altough the UDP based transmission is a requirement and so i have to implement it. My question is only why the transmission is slower than TCP? – Matthias Kohles Feb 15 '12 at 20:19
  • What's in between your two test machines? Are you directly wired with a cross-over cable? A switch/hub? The internet? – Marc B Feb 15 '12 at 20:52
  • You doing anything different when connecting by TCP (like using IPv6)? Also, is it running in a loopback or is each component running on a separate computer? – Tremmors Feb 15 '12 at 21:16
  • Each component is running on a separate computer and they are connected over fast ethernet with one switch (wrt54gl) between them. I'm using ipv4 for the transmission. – Matthias Kohles Feb 15 '12 at 22:01
  • I also figured that the sending of my bytes on the sender side took only 4 ms using TCP and about 1000 ms using UDP. So there is already quite a difference on the senders side. – Matthias Kohles Feb 16 '12 at 07:29
  • Did you ever work out what was going on? I am seeing similar results with small packets under different conditions to your example - http://stackoverflow.com/questions/18941606/real-time-performance-with-a-c-sharp-tcp-udp-server . It's possible that the UDP implementation in C# is slower than the TCP implementation??? – Remixed123 Sep 23 '13 at 02:20

2 Answers2

4

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.

Nikolai Fetissov
  • 82,306
  • 11
  • 110
  • 171
  • Well, i actually did that, since i thought that the IP fragmentation would cause this problem. So i splitted the whole byte array into parts of 1472 bytes. The weird thing is, that the transmission time did not change very much...it took about 7 seconds for the transmission. It took also about 7 second when i used 8192kb parts. – Matthias Kohles Feb 16 '12 at 06:20
  • 1
    @Mattias, where are you measuring the transmission time? On the sender or on the receiver? TCP sender buffers data in-kernel, so it might give you an impression of faster transfer, while in reality it's still pushing packets to the other side. – Nikolai Fetissov Feb 16 '12 at 14:54
  • 1
    Well, ok it seems my measuring was faulty...damn it. I ran some tests right now where i transferred a ~100mb file from one pc to another. TCP took about 17 seconds for the transmission. Then i transferred the file using UDP with different buffer sizes. UDP (1000byte buffer) took 22 seconds. UDP (1472bytes buffer) took 28seconds. UDP (8192byte buffer) needed 9seconds and UDP (65000byte buffer) also needed about 9 seconds. Does those values seem reasonable for you? – Matthias Kohles Feb 17 '12 at 08:26
  • I also just saw that the transmission of the 100mb file over tcp was received after ~9,3 seconds on the server, but the ACK messages were sent after the data was received on the server (observed it with wireshark). So the overall transmission process took ~19 seconds. Is this a normal behaviour? If true, then UDP would just be a few milliseconds faster?? – Matthias Kohles Feb 17 '12 at 08:50
0

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.

usr
  • 168,620
  • 35
  • 240
  • 369
  • Ok, thanks for this advice! I ran the profiler and got this result. [link](http://imageshack.us/photo/my-images/24/unbenanntymo.png/) So the method will be stuck to 99% in the receive method. Ok thats not necessarily a problem. Can i examine the runtimes per method in this profiler? – Matthias Kohles Feb 16 '12 at 07:09
  • ok, i have set the buffer size of the sending socket to 1000 and it took about 9000ms to send the 12 mb data. I also watched the packet flow in wireshark and each packet had a size of 1042bytes. The 42 bytes must be the ethernet, ip and udp headers. When i set the set the buffer size to 1472, it took about 7000-8000 ms. I really have no idea whats causing this... – Matthias Kohles Feb 16 '12 at 14:24
  • The last suggestion I have is to use ETW tracing to find out what is taking most time in the kernel. ETW tracing has many trce sources available. You can probably use some tool on the internet to profile the UDP stack and the Windows kernel. – usr Feb 16 '12 at 19:11