<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Dmitriy Nesteryuk</title>
    <description>The latest articles on DEV Community by Dmitriy Nesteryuk (@dnesteryuk).</description>
    <link>https://dev.to/dnesteryuk</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F173947%2Fff43dd86-4459-48c5-8861-7d66857ea5f1.jpg</url>
      <title>DEV Community: Dmitriy Nesteryuk</title>
      <link>https://dev.to/dnesteryuk</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dnesteryuk"/>
    <language>en</language>
    <item>
      <title>Grape: The cost of ActiveSupport::HashWithIndifferentAccess</title>
      <dc:creator>Dmitriy Nesteryuk</dc:creator>
      <pubDate>Sun, 08 Dec 2019 15:43:26 +0000</pubDate>
      <link>https://dev.to/dnesteryuk/grape-the-cost-of-activesupport-hashwithindifferentaccess-53f1</link>
      <guid>https://dev.to/dnesteryuk/grape-the-cost-of-activesupport-hashwithindifferentaccess-53f1</guid>
      <description>&lt;p&gt;By &lt;a href="https://github.com/ruby-grape/grape#params-class"&gt;default Grape&lt;/a&gt; uses &lt;a href="https://github.com/rails/rails/blob/master/activesupport/lib/active_support/hash_with_indifferent_access.rb"&gt;ActiveSupport::HashWithIndifferentAccess&lt;/a&gt; as a parameter builder. The most important thing about &lt;code&gt;ActiveSupport::HashWithIndifferentAccess&lt;/code&gt; is that symbols are mapped to strings when used as keys.&lt;/p&gt;

&lt;p&gt;So, if your routers refer to parameters by using symbols, keys get converted to strings. Let's see how it affects memory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'ruby-prof'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'grape'&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;API&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Grape&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;API&lt;/span&gt;
  &lt;span class="n"&gt;prefix&lt;/span&gt; &lt;span class="ss"&gt;:api&lt;/span&gt;
  &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="s1"&gt;'v1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;using: :path&lt;/span&gt;

  &lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;requires&lt;/span&gt; &lt;span class="ss"&gt;:address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: &lt;/span&gt;&lt;span class="no"&gt;Hash&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;requires&lt;/span&gt; &lt;span class="ss"&gt;:street&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: &lt;/span&gt;&lt;span class="no"&gt;String&lt;/span&gt;
      &lt;span class="n"&gt;requires&lt;/span&gt; &lt;span class="ss"&gt;:postal_code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: &lt;/span&gt;&lt;span class="no"&gt;Integer&lt;/span&gt;
      &lt;span class="n"&gt;optional&lt;/span&gt; &lt;span class="ss"&gt;:city&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: &lt;/span&gt;&lt;span class="no"&gt;String&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="s1"&gt;'/'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;declared&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;declared&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;declared&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:address&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="ss"&gt;:street&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="ss"&gt;method: &lt;/span&gt;&lt;span class="s1"&gt;'POST'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;params: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="ss"&gt;address: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="ss"&gt;street: &lt;/span&gt;&lt;span class="s1"&gt;'Test Street'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;postal_code: &lt;/span&gt;&lt;span class="s1"&gt;'90698'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;city: &lt;/span&gt;&lt;span class="s1"&gt;'Imagine'&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;env&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Rack&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;MockRequest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;env_for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/api/v1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# warm up&lt;/span&gt;
&lt;span class="no"&gt;API&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;

&lt;span class="no"&gt;RubyProf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;measure_mode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;RubyProf&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;MEMORY&lt;/span&gt;

&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;RubyProf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;profile&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="no"&gt;API&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;printer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;RubyProf&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;FlatPrinter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;printer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;STDOUT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;min_percent: &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;For profiling Grape 1.2.5 and Ruby 2.6.3 were used. Results:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Measure Mode: memory
Thread ID: 47373520927160
Fiber ID: 47373526155200
Total: 41112.000000
Sort by: self_time

 %self      total      self      wait     child     calls  name
 13.66   9304.000  5616.000     0.000  3688.000       18  *Hash#each_pair
 10.70  12648.000  4400.000     0.000  8248.000       29  *Class#new
  8.68   5088.000  3568.000     0.000  1520.000        8  *Array#map
  5.64   2320.000  2320.000     0.000     0.000       58   Symbol#to_s
  5.08   2088.000  2088.000     0.000     0.000        9   Hash#merge
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;According to the &lt;a href="https://ruby-prof.github.io/#reports"&gt;Ruby-prof doc&lt;/a&gt;, the total represents a number of bytes. Let's try another builder which only supports symbols.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Grape&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configure&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;param_builder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Grape&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Extensions&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Hash&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ParamBuilder&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Results:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Measure Mode: memory
Thread ID: 47077831236000
Fiber ID: 47077836608980
Total: 33328.000000
Sort by: self_time

 %self      total      self      wait     child     calls  name
 11.28   5736.000  3760.000     0.000  1976.000        7   Hash#each_pair
 10.71   5088.000  3568.000     0.000  1520.000        8  *Array#map
  6.94   9664.000  2312.000     0.000  7352.000       25  *Class#new
  6.27   2088.000  2088.000     0.000     0.000        9   Hash#merge
  4.08   1440.000  1360.000     0.000    80.000        5   Grape::Endpoint#run_filters
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Switching to the simpler builder saves 7,60 Kbytes per request. Yeah, it isn't crazy number, but the change is one LOC. So, if you can agree with your team on using symbols everywhere, you can save a chunk of memory. By the way, the parameter builder can be configured per route, thus, the migration might be done per route.&lt;/p&gt;

&lt;p&gt;However, if strings are used as keys for parameters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;  &lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;requires&lt;/span&gt; &lt;span class="s1"&gt;'address'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: &lt;/span&gt;&lt;span class="no"&gt;Hash&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;requires&lt;/span&gt; &lt;span class="s1"&gt;'street'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: &lt;/span&gt;&lt;span class="no"&gt;String&lt;/span&gt;
      &lt;span class="n"&gt;requires&lt;/span&gt; &lt;span class="s1"&gt;'postal_code'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: &lt;/span&gt;&lt;span class="no"&gt;Integer&lt;/span&gt;
      &lt;span class="n"&gt;optional&lt;/span&gt; &lt;span class="s1"&gt;'city'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: &lt;/span&gt;&lt;span class="no"&gt;String&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="s1"&gt;'/'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;declared&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;declared&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;declared&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'address'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;'street'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;the picture is a bit better for &lt;code&gt;Grape::Extensions::ActiveSupport::HashWithIndifferentAccess::ParamBuilder&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Measure Mode: memory
Thread ID: 47413799950760
Fiber ID: 47413805177840
Total: 39832.000000
Sort by: self_time

 %self      total      self      wait     child     calls  name
 14.10   9064.000  5616.000     0.000  3448.000       18  *Hash#each_pair
 11.05  12448.000  4400.000     0.000  8048.000       29  *Class#new
  8.96   5088.000  3568.000     0.000  1520.000        8  *Array#map
  5.24   2088.000  2088.000     0.000     0.000        9   Hash#merge
  4.66   2016.000  1856.000     0.000   160.000        8   ActiveSupport::HashWithIndifferentAccess#[]=
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Anyway &lt;code&gt;Grape::Extensions::Hash::ParamBuilder&lt;/code&gt; is a winner.&lt;/p&gt;

&lt;h3&gt;
  
  
  Upcoming Grape 1.3.0
&lt;/h3&gt;

