Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

XmlWriter async methods

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?

like image 900
Aleks Antonenko Avatar asked May 20 '13 00:05

Aleks Antonenko


3 Answers

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).

like image 67
Stephen Cleary Avatar answered Nov 14 '22 19:11

Stephen Cleary


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.

like image 5
Eric Patrick Avatar answered Nov 14 '22 18:11

Eric Patrick


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.

like image 2
YK1 Avatar answered Nov 14 '22 18:11

YK1