Question

In my small little file transfer website (this one, running .NET 4.5.1) I'm following the Microsoft Knowledge Base article 812406 to send previously uploaded files from the server to the browser.

Doing performance optimization I was suprised to find that the line

var buffer = new byte[10000];

takes a considerable percentage of time (I'm using Red Gate's ANTS Performance Profiler). The buffer is only allocated once per full download/client.

My questions:

  • Is it good practice to allocate a buffer this way and this size?
  • Any alternatives on allocating a ≈10k buffer?

Update 1:

Thanks to your comments I've seen that the memory is allocated inside the loop, too.

Still, ANTS Profiler only marks that allocation outside of the loop to take that much time, which I honestly do not understand (yet). I have removed the (meaningless) allocation inside the loop.

Update 2:

Having implemented the suggested BufferManager and also reduced the buffer size from 10k down to 4096 (just in case...), my website runs very smooth since days.

Was it helpful?

Solution

Yes. Actually, WCF uses a "buffer manager" to prevent this problem.

I have been myself developing a network service, and during profiling I found that the allocation of Byte[] buffers was creating a bottleneck. Not only during the allocation, but also the time the processor wasted in the GC was very high. Improvements to reuse those buffers and avoid allocations yield very big performance improvements.

You can use the BufferManager class to avoid writing your own buffer management strategy.

OTHER TIPS

Creating objects is usually very fast in .NET, but since this array object has a large size, clearing all its bytes takes a long time. C# always sets all bytes to 0 thus setting all of the fields to their default values when creating objects. (The constructors and field initializers can of course assign different values in classes and structs.)

In the example given by Microsoft the buffer is allocated before the loop and never changes its size. In addition, writing to the output stream writes only the required bytes.

// Gets the exact number of bytes read
length = iStream.Read(buffer, 0, 10000);

// Writes only 'length' bytes to the output
Response.OutputStream.Write(buffer, 0, length);

Therefore there is no need to "clear" the buffer by assigning a new one at each loop iteration. Dirty extra bytes won't hurt.

Solution: Drop the line buffer= new Byte[10000]; inside the while-loop!

Calling a constructor like that, especially with such a large buffer, is definitely going to take a lot of CPU time. Of course, the answers for your questions are going to be opinion based, but here's mine:

  1. There is nothing wrong with allocating a buffer of that size. 10K isn't that much memory on modern systems. Of course doing so in any kind of loop is going to eat up CPU time very quickly.

  2. If you can, try and avoid reconstructing the buffer for each use. Using a previously defined buffer avoids having to reallocate the memory every time you need it. Of course if this is threaded (multiple connections), each connection/thread would need its own buffer, but at least thats one allocation per connection, not a new buffer for every "chunk" of streamed data as in the linked example.

If you have file transfer tasks I suggest to use Microsoft.IO.RecyclableMemoryStream.

It has RecyclableMemoryStreamManager type, which can create RecyclableMemoryStream (which reuses bytes internally).

Pros:

  • It re-use byte arrays internally. E.g. if you need temporary memory stream, these classes will just get it for you, without additional cleanup.
  • It divides internal arrays by chunks.

Cons:

  • You should dispose each stream (otherwise your application will use a lot of memory)
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top