&lt;p&gt;It has &lt;a href="https://github.com/ruby-grape/grape/pull/1940"&gt;an improvement&lt;/a&gt; in &lt;code&gt;Grape::Extensions::ActiveSupport::HashWithIndifferentAccess::ParamBuilder&lt;/code&gt;, so it will be slightly better.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Measure Mode: memory
Thread ID: 46918412761520
Fiber ID: 46918421185000
Total: 36448.000000
Sort by: self_time

 %self      total      self      wait     child     calls  name
 12.86   8336.000  4688.000     0.000  3648.000       11  *Hash#each_pair
  9.79   5248.000  3568.000     0.000  1680.000        8  *Array#map
  8.89  11880.000  3240.000     0.000  8640.000       27  *Class#new
  5.93   2160.000  2160.000     0.000     0.000       54   Symbol#to_s
  5.73   2088.000  2088.000     0.000     0.000        9   Hash#merge
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



</description>
      <category>ruby</category>
      <category>grape</category>
      <category>performance</category>
    </item>
    <item>
      <title>Chrome: Cache storage VS Disk cache</title>
      <dc:creator>Dmitriy Nesteryuk</dc:creator>
      <pubDate>Sat, 01 Jun 2019 15:07:18 +0000</pubDate>
      <link>https://dev.to/dnesteryuk/chrome-cache-storage-vs-disk-cache-2f95</link>
      <guid>https://dev.to/dnesteryuk/chrome-cache-storage-vs-disk-cache-2f95</guid>
      <description>&lt;p&gt;Most likely, you've heard of &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Cache" rel="noopener noreferrer"&gt;Cache storage API&lt;/a&gt; and &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API" rel="noopener noreferrer"&gt;Service worker&lt;/a&gt; which might be used for caching resources and serving them later from the cache. One use case for that is prefetching resources before users need them.&lt;/p&gt;

&lt;p&gt;I've been working on &lt;a href="https://github.com/sirko-io/engine" rel="noopener noreferrer"&gt;a project&lt;/a&gt; which implements the described use case. A few weeks ago, Stefan created &lt;a href="https://github.com/sirko-io/engine/issues/45" rel="noopener noreferrer"&gt;a task&lt;/a&gt; where he described an observation that the speed of delivering assets from Cache storage is low in some cases. So, I decided to verify that.&lt;/p&gt;

&lt;p&gt;I created &lt;a href="https://gist.github.com/dnesteryuk/eff23819e44ec61c842a16d0237b8c96" rel="noopener noreferrer"&gt;demo&lt;/a&gt; to compare Cache storage and Disk cache. The &lt;code&gt;index.html&lt;/code&gt; needs to display N images. There is an option to precache them then embed into the page. The &lt;code&gt;sw.js&lt;/code&gt; looks into the cache, if resources are there, they get served from the cache, otherwise, they get normally downloaded.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conditions of tests
&lt;/h3&gt;

&lt;p&gt;Tests were only performed in Chrome. If there is enough interest, I might perform them in Firefox too. All images had identical size but different names to make the browser request them again and again. Below you will see the best results of 10 tries.&lt;/p&gt;

&lt;p&gt;Chrome Dev tools provides timing for every resource.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqjke8s1xvh7a4y7yfh5w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqjke8s1xvh7a4y7yfh5w.png" width="786" height="319"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Information about all loaded resources can be downloaded as &lt;a href="https://en.wikipedia.org/wiki/.har" rel="noopener noreferrer"&gt;a HAR file&lt;/a&gt;. Then any language/tool can be used to analyze the extracted information. In every try I was looking at time of loading all images. So, when you meet &lt;code&gt;min&lt;/code&gt;, &lt;code&gt;max&lt;/code&gt; or &lt;code&gt;mean&lt;/code&gt;, I refer to the time when all images were loaded.&lt;/p&gt;

&lt;h3&gt;
  
  
  Test #1: 100 big images
&lt;/h3&gt;

&lt;p&gt;For this test the image size was 1.5 Mb. In general, it is unlikely there are sites which load that many heavy images. It was more about curiosity.&lt;/p&gt;

&lt;h4&gt;
  
  
  Cache storage
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fha12owyrlmh6a8131tq6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fha12owyrlmh6a8131tq6.png" width="701" height="230"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Mostly, the browser spent time on downloading images. There is no clear pattern how the browser started to handle requests.&lt;/p&gt;

