views:

1215

answers:

10

I assume 100 bytes is too small and can slow down larger file transfers with all of the writes, but something like 1MB seems like it may be too much. Does anyone have any suggestions for an optimal chunk of bytes per write for sending data over a network?

To elaborate a bit more, I'm implementing something that sends data over a network connection and show the progress of that data being sent. I've noticed if I send large files at about 100 bytes each write, it is extremely slow but the progress bar works out very nicely. However, if I send at say 1M per write, it is much faster, but the progress bar doesn't work as nicely due to the larger chunks being sent.

+6  A: 

If you can, just let the IP stack handle it; most OSes have a lot of optimization already built in. Vista, for example, will dynamically alter various parameters to maximize throughput; second-guessing the algorithm is unlikely to be beneficial.

This is especially true in higher-order languages, far from the actual wire, like C#; there are enough layers between you and actual TCP/IP packets that I would expect your code to have relatively little impact on throughput.

At worst, test various message sizes in various situations for yourself; few solutions are one-size-fits-all.

technophile
+5  A: 

No, there is no universal optimal byte size.

TCP packets are subject to fragmentation, and while it would be nice to assume that everything from here to your destination is true ethernet with huge packet sizes, the reality is even if you can get the packet sizes of all the individual networks one of your packets takes, each packet you send out may take a different path through the internet.

It's not a problem you can "solve" and there's no universal ideal size.

Feed the data to the OS and TCP/IP stack as quickly as you can, and it'll dynamically adapt the packet size to the network connections (you should see the code they use for this optimization - it's really, really interesting. At least on the better stacks.)

If you control all the networks and stacks being used and inbetween your clients/servers, though, then you can do some hand tuning. But generally even then you'd have to have a really good grasp of the network and the data your sending before I'd suggest you approach it.

Adam Davis
+2  A: 

If you are using TCP/IP over Ethernet, the maximum packet size is about 1500 bytes. If you try to send more than that at once, the data will be split up into multiple packets before being sent out on the wire. If the data in your application is already packetized, then you might want to choose a packet size of just under 1500 so that when you send a full packet, the underlying stack doesn't have to break it up. For example, if every send you do is 1600 bytes, the TCP stack will have to send out two packets for each send, with the second packet being mostly empty. This is rather inefficient.

Having said that, I don't know much of a visible impact on performance this will have.

Graeme Perrow
The maximum ETHERNET packet size is around 1500 bytes. The maximum TCP/IP packet size is around 65k bytes, though that is, except under special circumstances, always fragmented into smaller packets.
Adam Davis
Thanks for the clarification.
Graeme Perrow
Many ethernet ports (especially 1Gb) have an MTU greater than 1500.
Joe Koberg
+2  A: 

The one thing I will add is that for a given ethernet connection it takes about as long to send a small packet as a large one. As other's have said: if you're just sending a stream of data let the system handle it. But if you're worried about individual short messages back and forth, a typical ethernet packet is about 1500 bytes- as long as you keep it under that you should be good.

Joel Coehoorn
There was an article in Network Computing from the late 90's where they investigated a performance issue in an app, and found that the app was sending 1 byte per packet. They graphed the overhead penalty. I cannot find the article on their web site, probably too old. Classic story though!
geoffc
+1  A: 

You'd need to use Path MTU Discovery, or use a good default value (ie less than 1500 bytes).

Greg Rogers
+1  A: 

Make a function named CalcChunkSize Add some private variables to your class:

Private PreferredTransferDuration As Integer = 1800 ' milliseconds, the timespan the class will attempt to achieve for each chunk, to give responsive feedback on the progress bar.
Private ChunkSizeSampleInterval As Integer = 15    ' interval to update the chunk size, used in conjunction with AutoSetChunkSize. 
Private ChunkSize As Integer = 16 * 1024           ' 16k by default  
Private StartTime As DateTime
Private MaxRequestLength As Long = 4096            ' default, this is updated so that the transfer class knows how much the server will accept

Before every download of a chunk, check if its time to calculate new chunksize using the ChunkSizeSampleInterval

            Dim currentIntervalMod As Integer = numIterations Mod Me.ChunkSizeSampleInterval
            If currentIntervalMod = 0 Then
                Me.StartTime = DateTime.Now
            ElseIf currentIntervalMod = 1 Then
                Me.CalcChunkSize()
            End If

numIterations is set to 0 outside the download-loop and after every downloaded chunk set to numIterations += 1

Have the CalcChunkSize doing this:

Protected Sub CalcAndSetChunkSize()
    ' chunk size calculation is defined as follows 
    ' * in the examples below, the preferred transfer time is 1500ms, taking one sample. 
    ' * 
    ' * Example 1 Example 2 
    ' * Initial size = 16384 bytes (16k) 16384 
    ' * Transfer time for 1 chunk = 800ms 2000 ms 
    ' * Average throughput / ms = 16384b / 800ms = 20.48 b/ms 16384 / 2000 = 8.192 b/ms 
    ' * How many bytes in 1500ms? = 20.48 * 1500 = 30720 bytes 8.192 * 1500 = 12228 bytes 
    ' * New chunksize = 30720 bytes (speed up) 12228 bytes (slow down from original chunk size) 
    ' 

    Dim transferTime As Double = DateTime.Now.Subtract(Me.StartTime).TotalMilliseconds
    Dim averageBytesPerMilliSec As Double = Me.ChunkSize / transferTime
    Dim preferredChunkSize As Double = averageBytesPerMilliSec * Me.PreferredTransferDuration
    Me.ChunkSize = CInt(Math.Min(Me.MaxRequestLength, Math.Max(4 * 1024, preferredChunkSize)))
    ' set the chunk size so that it takes 1500ms per chunk (estimate), not less than 4Kb and not greater than 4mb // (note 4096Kb sometimes causes problems, probably due to the IIS max request size limit, choosing a slightly smaller max size of 4 million bytes seems to work nicely) 
End Sub

Then just use the ChunkSize when requesting next chunk.

I found this in the "Sending files in chunks with MTOM web services and .Net 2.0" by Tim_mackey and have found it very useful myself to dynamically calculate most effective chunksize.

The source code in whole are here: http://www.codeproject.com/KB/XML/MTOMWebServices.aspx

And author here: http://www.codeproject.com/script/Membership/Profiles.aspx?mid=321767

Stefan
A: 

One empirical test you can do, if you haven't already, is of course to use a sniffer (tcpdump, Wireshark etc.) and look at what packet sizes are achieved when using other software for up/downloading. That might give you a hint.

Niklas
A: 

Here is the formula you need:

int optimalChunkSize = totalDataSize / progressBar1.Width;

Using this, each chunk you send will increment the progress bar by 1 pixel. A smaller chunk size than this is pointless, in terms of user feedback.

MusiGenesis
A: 

IIRC 4k is the usual size modern packets are sent. I know the (homebrew) wii handles packets 4k at a time. You did not mention if you were using TCP or UDP. TCP will break and merge things up so you dont really need to care. If your using UDP 4k would be a good size and you should have some sort of redundancy in the protocol.

acidzombie24
A: 

I believe your problem is that you use blocking sockets and not non-blocking ones.

When you use blocking sockets and you send 1M of data the network stack can wait for all of the data to be placed in a buffer, if the buffers are full you'll be blocked and your progress bar will wait for the whole 1M to be accepted into the buffers, this may take a while and your progress bar will be jumpy.

If however you use non-blocking sockets, whatever buffer size you use will not block, and you will need to do the waiting yourself with select/poll/epoll/whatever-works-on-your-platform (select is the most portable though). In this way your progress bar will be updated fast and reflect the most accurate information.

Do note that at the receiver the progress bar is partially broken any way since the kernel will buffer some data and you will reach 100% before the other side really received the data. The only way around this is if your protocol includes a reply on the amount of data received by the receiver.

As others said, second guessing the OS and the network is mostly futile, if you keep using blocking sockets pick a size that is large enough to include more data than a single packet so that you don't send too little data in a packet as this will reduce your throughput needlessly. I'd go with something like 4K to include at least two packets at a time.

Baruch Even