12

I have found example of async using of XmlWriter within msdn documentation http://msdn.microsoft.com/en-us/library/system.xml.xmlwriter.aspx

async Task TestWriter(Stream stream) 
{
    XmlWriterSettings settings = new XmlWriterSettings();
    settings.Async = true;
    using (XmlWriter writer = XmlWriter.Create(stream, settings)) {
        await writer.WriteStartElementAsync("pf", "root", "http://ns");
        await writer.WriteStartElementAsync(null, "sub", null);
        await writer.WriteAttributeStringAsync(null, "att", null, "val");
        await writer.WriteStringAsync("text");
        await writer.WriteEndElementAsync();
        await writer.WriteProcessingInstructionAsync("pName", "pValue");
        await writer.WriteCommentAsync("cValue");
        await writer.WriteCDataAsync("cdata value");
        await writer.WriteEndElementAsync();
        await writer.FlushAsync();
    }
}

All that I know about threads and async programming said me that this is too slow code and using synchronous Write methods will be much faster. I have modified this code and tested it. I have found that I'm right and synchronous code faster in 3-4 times on files more than 100Mb and more than 8-10 times faster on files less than 10mb on my env.

So my question is there any scenario where such code is usable and provides reasonable performance gains?

ajeh
  • 2,652
  • 2
  • 34
  • 65
Aleks Antonenko
  • 131
  • 1
  • 5

3 Answers3

14

First off, I do have to question the benchmarking. 3-4 times slower on 100MB files is really significant.

But regardless, async is not about doing things faster. It's about doing something else while that operation is going on. On the client side, you get the benefit of responsiveness; on the server side, you get the benefit of scalability.

The tradeoff is that the operation itself is actually slower (but it should be just a little slower, not 3-4 times slower). It's likely that you're not using a truly asynchronous stream for writing (you have to specifically open a file stream asynchronously to get an asynchronous stream).

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • 2
    As I suspected, you are not opening the file stream asynchronously. See the [MSDN docs (first couple paragraphs under Remarks)](http://msdn.microsoft.com/en-us/library/system.io.filestream.aspx) for more information. – Stephen Cleary May 20 '13 at 13:04
  • Even if I use FileOptions.Asynchronous like this Stream str = File.Create(FILE_NAME_A, 6140, FileOptions.Asynchronous); it becomes only slower by 5-10% – Aleks Antonenko May 20 '13 at 14:02
  • @AleksAntonenko, can you give us the absolute times of your test run? – Paulo Morgado May 20 '13 at 21:00
  • Async time : 7093.7093 Sync time : 1395.1395 – Aleks Antonenko May 22 '13 at 14:28
  • I understand, but my real question is about when code with such amount of small async operations is preferrable? why don't write bug block of date synchronously and then do async flush or kind of operation – Aleks Antonenko May 26 '13 at 15:57
  • > why don't write bug block of date synchronously -> why don't write big block of data synchronously – Aleks Antonenko May 26 '13 at 17:13
  • The point of Async code is too lighten the burden for the software. Lose some time on the read but gain responsiveness on the software. – yams Apr 15 '15 at 15:04
  • 1
    @ajeh: Asynchronous code using `async` and `await` is orders of magnitude easier to maintain than asynchronous code using any other pattern. – Stephen Cleary Mar 06 '18 at 23:09
5

There are some judgement calls to make when implementing async/await: namely, is the operation you are awaiting likely to include enough delay that the small overhead of async/await is worthwhile. As Stephen points out, Microsoft recommends 50 milliseconds as a rule of thumb threshold.

In your sample code, it's pretty unlikely that any of your operations will take more than 50 milliseconds.

Your real-world CDATA sections might be a worthwhile candidate for async/await. In the real world, we often write out base64 encoded PDFs to XML streams. In such an example, you may find that awaiting that portion of your code to be worthwhile.

async Task TestWriter(Stream stream, Stream sourceDocument)
{
    XmlWriterSettings settings = new XmlWriterSettings();
    settings.Async = true;
    using (XmlWriter writer = XmlWriter.Create(stream, settings))
    {
        writer.WriteStartElement("pf", "root", "http://ns");
        writer.WriteStartElement(null, "sub", null);
        writer.WriteAttributeString(null, "att", null, "val");
        writer.WriteString("text");
        writer.WriteEndElement();


        // Write the source document
        writer.WriteStartElement(null, "SourceDocument", null);
        Byte[] buffer = new Byte[4096];
        int bytesRead = await sourceDocument.ReadAsync(buffer, 0, 4096);
        while (bytesRead > 0)
        {
            await writer.WriteBase64Async(buffer, 0, bytesRead);
            bytesRead = await sourceDocument.ReadAsync(buffer, 0, 4096);
        }
        writer.WriteEndElement(); // SourceDocument

        writer.WriteEndElement(); // pf
        await writer.FlushAsync();
    }
}

Of course, if you bite off async/await in any portion of your code, you must implement it in your entire call stack to make it worthwhile, including subtle points like opening files with the async flag as Stephen noted in his comments.

Eric Patrick
  • 2,097
  • 2
  • 20
  • 31
2

I think Stephen Cleary's answer is correct and should be accepted as answer. However, I just want to put my viewpoint here as it may be too big for comment.

In my opinion async/await keywords, out of everything else, has two significant advantages:

1) await is a non-blocking wait - Traditionally, when we start an asynchronous task (say over another thread) and want its result/completion, we have used Wait(), WaitAll(), Thread.Join() etc (we also used APM and EAP - I'll mention it in point 2). These are all blocking calls - meaning they block the thread (like Thread.Sleep). So, if UI thread blocks, UI will freeze - if too many ThreadPool threads block, then ThreadPool has to bare overhead of creating new threads. By using async/await - we are able to perform non-blocking wait. At high level async/await keywords will break the method into a state machine (say, set of delegates before and after await) and schedule the delegate (using TPL task scheduler) when async task is over. This way we can await without freezing UI or blocking ThreadPool threads. So, summing up async/await will save resources (threads) and will not speed up stuff - it will have its own overhead of state machine and scheduling.

2) async/await increases code readability by gazillion times - if you've used EAP or APM (they are non-blocking in a way) - then you have to turn your code upside down. Difficult to figure out who's calling who - where to handle exception. Nightmare for developer. Using async/await, we can write asynchronous code which looks like synchronous.

async/await has its own way of how you think about async code and has its own set of gotchas - so, one has to use it carefully.

I think the example on MSDN is just for API demonstration purpose.

YK1
  • 7,327
  • 1
  • 21
  • 28