&lt;p&gt;As I mentioned 10 tries were performed, so here is statistics about them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;min:&lt;/strong&gt; 514.93 ms (represented in the above chart)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;mean:&lt;/strong&gt; 755.43 ms&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;max:&lt;/strong&gt; 1031.07 ms&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Disk cache
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Firq4yvvud9siakrpsw8x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Firq4yvvud9siakrpsw8x.png" width="704" height="231"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this case, the download operation didn't take that much time, but images waited to be queued. We can even see how the browser took images for processing (approximately 6 images in one batch).&lt;/p&gt;

&lt;p&gt;Statistics about 10 tries:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;min:&lt;/strong&gt; 646.73 ms (represented in the above chart)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;mean:&lt;/strong&gt; 1050.81 ms&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;max:&lt;/strong&gt; 1450.36 ms&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cache storage is a clear winner in this test.&lt;/p&gt;

&lt;h3&gt;
  
  
  Test #2: 30 small images
&lt;/h3&gt;

&lt;p&gt;For this test the image size was 31.3 Kb. This scenario has higher probability to be real than the previous one. I used images for tests, but it might be different assets (javascript files, fonts, images, css files etc), so some sites might load 30 assets on a page.&lt;/p&gt;

&lt;h4&gt;
  
  
  Cache storage
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F65j6k91tmppgf55pwqdz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F65j6k91tmppgf55pwqdz.png" width="702" height="231"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The download wasn't a problem any more but the waiting was.&lt;/p&gt;

&lt;p&gt;Statistics about 10 tries:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;min:&lt;/strong&gt; 26.22 ms (represented in the above chart)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;mean:&lt;/strong&gt; 34.1 ms&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;max:&lt;/strong&gt; 40.84 ms&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Disk cache
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2v9wn0vcatt9s7jvsr2a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2v9wn0vcatt9s7jvsr2a.png" width="700" height="228"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Again, the queueing operation took more time than any other operation.&lt;/p&gt;

&lt;p&gt;Statistics about 10 tries:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;min:&lt;/strong&gt; 15.3 ms (represented in the above chart)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;mean:&lt;/strong&gt; 22.89 ms&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;max:&lt;/strong&gt; 28.97 ms&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, Disk cache is a winner.&lt;/p&gt;

&lt;h3&gt;
  
  
  Test #3: 100 small images
&lt;/h3&gt;

&lt;p&gt;Again, it is almost unreal case, but I was curios why Cache storage was faster in the first test. It might have been a number of images or the image size. So, I took the image from the previous test and loaded it 100 times.&lt;/p&gt;

&lt;h4&gt;
  
  
  Cache storage
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy831u8djw6hukz78d4o8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy831u8djw6hukz78d4o8.png" width="701" height="231"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Again, the waiting operation dominated here.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;min:&lt;/strong&gt; 65.5 ms (represented in the above chart)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;mean:&lt;/strong&gt; 78.54 ms&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;max:&lt;/strong&gt; 90.51 ms&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Disk cache
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fap85uifv79xs7wsu9sg4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fap85uifv79xs7wsu9sg4.png" width="701" height="228"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Again, the chart shows that the browser took the batch of 5-6 images and loaded them in parallel then took another batch.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;min:&lt;/strong&gt; 55.44 ms (represented in the above chart)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;mean:&lt;/strong&gt; 101.84 ms&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;max:&lt;/strong&gt; 142.45 ms&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By comparing the mean Cache storage is a winner again. So, I assume it is more about an ability to serve requests in parallel.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap up
&lt;/h2&gt;

&lt;p&gt;Even tests were performed on localhost results differ between tries. Disk cache was slightly better in delivering a small number of assets, Cache storage was better in delivering lots of assets. At some point, it is a little bit unfair to compare Cache storage and Disk cache, the first one has more wide use and developers have access to API to control it. Anyway, Cache storage isn't slow as it might have looked when the task was opened.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>pwa</category>
      <category>browsers</category>
    </item>
  </channel>
</rss>
