{"id":788,"date":"2014-12-07T16:33:06","date_gmt":"2014-12-07T14:33:06","guid":{"rendered":"http:\/\/dev.bratched.fr\/fr\/?p=788"},"modified":"2014-12-07T16:33:06","modified_gmt":"2014-12-07T14:33:06","slug":"image-depuis-http-avec-c-xaml-comparatifs-et-performances-partie-1","status":"publish","type":"post","link":"https:\/\/bratched.com\/fr\/2014\/12\/07\/image-depuis-http-avec-c-xaml-comparatifs-et-performances-partie-1\/","title":{"rendered":"Image depuis HTTP avec C# XAML &#8211; Comparatifs et Performances &#8211; Partie 1"},"content":{"rendered":"<h1>Introduction<\/h1>\n<p><a href=\"http:\/\/dev.bratched.fr\/fr\/wp-content\/uploads\/sites\/2\/2014\/12\/httpimagesbench.png\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone wp-image-792 size-full\" src=\"http:\/\/dev.bratched.fr\/fr\/wp-content\/uploads\/sites\/2\/2014\/12\/httpimagesbench.png\" alt=\"Chargement Bitmap Image en Http\" width=\"300\" height=\"300\" srcset=\"https:\/\/bratched.com\/fr\/wp-content\/uploads\/sites\/2\/2014\/12\/httpimagesbench.png 300w, https:\/\/bratched.com\/fr\/wp-content\/uploads\/sites\/2\/2014\/12\/httpimagesbench-150x150.png 150w\" sizes=\"(max-width: 300px) 100vw, 300px\" \/><\/a><\/p>\n<p>Voici un\u00a0article en 2 parties pour d\u00e9crire les diff\u00e9rentes impl\u00e9mentations possibles de la lecture\u00a0d&rsquo;images provenant d&rsquo;un serveur en Http.<br \/>\nEn terme de performances, il existe\u00a0des diff\u00e9rences\u00a0assez cons\u00e9quentes\u00a0suivant les impl\u00e9mentations choisies.<\/p>\n<p>Nous allons voir que parfois les r\u00e9sultats sont assez surprenants.<\/p>\n<h1>Charger une image depuis XAML<\/h1>\n<p>Le moyen le plus simple pour charger une image reste l\u2019impl\u00e9mentation XAML.<\/p>\n<pre class=\"lang:default decode:true \">&lt;Image Source=\"{Binding ImageUrl}\" Width=\"400\" Height=\"240\"\/&gt;<\/pre>\n<p>ImageUrl est de type Url et peut \u00eatre d\u00e9clar\u00e9e de cette fa\u00e7on dans le viewModel associ\u00e9 \u00e0 la Page.<\/p>\n<pre class=\"lang:default decode:true\">public  Uri ImageUrl\n{\n    get\n    {\n        return new Uri(@\"http:\/\/www.bing.com\/az\/hprichbg\/rb\/SnowyCP_FR-FR11972876588_400x240.jpg\");\n    }\n}<\/pre>\n<p>Cette impl\u00e9mentation est tr\u00e8s efficace, le contr\u00f4le XAML g\u00e8re quasiment tout pour vous :<\/p>\n<ul>\n<li>le chargement en asynchrone : Ne fige pas l\u2019affichage de l\u2019interface lors du chargement<\/li>\n<li>la mise en cache m\u00e9moire (un seul chargement)<\/li>\n<li>L\u2019optimisation http (\u00e9vite un \u00e9change serveur si l\u2019image est d\u00e9j\u00e0 charg\u00e9e)<\/li>\n<\/ul>\n<p>Cependant l\u2019image charg\u00e9e en XAML sera de nouveau recharg\u00e9e \u00e0 chaque lancement de l\u2019application.<br \/>\nAfin de mettre en place un syst\u00e8me de cache persist\u00e9, il est n\u00e9cessaire de charger le contenu de l\u2019image et de la <a title=\"Mise en cache d\u2019image dans l\u2019isolated storage avec Windows XAML et SemaphoreSlim\" href=\"http:\/\/dev.bratched.fr\/fr\/mise-en-cache-dimage-dans-lisolated-storage-avec-windows-xaml\/\">sauvegarder sur le t\u00e9l\u00e9phone dans l&rsquo;Isolated Storage<\/a>.<\/p>\n<p>Quelles sont les m\u00e9thodes possibles pour charger et afficher une image en asynchrone ?<\/p>\n<p>Voici\u00a09 fa\u00e7ons de faire en Universal Apps (Windows Phone XAML ou Windows 8.1). Leurs temps de r\u00e9ponse seront mesur\u00e9s sur Windows Phone 8.1 et expos\u00e9s dans la 2\u00e8me partie de cet article.<\/p>\n<h1>Impl\u00e9mentations<\/h1>\n<p>&nbsp;<\/p>\n<h1>1 &#8211; DataWriter<\/h1>\n<p>La premi\u00e8re impl\u00e9mentation, la plus classique, est d&rsquo;utiliser un httpClient pour r\u00e9cup\u00e9rer le flux sous forme de Stream puis\u00a0de le transf\u00e9rer dans le BitmapImage en passant par un DataWritter.<\/p>\n<p>Windows 8 et les Universal Apps nous obligent \u00e0 passer par un nouveau type de Stream le<strong> InMemoryRandomAccessStream<\/strong> pour l&rsquo;afficher comme source dans le BitmapImage.<!--more--><\/p>\n<pre class=\"lang:default decode:true\">public static async Task&lt;object&gt; GethttpImage01(string urlImage)\n{\n    HttpClient client = new HttpClient { MaxResponseContentBufferSize = 1000000 };\n    byte[] imageData = await client.GetByteArrayAsync(urlImage);\n    InMemoryRandomAccessStream rstream = new InMemoryRandomAccessStream();\n    IOutputStream outputStream = rstream.GetOutputStreamAt(0);\n    DataWriter writer = new DataWriter(outputStream);\n    writer.WriteBytes(imageData);           \n    await writer.StoreAsync();\n    await writer.FlushAsync();           \n    BitmapImage b = new BitmapImage();\n    await b.SetSourceAsync(rstream);\n    return b;\n}<\/pre>\n<p>Rien d\u2019exceptionnel, c\u2019est l\u2019exemple le plus courant que l\u2019on trouve sur les impl\u00e9mentations Windows 8.0.<\/p>\n<h2>2 &#8211; AsRandomAccessStream<\/h2>\n<p>La version 8.1 de Windows et Windows Phone Xaml a introduit une conversion implicite du MemoryStream en IMemoryRandomAccessStream.<br \/>\nLe m\u00eame code peut \u00eatre \u00e9crite de cette fa\u00e7on maintenant :<\/p>\n<pre class=\"lang:default decode:true\">public static async Task&lt;object&gt; GethttpImage02(string urlImage)\n{\n    var client = new HttpClient();\n    Stream stream = await client.GetStreamAsync(urlImage);\n    var memStream = new MemoryStream();\n    await stream.CopyToAsync(memStream);\n    memStream.Position = 0;\n    BitmapImage b = new BitmapImage();\n    await b.SetSourceAsync(memStream.AsRandomAccessStream());\n    return b;\n}<\/pre>\n<p>Le code est du coup plus l\u00e9ger et plus lisible. Est-il plus rapide ?<\/p>\n<h2>3 &#8211; AsRandomAccessStream and ConfigureAwait.<\/h2>\n<p>Afin d\u2019am\u00e9liorer le concept la trois\u00e8me \u00e9criture va utiliser l\u2019option <strong>ConfigureAwait(false)<\/strong> sur les m\u00e9thodes async\/await.<br \/>\nLe ConfigureAwait(false) emp\u00eache au thread qui ex\u00e9cute la m\u00e9thode en asynchrone de retourner sur le thread initial (souvent le Thread UI).<\/p>\n<p>Lorsqu\u2019il y a plusieurs await \u00e0 la suite qui ne n\u00e9cessitent pas un affichage, cela peut acc\u00e9l\u00e9rer consid\u00e9rablement les performances.<br \/>\nPar contre BitmapImage a besoin de s\u2019ex\u00e9cuter dans le ThreadUI, il faudra donc indiquer le retour au Thread UI\u00a0avec la m\u00e9thode RunAsync du Dispatcher.<br \/>\nPour ex\u00e9cuter une action depuis une classe il faudra utiliser la syntaxe suivante pour retrouver le Dispatcher associ\u00e9 \u00e0 l\u2019\u00e9cran principal.<\/p>\n<pre class=\"lang:default decode:true \">await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync\n  (Windows.UI.Core.CoreDispatcherPriority.Normal, async () =&gt;\n  {\n\/\/        UI Thread\n  });<\/pre>\n<p>Ce qui nous donne\u00a0:<\/p>\n<pre class=\"lang:default decode:true\">public static async Task&lt;object&gt; GethttpImage03(string urlImage)\n{\n    var client = new HttpClient();\n    Stream stream = await client.GetStreamAsync(urlImage).ConfigureAwait(false);\n    var memStream = new MemoryStream();\n    await stream.CopyToAsync(memStream).ConfigureAwait(false);\n    memStream.Position = 0;\n    BitmapImage b = null;\n    await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync\n        (Windows.UI.Core.CoreDispatcherPriority.Normal, async () =&gt;\n        {\n            b = new BitmapImage();\n            await b.SetSourceAsync(memStream.AsRandomAccessStream());\n        });\n    return b;\n}<\/pre>\n<h2>\u00a04 &#8211; Transformation du flux MemoryStream dans un Task \u00e0 part<\/h2>\n<p>La solution suivante consiste \u00e0 r\u00e9\u00e9crire la transformation du MemoryStream vers IRandomAccessTream dans une t\u00e2che ex\u00e9cut\u00e9e en asynchrone.<br \/>\nLa proc\u00e9dure de conversion ressemble \u00e0 \u00e7a.<\/p>\n<pre class=\"lang:default decode:true \">public static async Task&lt;IRandomAccessStream&gt; ConvertToRandomAccessStream(MemoryStream memoryStream)\n{\n    var randomAccessStream = new InMemoryRandomAccessStream();\n\n    var outputStream = randomAccessStream.GetOutputStreamAt(0);\n    var dw = new DataWriter(outputStream);\n    var task = new Task(async () =&gt; \n            {\n                dw.WriteBytes(memoryStream.ToArray());\n                await dw.StoreAsync();\n                await outputStream.FlushAsync();\n            });\n    task.Start();\n    await task.ConfigureAwait(false);\n            \n    return randomAccessStream;\n}<\/pre>\n<p>Maintenant nous pouvons utiliser cette m\u00e9thode dans le chargement de l\u2019image :<\/p>\n<pre class=\"lang:default decode:true \">public static async Task&lt;object&gt; GethttpImage04(string urlImage)\n{\n    var httpClient = new HttpClient();\n    var memStream = new MemoryStream();\n    using (var responseStream = await httpClient.GetStreamAsync(new Uri(urlImage)))         \n    {\n        await responseStream.CopyToAsync(memStream).ConfigureAwait(false); \n        memStream.Position = 0;\n    }\n    IRandomAccessStream rstream = await ConvertToRandomAccessStream(memStream).ConfigureAwait(false);\n    BitmapImage b = null;\n    await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync\n        (Windows.UI.Core.CoreDispatcherPriority.Normal, async () =&gt;\n        {\n            b = new BitmapImage();\n            await b.SetSourceAsync(rstream);\n        });\n    return b;\n}<\/pre>\n<h2>5 &#8211; Remplacement du HttpClient par le HttpWebRequest<\/h2>\n<p>On remplace l\u2019acc\u00e8s http qui se fait simplement par\u00a0le HttpClient\u00a0par un HttpWebRequest.<br \/>\nLe HttpWebRequest est moins \u00e9volu\u00e9 que le HttpClient et permet un param\u00e9trage plus fin tout en \u00e9tant plus performant.<br \/>\nRien de compliqu\u00e9, le code reste identique, seul le d\u00e9but change.<\/p>\n<pre class=\"lang:default decode:true\">public static async Task&lt;object&gt; GethttpImage05(string urlImage)\n{\n    var request = (HttpWebRequest)WebRequest.Create(urlImage);\n    var memStream = new MemoryStream();\n    request.Proxy = null;\n    request.ContinueTimeout = 0;            \n    WebResponse httpwebResponse = await request.GetResponseAsync().ConfigureAwait(false);\n    using (var responseStream = httpwebResponse.GetResponseStream())\n    {\n        await responseStream.CopyToAsync(memStream).ConfigureAwait(false);\n        memStream.Position = 0;\n    }\n    IRandomAccessStream rstream = await ConvertToRandomAccessStream(memStream).ConfigureAwait(false);\n    BitmapImage b = null;\n    await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync\n        (Windows.UI.Core.CoreDispatcherPriority.Normal, () =&gt;\n        {\n            b = new BitmapImage();\n            b.SetSource(rstream);                   \n        });           \n    return b;\n}<\/pre>\n<h2>6 &#8211; Actions \u00e0 la place du Async \/ Await<\/h2>\n<p>Cela reste toujours une \u00e9nigme mais l\u2019utilisation du mode standard sans le async\/await reste la plus performante (cf. <a title=\"HTTP et images sous Windows Phone\" href=\"http:\/\/dev.bratched.fr\/fr\/http-et-images-sous-windows-phone\/\">article sur Windows Phone 7 et 8<\/a>).<br \/>\nLe code est r\u00e9\u00e9crit dans un fonctionnement avec des types Action (Comme\u00a0au bon vieux temps)<\/p>\n<pre class=\"lang:default decode:true\">private static void BitmapImageAction(string urlImage, Action&lt;object&gt; action)\n{\n    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(urlImage);\n    request.Proxy = null;\n    request.ContinueTimeout = 0;   \n    request.BeginGetResponse(async result =&gt;\n    {\n        using (var sr = request.EndGetResponse(result))\n        {\n            MemoryStream memStream = new MemoryStream();\n            sr.GetResponseStream().CopyTo(memStream);                    \n            IRandomAccessStream rstream = await ConvertToRandomAccessStream(memStream).ConfigureAwait(false);\n            await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync\n                (Windows.UI.Core.CoreDispatcherPriority.Normal, () =&gt;\n                {\n                    BitmapImage image = new BitmapImage();\n                    image.CreateOptions = BitmapCreateOptions.IgnoreImageCache;\n                    image.SetSource(rstream);                            \n                    if (action != null) action(image);\n                });\n        }\n    }, null);\n}<\/pre>\n<p>La seule diff\u00e9rence avec la m\u00e9thode 5 est donc\u00a0le r\u00e9sultat qui est retourn\u00e9 par une Action.<br \/>\nAfin de l\u2019utiliser facilement, la fonction est appel\u00e9e dans un m\u00e9canisme async\/await de cette fa\u00e7on :<\/p>\n<pre class=\"lang:default decode:true\">public static Task&lt;object&gt; GethttpImage06(string urlImage)\n{\n    return Task.Run(() =&gt;\n    {\n        var t = new TaskCompletionSource&lt;object&gt;();\n        BitmapImageAction(urlImage, s =&gt; t.TrySetResult(s));\n        return t.Task;\n    });\n}<\/pre>\n<h2>7 &#8211; HttpWebRequest et AsRandomAccessStream<\/h2>\n<p>Une variante de la 3\u00e8me m\u00e9thode avec le HttpWebRequest<\/p>\n<pre class=\"lang:default decode:true\">public static async Task&lt;object&gt; GethttpImage07(string urlImage)\n{\n    var request = (HttpWebRequest)WebRequest.Create(urlImage);\n    var memStream = new MemoryStream();\n    request.Proxy = null;\n    request.ContinueTimeout = 0;\n    WebResponse httpwebResponse = await request.GetResponseAsync().ConfigureAwait(false);\n    using (var responseStream = httpwebResponse.GetResponseStream())\n    {\n        await responseStream.CopyToAsync(memStream).ConfigureAwait(false);\n        memStream.Position = 0;\n    }\n\n    BitmapImage b = null;\n    await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync\n        (Windows.UI.Core.CoreDispatcherPriority.Normal, async () =&gt;\n        {\n            b = new BitmapImage();\n            await b.SetSourceAsync(memStream.AsRandomAccessStream());\n        });\n    return b;            \n}<\/pre>\n<h2>\u00a08 &#8211; HttpWebRequest et AsRandomAccessStream\u00a0avec Action<\/h2>\n<p>C&rsquo;est une variante de la solution 6. On remplace la conversion maison par le <strong>AsRandomAccessStream<\/strong>. Le tout est retourn\u00e9 par une Action.<\/p>\n<pre class=\"lang:default decode:true\">private static void GethttpImageAction2(string urlImage, Action&lt;object&gt; action)\n{\n    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(urlImage);\n    request.Proxy = null;\n    request.ContinueTimeout = 0;\n    MemoryStream memStream = new MemoryStream();\n    request.BeginGetResponse(async result =&gt;\n    {\n        using (var sr = request.EndGetResponse(result))\n        {                    \n            sr.GetResponseStream().CopyTo(memStream);                \n            memStream.Position = 0;\n        }                \n        await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync\n                (Windows.UI.Core.CoreDispatcherPriority.Low, () =&gt;\n                {\n                    BitmapImage image = new BitmapImage();\n                    image.SetSource(memStream.AsRandomAccessStream());\n                    if (action != null) action(image);\n                });                \n    }, null);\n}\n\npublic static Task&lt;object&gt; GethttpImage08(string urlImage)\n{\n    return Task.Run(() =&gt;\n    {\n        var t = new TaskCompletionSource&lt;object&gt;();\n        GethttpImageAction2(urlImage, s =&gt; t.TrySetResult(s));\n        return t.Task;\n    });\n}<\/pre>\n<h2>9 &#8211; HttpWebRequest, AsRandom Access Stream sans ConfigureAwait<\/h2>\n<p>Le m\u00eame sc\u00e9nario que le 2 mais en utilisant HttpWebRequest \u00e0 la place de HttpClient.<\/p>\n<pre class=\"lang:default decode:true\">public static async Task&lt;object&gt; GethttpImage09(string urlImage)\n{\n    var request = (HttpWebRequest)WebRequest.Create(urlImage);\n    var memStream = new MemoryStream();\n    request.Proxy = null;\n    request.ContinueTimeout = 0;\n    WebResponse httpwebResponse = await request.GetResponseAsync();\n    using (var responseStream = httpwebResponse.GetResponseStream())\n    {\n        responseStream.CopyTo(memStream);\n        memStream.Position = 0;\n    }\n\n    BitmapImage b = null;\n    await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync\n        (Windows.UI.Core.CoreDispatcherPriority.Normal, async () =&gt;\n        {\n            b = new BitmapImage();\n            await b.SetSourceAsync(memStream.AsRandomAccessStream());\n        });\n    return b;\n}<\/pre>\n<h1>\u00a0Conclusion de la Partie 1<\/h1>\n<p>Plusieurs impl\u00e9mentations et des diff\u00e9rences dans les r\u00e9sultats de performances. Certes pas \u00e9normes :\u00a015-20%% d&rsquo;\u00e9cart, mais tout de m\u00eame 15% de performances sur des devices mobiles c&rsquo;est toujours bon \u00e0 prendre.<\/p>\n<p>Quelle est la m\u00e9thode la plus rapide selon vous ?<\/p>\n<p>R\u00e9ponse tr\u00e8s prochainement dans la deuxi\u00e8me Partie.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Introduction Voici un\u00a0article en 2 parties pour d\u00e9crire les diff\u00e9rentes impl\u00e9mentations possibles de la lecture\u00a0d&rsquo;images provenant d&rsquo;un serveur en Http. En terme de performances, il existe\u00a0des diff\u00e9rences\u00a0assez cons\u00e9quentes\u00a0suivant les impl\u00e9mentations choisies. Nous allons voir que parfois les r\u00e9sultats sont assez surprenants. Charger une image depuis XAML Le moyen le plus simple pour charger une image [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[59,62,63],"tags":[135,68,69,70,136,9,77,79,137,138,58,8],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/bratched.com\/fr\/wp-json\/wp\/v2\/posts\/788"}],"collection":[{"href":"https:\/\/bratched.com\/fr\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/bratched.com\/fr\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/bratched.com\/fr\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/bratched.com\/fr\/wp-json\/wp\/v2\/comments?post=788"}],"version-history":[{"count":0,"href":"https:\/\/bratched.com\/fr\/wp-json\/wp\/v2\/posts\/788\/revisions"}],"wp:attachment":[{"href":"https:\/\/bratched.com\/fr\/wp-json\/wp\/v2\/media?parent=788"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/bratched.com\/fr\/wp-json\/wp\/v2\/categories?post=788"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/bratched.com\/fr\/wp-json\/wp\/v2\/tags?post=788"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}