I have a program where I send data over TCP link. I am using Asynchronous Reads and writes to both the disk and network. If I put a DeflateStream in the middle (so I compress before I write to the network link and I decompress when I receive the data and write it out to the disk) I am CPU bound on the compressing side. This causes my max transfer rate to be about 300 KB/s. However if I remove the compression step I am now I/O bound to the disk and I get transfer rates of 40,000 KB/s.
Under strictly LAN conditions my upper I/O limit will always be more than 300 KB/s, however if my program is run over the internet I very well may have a network IO limit below 300 KB/s.
I would like to detect if I am I/O bound and my network/disk link is the limiting factor or if I am CPU bound and the act of compressing is what is slowing me down most. How could I detect if my program is being limited by my CPU or by my I/O at runtime so I could switch protocols and get the best possible transfer rate?
private static void SendFile(string filename, NetworkStream stream, int sendBufferSize)
{
using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 4096, FileOptions.Asynchronous | FileOptions.SequentialScan))
using (var ds = new DeflateStream(stream, CompressionMode.Compress))
{
StreamUtilities.CopyAsync(fs, ds, sendBufferSize);
}
}
public static void CopyAsync(Stream sourceStream, Stream destStream, int bufferSize = 4096)
{
Byte[] bufferA = new Byte[bufferSize];
Byte[] bufferB = new Byte[bufferSize];
IAsyncResult writeResult = null;
IAsyncResult readResult = null;
bool readBufferA = false;
int read;
readResult = sourceStream.BeginRead(bufferA, 0, bufferA.Length, null, null);
//Complete last read
while ((read = sourceStream.EndRead(readResult)) > 0)
{
if (readBufferA)
{
PerformOperations(sourceStream, destStream, bufferA, bufferB, ref readResult, ref writeResult, read);
}
else
{
PerformOperations(sourceStream, destStream, bufferB, bufferA, ref readResult, ref writeResult, read);
}
//Flip the bit on the next buffer
readBufferA = !readBufferA;
}
if (writeResult != null)
destStream.EndWrite(writeResult);
}
private static void PerformOperations(Stream sourceStream, Stream destStream, Byte[] readBuffer, Byte[] writeBuffer, ref IAsyncResult readResult, ref IAsyncResult writeResult, int bytesToWrite)
{
//Start next read
readResult = sourceStream.BeginRead(readBuffer, 0, readBuffer.Length, null, null);
//End previous write
if (writeResult != null)
destStream.EndWrite(writeResult);
writeResult = destStream.BeginWrite(writeBuffer, 0, bytesToWrite, null, null);
}
One option is to separate the two aspects out into a producer/consumer queue: your compressor write blocks into a queue which is then consumed by a thread which just performs IO.
That way: