<?xml version="1.0" encoding="utf-8" standalone="yes"?><feed xmlns="http://www.w3.org/2005/Atom">
  <title></title>
  <subtitle></subtitle>
  <id>https://www.endpointdev.com/blog/tags/optimization/</id>
  <link href="https://www.endpointdev.com/blog/tags/optimization/"/>
  <link href="https://www.endpointdev.com/blog/tags/optimization/" rel="self"/>
  <updated>2022-03-01T00:00:00+00:00</updated>
  <author>
    <name>End Point Dev</name>
  </author>
  
    <entry>
      <title>Optimizing media delivery with Cloudinary</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2022/03/optimizing-image-delivery-with-cloudinary/"/>
      <id>https://www.endpointdev.com/blog/2022/03/optimizing-image-delivery-with-cloudinary/</id>
      <published>2022-03-01T00:00:00+00:00</published>
      <author>
        <name>Juan Pablo Ventoso</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2022/03/optimizing-image-delivery-with-cloudinary/la-cumbrecita-202201.webp&#34; alt=&#34;Beautiful cloudy mountain scene with river flowing by lush banks with people swimming, relaxing, and walking towards multistory buildings&#34;&gt;&lt;/p&gt;
&lt;!-- Photo by Juan Pablo Ventoso --&gt;
&lt;p&gt;I remember how we needed to deal with different image formats and sizes years ago: From using the WordPress-style approach of automatically saving different resolutions on the server when uploading a picture, to using a PHP script to resize or crop images on the fly and return the result as a response to the frontend. Of course, many of those approaches were expensive, and not fully optimized for different browsers or device sizes.&lt;/p&gt;
&lt;p&gt;With those experiences in mind, it was a nice surprise for me to discover &lt;a href=&#34;https://cloudinary.com/&#34;&gt;Cloudinary&lt;/a&gt; when working on a new project a couple of months ago. It&amp;rsquo;s basically a cloud service that saves and delivers media content with a lot of transformations and management options for us to use. &lt;a href=&#34;https://cloudinary.com/pricing&#34;&gt;There is a free version&lt;/a&gt; with a usage limit: Up to 25K transformations or 25 GB of storage/​bandwidth, which should be enough for most non-enterprise websites. The cheapest paid service is $99 per month.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s a list of the image features we used on that project. I know they offer many other things that can be used as well, but I think this is a good start for anyone who hasn&amp;rsquo;t used this service yet:&lt;/p&gt;
&lt;h3 id=&#34;resizing-and-cropping&#34;&gt;Resizing and cropping&lt;/h3&gt;
&lt;p&gt;When you make a request for an image, you can instruct the Cloudinary API to &lt;a href=&#34;https://cloudinary.com/documentation/resizing_and_cropping&#34;&gt;retrieve it with a given size&lt;/a&gt;, which will trigger a transformation on their end before delivering the content. You can also use a cropping method: fill, fill with padding, scale down, etc.&lt;/p&gt;
&lt;h3 id=&#34;gravity-position&#34;&gt;Gravity position&lt;/h3&gt;
&lt;p&gt;When we specify a &lt;a href=&#34;https://cloudinary.com/documentation/resizing_and_cropping#control_gravity&#34;&gt;gravity position&lt;/a&gt; to crop an image, the service will keep the area of the image we decide to use as the focal point. We can choose a corner (for example, top left), but also—and this is probably one of the most interesting capabilities on this service—we can specify &lt;a href=&#34;https://cloudinary.com/documentation/transformation_reference#g_special_position&#34;&gt;&amp;ldquo;special positions&amp;rdquo;&lt;/a&gt;: By using machine learning, we can instruct Cloudinary to use face detection, or even focus on other objects, like an animal or a flower in the picture.&lt;/p&gt;
&lt;h3 id=&#34;automatic-format&#34;&gt;Automatic format&lt;/h3&gt;
&lt;p&gt;Another cool feature is the &lt;a href=&#34;https://cloudinary.com/documentation/transformation_reference#f_auto&#34;&gt;automatic format&lt;/a&gt;, which will use your request headers to find the most efficient picture format for your browser type and version. For example, if the browser supports it, Cloudinary will return the image in WebP format, which is generally more efficient than standard JPEG, as End Point CTO Jon Jensen demonstrates on his recent &lt;a href=&#34;https://www.endpointdev.com/blog/2022/02/webp-heif-avif-jpegxl/&#34;&gt;blog post&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2022/03/optimizing-image-delivery-with-cloudinary/image-response.jpg&#34; alt=&#34;Screenshot of Chrome browser dev tools showing network response for a WebP image&#34;&gt;&lt;br&gt;
Automatic format in action: Returning a WebP image in Chrome&lt;/p&gt;
&lt;h3 id=&#34;other-features&#34;&gt;Other features&lt;/h3&gt;
&lt;p&gt;There are many other options for us to choose when querying their API, like setting up a default placeholder when we don’t have an image, applying color transformations, removing red eyes, among other things. The &lt;a href=&#34;https://cloudinary.com/documentation/transformation_reference&#34;&gt;Transformation reference page&lt;/a&gt; on their documentation section is a great resource.&lt;/p&gt;
&lt;h3 id=&#34;nuxtjs-integration&#34;&gt;NuxtJS integration&lt;/h3&gt;
&lt;p&gt;The project I mentioned above was a &lt;a href=&#34;https://nuxtjs.org/&#34;&gt;NuxtJS&lt;/a&gt; application with a &lt;a href=&#34;https://nodejs.org/&#34;&gt;Node.js&lt;/a&gt; backend. And since there&amp;rsquo;s a &lt;a href=&#34;https://cloudinary.nuxtjs.org/&#34;&gt;NuxtJS module for Cloudinary&lt;/a&gt;, it made sense to use it instead of building the queries to the API from scratch.&lt;/p&gt;
&lt;p&gt;The component works great, except for one bug that we found that didn&amp;rsquo;t allow us to fully use their image component with server-side rendering enabled. Between that drawback and some issues trying to use the lazy loading setting, we ended up creating a Vue component ourselves that used a standard image tag instead. But we still used their component to generate most of the API calls and render the results.&lt;/p&gt;
&lt;p&gt;Below is an example of using the Cloudinary Image component on a Vue template:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;template&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;div&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;cld-image&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#369&#34;&gt;:public-id&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;publicId&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#369&#34;&gt;width&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;200&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#369&#34;&gt;height&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;200&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#369&#34;&gt;crop&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;fill&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#369&#34;&gt;gravity&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;auto:subject&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#369&#34;&gt;radius&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;max&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#369&#34;&gt;fetchFormat&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;auto&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#369&#34;&gt;quality&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;auto&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#369&#34;&gt;alt&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;An image example with Cloudinary&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;div&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;template&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;alternatives&#34;&gt;Alternatives&lt;/h3&gt;
&lt;p&gt;Of course, Cloudinary is not the only image processing and CDN company out there: There are other companies offering similar services, like &lt;a href=&#34;https://www.cloudflare.com/products/cloudflare-images/&#34;&gt;Cloudflare images&lt;/a&gt;, &lt;a href=&#34;https://www.cloudimage.io/&#34;&gt;Cloudimage&lt;/a&gt;, or &lt;a href=&#34;https://imagekit.io/&#34;&gt;imagekit.io&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Do you know any other good alternatives, or have you used any other Cloudinary feature that is not listed here? Feel free to add a comment below!&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Image compression: WebP presets, HEIC, AVIF, JPEG XL</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2022/02/webp-heif-avif-jpegxl/"/>
      <id>https://www.endpointdev.com/blog/2022/02/webp-heif-avif-jpegxl/</id>
      <published>2022-02-15T00:00:00+00:00</published>
      <author>
        <name>Jon Jensen</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2022/02/webp-heif-avif-jpegxl/20211223-224644-sm.webp&#34; alt=&#34;Rubble of a demolished house in front of a guilty-looking Caterpillar excavator&#34;&gt;&lt;/p&gt;
&lt;!-- Photo by Jon Jensen --&gt;
&lt;p&gt;How time flies. Eight years ago I wrote the blog post &lt;a href=&#34;/blog/2014/01/webp-images-experiment-on-end-point/&#34;&gt;WebP images experiment on End Point website&lt;/a&gt; to describe and demonstrate how the WebP image format can store an equivalent-quality image in a much smaller file size than the older JPEG, PNG, and GIF formats.&lt;/p&gt;
&lt;p&gt;My WebP examples there were 17–23% of the JPEGs they came from, or about 5–6× smaller. While experimenting with higher levels of compression, I found that WebP tends to leave less-noticeable artifacts than JPEG does.&lt;/p&gt;
&lt;p&gt;The main drawback at the time was that, among major browsers, only Chrome and Opera supported WebP, and back then, Chrome was far less popular than it is now.&lt;/p&gt;
&lt;h3 id=&#34;can-i-use-it&#34;&gt;Can I use it?&lt;/h3&gt;
&lt;p&gt;Since Apple&amp;rsquo;s iOS 14 and macOS 11 (Big Sur) became available in late 2020, the WebP image format now works in all the currently supported major operating systems and browsers: Linux, Windows, and macOS, running Chromium, Chrome, Brave, Edge, Opera, Firefox, and Safari. You can see the specifics at the ever-useful site &lt;a href=&#34;https://www.caniuse.com/webp&#34;&gt;&amp;ldquo;Can I use&amp;rdquo;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;It only took about 10 years! 😁&lt;/p&gt;
&lt;p&gt;So for you who are hosting websites, all your site visitors can see WebP images and animations except those vanishing few using Internet Explorer (now long past its end of support by Microsoft and dangerous to use), and people (or their organizations) who intentionally do not allow their older browsers and operating systems to be updated.&lt;/p&gt;
&lt;p&gt;That means you may want to continue using JPEG, PNG, and/or GIF images in places crucial for rendering your main site features, for people to be able to understand the main things your site is trying to communicate.&lt;/p&gt;
&lt;p&gt;But for any images that are less essential and primarily making it prettier, or where you don&amp;rsquo;t mind suggesting that users of old browsers upgrade to see them, WebP can now be your default image format.&lt;/p&gt;
&lt;h3 id=&#34;how-do-i-create-webp-images&#34;&gt;How do I create WebP images?&lt;/h3&gt;
&lt;p&gt;Mobile phones and digital cameras typically save JPEG, HEIF, or raw (uncompressed) images. Some stock photography collections offer WebP downloads, but many still use JPEG.&lt;/p&gt;
&lt;p&gt;So in many cases you won&amp;rsquo;t be starting with a WebP image, and you&amp;rsquo;ll convert some other image to WebP, likely after cropping, scaling, and other adjustments.&lt;/p&gt;
&lt;p&gt;GIMP (GNU Image Manipulation Program) supports WebP images since about 2017, and Adobe Photoshop does not natively but can use the &lt;a href=&#34;https://developers.google.com/speed/webp/docs/webpshop&#34;&gt;free WebPShop plugin&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The oldest way to convert images to WebP, and still very useful for batch processing or fine-tuning, is &lt;a href=&#34;https://developers.google.com/speed/webp&#34;&gt;Google&amp;rsquo;s WebP converter &amp;ldquo;cwebp&amp;rdquo;&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;cwebp-settings&#34;&gt;cwebp settings&lt;/h3&gt;
&lt;p&gt;With the power &amp;ldquo;cwebp&amp;rdquo; offers comes some complexity, but it is mostly harmless.&lt;/p&gt;
&lt;p&gt;Run this to see its many options:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cwebp -longhelp&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Or read the same thing in its &lt;a href=&#34;https://developers.google.com/speed/webp/docs/cwebp&#34;&gt;online documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;There among other things you will see the &lt;code&gt;-z&lt;/code&gt; option which activates preset features for lossless encoding, with an integer level chosen from 0 to 9 where 0 is fastest but compresses less and 9 is slowest but compresses better. Use this to replace PNG files when you want no degradation of the image at all.&lt;/p&gt;
&lt;p&gt;The documentation also shows the useful &lt;code&gt;-preset&lt;/code&gt; and &lt;code&gt;-hint&lt;/code&gt; options for lossy compression similar to what JPEG does, but better:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;-preset &amp;lt;string&amp;gt; ....... preset setting, one of:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                          default, photo, picture,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                          drawing, icon, text
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;-hint &amp;lt;string&amp;gt; ......... specify image characteristics hint,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                         one of: photo, picture or graph&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The meaning of a few of those terms, especially &amp;ldquo;photo&amp;rdquo; and &amp;ldquo;picture&amp;rdquo;, was not clear to me and not defined elsewhere in the documentation that I could see.&lt;/p&gt;
&lt;p&gt;To find that out I had to make a quick trip into the source code, and there are comments explaining each option&amp;rsquo;s use case a bit:&lt;/p&gt;
&lt;p&gt;The comments for the &lt;a href=&#34;https://chromium.googlesource.com/webm/libwebp/+/refs/heads/main/src/webp/encode.h#159&#34;&gt;&lt;code&gt;-preset&lt;/code&gt; option&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;WEBP_PRESET_PICTURE,  // digital picture, like portrait, inner shot
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;WEBP_PRESET_PHOTO,    // outdoor photograph, with natural lighting
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;WEBP_PRESET_DRAWING,  // hand or line drawing, with high-contrast details
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;WEBP_PRESET_ICON,     // small-sized colorful images
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;WEBP_PRESET_TEXT      // text-like&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And the comments for the &lt;a href=&#34;https://chromium.googlesource.com/webm/libwebp/+/refs/heads/main/src/webp/encode.h#88&#34;&gt;&lt;code&gt;-hint&lt;/code&gt; option&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;WEBP_HINT_PICTURE,    // digital picture, like portrait, inner shot
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;WEBP_HINT_PHOTO,      // outdoor photograph, with natural lighting
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;WEBP_HINT_GRAPH,      // Discrete tone image (graph, map-tile etc).&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;So in short, &amp;ldquo;cwebp&amp;rdquo; considers a &amp;ldquo;picture&amp;rdquo; to be indoors and close-up, while &amp;ldquo;photo&amp;rdquo; is outdoors and more likely with more distant focus. That&amp;rsquo;s good to know.&lt;/p&gt;
&lt;h3 id=&#34;batch-conversion&#34;&gt;Batch conversion&lt;/h3&gt;
&lt;p&gt;With that in mind, I can convert a pile of screenshots that have been collecting on my computer to refer to later. One kind of screenshots I sometimes take is of video meetings with mostly indoor views of people. I will use the &amp;ldquo;picture&amp;rdquo; preset for those.&lt;/p&gt;
&lt;p&gt;A simple &lt;code&gt;bash&lt;/code&gt; script works well to process many images in a row:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;for&lt;/span&gt; infile in Screenshot*.png
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#038&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$infile&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;base&lt;/span&gt;=&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;$(&lt;/span&gt;basename &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$infile&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt; .png&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    cwebp -preset picture -v &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$infile&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt; -o &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$base&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;.webp
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;done&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you have many images to convert to WebP and want to do several at the same time to get done faster, you can use &lt;a href=&#34;https://www.gnu.org/software/parallel/&#34;&gt;GNU parallel&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;My screenshots when converted from PNG to WebP consistently take about 3% of the original space, 33–35× smaller! And the quality looks about the same. Amazing.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;These screenshots are 2880×1800 pixels, mostly of Google Meet low-bandwidth video streams. The originals of these screenshots don&amp;rsquo;t look particularly good to begin with, with some blurriness. But exactly because of this, there is no reason for me to keep a larger high-quality original here. The much smaller WebP is fine.&lt;/p&gt;
&lt;h3 id=&#34;competitors-to-webp&#34;&gt;Competitors to WebP&lt;/h3&gt;
&lt;p&gt;Other newer image formats have also been in the works for years, chasing some of the same goals. Should we skip WebP and use one of them instead?&lt;/p&gt;
&lt;h4 id=&#34;heic&#34;&gt;HEIC&lt;/h4&gt;
&lt;p&gt;The &lt;a href=&#34;https://en.wikipedia.org/wiki/High_Efficiency_Image_File_Format#HEIC:_HEVC_in_HEIF&#34;&gt;HEIC (High-Efficiency Image Container)&lt;/a&gt; subset of the HEIF (High Efficiency Image File Format) standard uses High Efficiency Video Coding (HEVC, H.265) for storing images with a &lt;code&gt;.heic&lt;/code&gt; suffix.&lt;/p&gt;
&lt;p&gt;Compared to JPEG, HEIC offers the nice advantages of smaller file sizes for the same quality level (roughly half the size of an equivalent JPEG), and animation support (to replace GIF).&lt;/p&gt;
&lt;p&gt;On the downside, HEIC is encumbered by patents that limit its use for major commercial projects, even on devices licensed for consumer use. It is also slower to encode/​decode. And, concerning for archivists, HEIC shows severe visual damage to the entire image if part of the file is corrupted. In contrast, with corrupted JPEG files the visual damage is typically localized to particular smaller square regions, not the entire image.&lt;/p&gt;
&lt;p&gt;HEIC has been used in Apple&amp;rsquo;s operating systems since the 2017 release of the iPhone 7 and iOS 11 and macOS 10.13 (High Sierra). Support was later added to Windows 10, Android 9, and Ubuntu 20.04.&lt;/p&gt;
&lt;p&gt;As of this writing, no major browsers &lt;a href=&#34;https://caniuse.com/heif&#34;&gt;support HEIC natively&lt;/a&gt;, not even Apple&amp;rsquo;s own Safari.&lt;/p&gt;
&lt;p&gt;So for now, HEIC is primarily used by Apple to store photos more efficiently on its mobile devices.&lt;/p&gt;
&lt;h4 id=&#34;avif&#34;&gt;AVIF&lt;/h4&gt;
&lt;p&gt;The &lt;a href=&#34;https://en.wikipedia.org/wiki/AVIF&#34;&gt;AVIF (AV1 Image File Format)&lt;/a&gt; competes with HEIC and, confusingly, uses the same HEIF container file format that HEIC does. That confusion is reduced in practice by its use of the separate file extension &lt;code&gt;.avif&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.caniuse.com/avif&#34;&gt;AVIF is supported&lt;/a&gt; in current Chrome, Firefox, and Opera. Support was added to WebKit in 2021, but it still has not made its way into Safari. It also works in newer VLC, GIMP, Windows, Android, etc.&lt;/p&gt;
&lt;p&gt;Netflix has a &lt;a href=&#34;https://netflixtechblog.com/avif-for-next-generation-image-coding-b1d75675fe4&#34;&gt;very detailed blog post&lt;/a&gt; comparing AVIF to JPEG and showing AVIF&amp;rsquo;s many advantages.&lt;/p&gt;
&lt;h4 id=&#34;jpeg-xl&#34;&gt;JPEG XL&lt;/h4&gt;
&lt;p&gt;A semi-compatible successor to JPEG has long been in the works, and JPEG XL seems likely to eventually fill that role.&lt;/p&gt;
&lt;p&gt;Whereas the other new image formats mentioned above usually lose some quality when recompressing JPEG and other images that were already lossy-compressed, according to the &lt;a href=&#34;https://jpeg.org/jpegxl/&#34;&gt;Joint Photographic Experts Group (JPEG)&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Existing JPEG files can be losslessly transcoded to JPEG XL, significantly reducing their size.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;It has been reported that JPEG XL is expected to become available in its final standard form in 2022, and support is already available in preliminary form in some software (see the &lt;a href=&#34;https://en.wikipedia.org/wiki/JPEG_XL&#34;&gt;Wikipedia JPEG XL article&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;That of course means that for now, no major browsers &lt;a href=&#34;https://caniuse.com/jpegxl&#34;&gt;support JPEG XL natively&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;use-webp-now&#34;&gt;Use WebP now&lt;/h3&gt;
&lt;p&gt;So all the other new options are not yet usable for general web images. WebP is the current obvious choice, whether you want a lossy replacement for JPEG photos, a lossless replacement for PNG images, or a replacement for GIF animations.&lt;/p&gt;
&lt;p&gt;Our developers have set up automatic server-side app conversion of high-quality PNG originals to WebP or JPEG on the fly, with the image size dependent on the browser viewport size. And we have worked with Cloudinary, Cloudflare, and other CDNs to use their image conversion services. We are available to help with your projects too.&lt;/p&gt;
&lt;h3 id=&#34;reference&#34;&gt;Reference&lt;/h3&gt;
&lt;p&gt;The Mozilla Development Network (MDN) has excellent documentation of web image format details, filename suffixes, and support in the major browsers in its &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Image_types&#34;&gt;Image file type and format guide&lt;/a&gt;.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Web Projects for a Rainy Day</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2020/03/web-projects-for-rainy-day/"/>
      <id>https://www.endpointdev.com/blog/2020/03/web-projects-for-rainy-day/</id>
      <published>2020-03-25T00:00:00+00:00</published>
      <author>
        <name>Elizabeth Garrett Christensen</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2020/03/web-projects-for-rainy-day/image-0.jpg&#34; alt=&#34;raindrops on a plant&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.flickr.com/photos/yellowstonenps/32984582893/&#34;&gt;Image&lt;/a&gt; by &lt;a href=&#34;https://www.flickr.com/photos/yellowstonenps/&#34;&gt;Yellowstone NPS on Flickr&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;With the COVID-19 quarantine disrupting life for many of us, I thought I’d put together a list of things you can do with your website on a rainy day. These are things to keep your business moving even if you’re at home and some of your projects are stuck waiting on things to reopen. If you’re looking for some useful things to do to fill your days over the next few months, this post is for you!&lt;/p&gt;
&lt;h3 id=&#34;major-version-updates&#34;&gt;Major Version Updates&lt;/h3&gt;
&lt;p&gt;Make a list of your entire stack, from OS to database to development frameworks. Note the current version and research the current supported versions. I find Wikipedia pages to be fairly reliable for this (e.g. &lt;a href=&#34;https://en.wikipedia.org/wiki/CentOS&#34;&gt;en.wikipedia.org/wiki/CentOS&lt;/a&gt;). Ok, so what things need to be updated, or will need to be in the next year? Start on those now and use some downtime to get ahead of your updates.&lt;/p&gt;
&lt;h4 id=&#34;sample-of-a-clients-stack-review&#34;&gt;Sample of a client’s stack review&lt;/h4&gt;
&lt;div class=&#34;table-scroll&#34;&gt;
  &lt;table&gt;
    &lt;thead&gt;
      &lt;td&gt;Software&lt;/td&gt;
      &lt;td&gt;Purpose&lt;/td&gt;
      &lt;td&gt;Our version&lt;/td&gt;
      &lt;td&gt;Release date&lt;/td&gt;
      &lt;td&gt;End of support&lt;/td&gt;
      &lt;td&gt;Next update&lt;/td&gt;
      &lt;td&gt;Newest version&lt;/td&gt;
      &lt;td&gt;Notes&lt;/td&gt;
    &lt;/thead&gt;
    &lt;tr&gt;
      &lt;td&gt;CentOS&lt;/td&gt;
      &lt;td&gt;OS for e-commerce server&lt;/td&gt;
      &lt;td&gt;7&lt;/td&gt;
      &lt;td&gt;July 2014&lt;/td&gt;
      &lt;td&gt;June 2024&lt;/td&gt;
      &lt;td&gt;Not imminent&lt;/td&gt;
      &lt;td&gt;8&lt;/td&gt;
      &lt;td&gt;&lt;a href=&#34;https://wiki.centos.org/About/Product&#34;&gt;https://wiki.centos.org/About/Product&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Nginx&lt;/td&gt;
      &lt;td&gt;Web server&lt;/td&gt;
      &lt;td&gt;1.16.0&lt;/td&gt;
      &lt;td&gt;March 2020&lt;/td&gt;
      &lt;td&gt;Unclear&lt;/td&gt;
      &lt;td&gt;Not imminent&lt;/td&gt;
      &lt;td&gt;1.16.1&lt;/td&gt;
      &lt;td&gt;&lt;a href=&#34;https://nginx.org/&#34;&gt;https://nginx.org/&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;PostgreSQL&lt;/td&gt;
      &lt;td&gt;Database server&lt;/td&gt;
      &lt;td&gt;9.5.20&lt;/td&gt;
      &lt;td&gt;January 2016&lt;/td&gt;
      &lt;td&gt;Feb 2020&lt;/td&gt;
      &lt;td&gt;Medium term, to version 11&lt;/td&gt;
      &lt;td&gt;12&lt;/td&gt;
      &lt;td&gt;&lt;a href=&#34;https://www.postgresql.org/support/versioning/&#34;&gt;https://www.postgresql.org/support/versioning/&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Rails&lt;/td&gt;
      &lt;td&gt;App framework for store&lt;/td&gt;
      &lt;td&gt;5.1&lt;/td&gt;
      &lt;td&gt;February 2017&lt;/td&gt;
      &lt;td&gt;Current&lt;/td&gt;
      &lt;td&gt;Long Term, to version 6&lt;/td&gt;
      &lt;td&gt;6&lt;/td&gt;
      &lt;td&gt;&lt;a href=&#34;https://rubygems.org/gems/rails/versions&gt;https://rubygems.org/gems/rails/versions&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Spree&lt;/td&gt;
      &lt;td&gt;Ecommerce and admin gem&lt;/td&gt;
      &lt;td&gt;3.3&lt;/td&gt;
      &lt;td&gt;April 2017&lt;/td&gt;
      &lt;td&gt;Current&lt;/td&gt;
      &lt;td&gt;Long Term, to version 4&lt;/td&gt;
      &lt;td&gt;4&lt;/td&gt;
      &lt;td&gt;&lt;a href=&#34;https://rubygems.org/gems/spree/versions&#34;&gt;https://rubygems.org/gems/spree/versions&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Elasticsearch&lt;/td&gt;
      &lt;td&gt;Search platform for product import/search&lt;/td&gt;
      &lt;td&gt;5.6.x&lt;/td&gt;
      &lt;td&gt;September 2017&lt;/td&gt;
      &lt;td&gt;March 2019&lt;/td&gt;
      &lt;td&gt;Immediate, to version 6.8&lt;/td&gt;
      &lt;td&gt;7.4&lt;/td&gt;
      &lt;td&gt;&lt;a href=&#34;https://www.elastic.co/support/eol&#34;&gt;https://www.elastic.co/support/eol&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;WordPress&lt;/td&gt;
      &lt;td&gt;Info site&lt;/td&gt;
      &lt;td&gt;5.2.3&lt;/td&gt;
      &lt;td&gt;September 2019&lt;/td&gt;
      &lt;td&gt;Unclear&lt;/td&gt;
      &lt;td&gt;5.2.4 shipped recently&lt;/td&gt;
      &lt;td&gt;5.2&lt;/td&gt;
      &lt;td&gt;&lt;a href=&#34;https://codex.wordpress.org/Supported_Versions&#34;&gt;https://codex.wordpress.org/Supported_Versions&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/table&gt;
&lt;/div&gt;
&lt;h3 id=&#34;content-cleanup--seo-review&#34;&gt;Content Cleanup &amp;amp; SEO Review&lt;/h3&gt;
&lt;p&gt;Everyone’s website gets cluttered with outdated content. Take a look at your pages, review, and update what needs to be changed. Pay attention to search engine optimization (SEO) concerns as you go through it. Make sure your content has headers, accurate keywords, and good meta-descriptions. Research SEO best practices if you need a refresher.&lt;/p&gt;
&lt;p&gt;Nowadays, reducing repeated content has huge benefits for SEO so we recommend any content review includes a review of duplication. If you have a small site, you can go through your content and SEO manually. Larger projects can utilize tools on the market such as &lt;a href=&#34;https://www.siteliner.com/&#34;&gt;Siteliner&lt;/a&gt; or &lt;a href=&#34;https://wordpress.org/plugins/wp-optimize/&#34;&gt;WPOptimize&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;While you’re taking a dive into content, don’t forget to review your Google Analytics and understand what content is being used and what isn’t. Google has added many new features to Analytics and Ads, so it’s a good idea to refresh yourself on the updated documentation and new features.&lt;/p&gt;
&lt;h3 id=&#34;reporting&#34;&gt;Reporting&lt;/h3&gt;
&lt;p&gt;A lot of clients with big ecommerce data sets or other applications that collect data benefit from a separate reporting or business analytics tool. A rainy day can be a good time to think about what reports you want on last year’s business, what data will help you plan for the future. End Point has worked with a few different reporting tools that easily add on to your database, like &lt;a href=&#34;https://www.hitachivantara.com/en-us/products/data-management-analytics/pentaho.html&#34;&gt;Pentaho&lt;/a&gt; or &lt;a href=&#34;https://www.jaspersoft.com/reporting-software&#34;&gt;Jasper&lt;/a&gt; and those can be really useful.&lt;/p&gt;
&lt;h3 id=&#34;documentation&#34;&gt;Documentation&lt;/h3&gt;
&lt;p&gt;I wouldn’t be a good project manager if I didn’t throw this one in the list. Documentation is so, so important, yet really we can always do more. End Point uses a few different tools, including wikis running &lt;a href=&#34;https://www.mediawiki.org/wiki/MediaWiki&#34;&gt;MediaWiki&lt;/a&gt; and Google Docs, for keeping track of project details. Now’s a good time to set up a nice documentation system or do a big review and make sure everything is updated and back in order. Maybe dream of a vacation you &lt;em&gt;might&lt;/em&gt; be able to take when this is over and make sure everything’s ready for you to do that.&lt;/p&gt;
&lt;h3 id=&#34;disaster-recovery-tests&#34;&gt;Disaster Recovery Tests&lt;/h3&gt;
&lt;p&gt;For anyone with business-critical infrastructure, you need to ensure you know how to get everything back up and running in the case of a major failure, either with on-premises or cloud hosting. Now’s a good time to clarify with your hosting vendor things like: What are your backups like? What is your disaster recovery plan? What is the timeline for recovering the application in the event of a major failure? If you can, take time to do a simulation and make sure all the pieces are there if they need to be. Simply said, we also need to test our backups in order to ensure that they actually work.&lt;/p&gt;
&lt;h3 id=&#34;redesign&#34;&gt;Redesign&lt;/h3&gt;
&lt;p&gt;If you’ve been meaning to refresh your website, a rainy day is prime time to do it. Designers and developers are looking for projects and you’ll have extra time on your hands to oversee the process, spend time reviewing and testing, and get things done just the way you want.&lt;/p&gt;
&lt;h3 id=&#34;automated-testing&#34;&gt;Automated Testing&lt;/h3&gt;
&lt;p&gt;Good developers want an automated test suite as part of their application. Not all applications were built with this from the beginning and many didn’t have the budget or time to get it done. With extra time on your hands, this can be a great time to start building your test suite or to improve the coverage of your existing one.&lt;/p&gt;
&lt;p&gt;Unit tests in particular are a good place to start. Unit tests are great not only because they help validate software correctness and protect against regressions, but also because they require a well-factored and modular system. This means that, while writing your unit tests, you will often be forced to go back to your application’s code base and refactor it to make it testable, to make it better. Investing in creating a solid unit test suite is a great bang for your buck. You can also look at implementing continuous integration—having a pipeline to let multiple developers deploy code throughout the day and configure your automated tests into the workflow.&lt;/p&gt;
&lt;h3 id=&#34;versioning--deployment-tools&#34;&gt;Versioning &amp;amp; Deployment Tools&lt;/h3&gt;
&lt;p&gt;When you’re cleaning house, take a look at your Git version control repository and make sure everything important is in there. We have a few projects that have a main project in Git, but sometimes the smaller projects and one-offs can go astray. This is a good time to get everything organized into one repository, or make sure external repositories are connected and integrated.&lt;/p&gt;
&lt;p&gt;Automated DevOps deployment tools can also be nice to work on. Tools like Ansible and Chef can take a lot of time to set up and test, but they have some great time-saving and accuracy advantages down the line. Our in-house security experts also recommend tools like AIDE and OSSEC which automate monitoring file changes daily.&lt;/p&gt;
&lt;h3 id=&#34;security-audit-and-monitoring&#34;&gt;Security Audit and Monitoring&lt;/h3&gt;
&lt;p&gt;Taking a few days to review your personal security and that of your application is something you should do regularly and now’s a good time to plan for it. Charlie’s got &lt;a href=&#34;/blog/2020/02/end-point-security-tips/&#34;&gt;a great security post&lt;/a&gt; that’s a good top-level review. For application security, End Point uses some tools for vulnerability scanning. We also have a checklist of basic security items that include password handling, PII data, and other common security holes. For certain projects/clients we must also take HIPAA or PCI DSS compliance into account. Also, don’t neglect to review your TLS status and ensure that web applications run on TLS 1.2 and are TLS 1.3 ready. This also may relate to the underlying operating systems—whether they are able to support the latest TLS version natively.&lt;/p&gt;
&lt;h3 id=&#34;optimization-and-performance&#34;&gt;Optimization and Performance:&lt;/h3&gt;
&lt;p&gt;Most of the time new features have higher priority than improving the performance of an existing system. It could be the right time to review core functionalities and list out the areas that need improvement in serving a better experience to customers by optimization. The areas can be focused on optimizing code, database queries, image size, data compression over network, adding cache, CDN, and so on. We’ve been moving quite a few clients to the &lt;a href=&#34;https://www.cloudflare.com/&#34;&gt;Cloudflare&lt;/a&gt; DNS and CDN service and we’ve been really happy with it. Optimization work will definitely influence the customer retention rate which helps to increase profitability long term.&lt;/p&gt;
&lt;h3 id=&#34;refactoring&#34;&gt;Refactoring&lt;/h3&gt;
&lt;p&gt;Along the same lines as optimization, code refactoring can have long term gains in performance and ease of future development. Think of this like house cleaning. It is always easier to find any item in the house when things are arranged in an orderly manner. Similarly, the organized and clean code base will play a vital role in future code changes and development, helping to reduce the chances of unexpected bugs, save time making changes at one place and improving code readability. Disciplined refactoring delivers readable, reusable, non-redundant code. Refactoring can be applied to your databases and user interfaces as well.&lt;/p&gt;
&lt;p&gt;Want to get started on some background projects for your website? &lt;a href=&#34;/contact/&#34;&gt;Talk to us today&lt;/a&gt;.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Decreasing your website load time</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2020/01/decreasing-website-load-time/"/>
      <id>https://www.endpointdev.com/blog/2020/01/decreasing-website-load-time/</id>
      <published>2020-01-07T00:00:00+00:00</published>
      <author>
        <name>Juan Pablo Ventoso</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2020/01/decreasing-website-load-time/mobile-desktop-browsing.jpg&#34; alt=&#34;Decreasing our website load time&#34; /&gt; &lt;a href=&#34;https://www.flickr.com/photos/johanl/6798184016/&#34;&gt;Photo&lt;/a&gt; by &lt;a href=&#34;https://www.flickr.com/photos/johanl/&#34;&gt;Johan Larsson&lt;/a&gt;, used under &lt;a href=&#34;https://creativecommons.org/licenses/by/2.0/&#34;&gt;CC BY 2.0&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We live in a competitive world, and the web is no different. Improving latency issues is crucial to any Search Engine Optimization (SEO) strategy, increasing the website’s ranking and organic traffic (visitors from search engines) as a result.&lt;/p&gt;
&lt;p&gt;There are many factors that can lead to a faster response time, including optimization of your hosting plan, server proximity to your main traffic source, or utilization of a Content Distribution Network (CDN) if you are expecting visitors on an international level. Some of these solutions and many others can be implemented with only a couple hours of coding.&lt;/p&gt;
&lt;h3 id=&#34;inline-styles-and-scripts-for-the-topmost-content&#34;&gt;Inline styles and scripts for the topmost content&lt;/h3&gt;
&lt;p&gt;Nobody enjoys waiting for long load times. When opening a Google search link, being met with a blank page or a loading GIF for several seconds can seem agonizing. That’s why optimizing the initial rendering of your page is crucial.&lt;/p&gt;
&lt;p&gt;The content that immediately appears to the user without the need to scroll down is referred to as “above-the-fold”. This is where your optimization efforts should be aimed. So here’s a plan to load and display as quickly as possible:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;First, differentiate the critical styles and scripts you need to render the topmost content, and separate them from the rest of our stylesheet and external script references.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Then, &lt;a href=&#34;https://www.imperva.com/learn/performance/minification/&#34;&gt;minify&lt;/a&gt; the separated &lt;a href=&#34;https://csscompressor.com/&#34;&gt;styles&lt;/a&gt; and &lt;a href=&#34;https://jscompress.com/&#34;&gt;scripts&lt;/a&gt;, and insert them directly on our page template, right before the closing &lt;code&gt;&amp;lt;/head&amp;gt;&lt;/code&gt; tag.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Finally, take the stylesheet and scripts link references from the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; tag (where it’s usually located) and move them to the end of the above-the-fold content.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now, the user won’t have to wait until all references are loaded before seeing content. &lt;b&gt;Tip&lt;/b&gt;: Remember to use the &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-async&#34;&gt;async&lt;/a&gt; tag on scripts whenever possible.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;example.html:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;head&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;style&lt;/span&gt;&amp;gt;{&lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;above-the-fold&lt;/span&gt; &lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;minified&lt;/span&gt; &lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;inline&lt;/span&gt; &lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;styles&lt;/span&gt; &lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;goes&lt;/span&gt; &lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;here&lt;/span&gt;}&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;style&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;script&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;type&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;text/javascript&amp;#34;&lt;/span&gt;&amp;gt;{above-the-fold critical scripts goes here}&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;script&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;head&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;body&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;div&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;class&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;above-the-fold-content&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;div&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;link&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;rel&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;stylesheet&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;href&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;{below-the-fold minified stylesheet reference goes here}&amp;#34;&lt;/span&gt; /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;script&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;async&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;src&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;{below-the-fold minified javascript reference goes here}&amp;#34;&lt;/span&gt; /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;div&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;class&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;below-the-fold-content&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;div&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;body&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;deferred-loading-of-ads&#34;&gt;Deferred loading of ads&lt;/h3&gt;
&lt;p&gt;If you’re monetizing your website through Google AdSense or another ad agency that uses scripts to load ads, consider loading ads after the content is fully rendered. This may have a small impact on your revenue, but will improve the user’s experience while optimizing the load speed.&lt;/p&gt;
&lt;p&gt;Although there are several ways to achieve this, a technique I have successfully used on many websites is removing all of the script references to Google AdSense until your page is fully loaded. A short delay can be added in order to allow some browsing time before showing ads.&lt;/p&gt;
&lt;p&gt;Remove script references, the comment, and extra spaces from your original ad code, to convert it from something like this&amp;hellip;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;script&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;async&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;src&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;script&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&amp;lt;!-- Your ad name --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;ins&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;class&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;adsbygoogle&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#369&#34;&gt;style&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;display:inline-block;width:728px;height:90px&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#369&#34;&gt;data-ad-client&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;ca-pub-XXXXXXXXXXXXXXXXX&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#369&#34;&gt;data-ad-slot&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;XXXXXXXXX&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;ins&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;script&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     (adsbygoogle = &lt;span style=&#34;color:#038&#34;&gt;window&lt;/span&gt;.adsbygoogle || []).push({});
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;script&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&amp;hellip; to something like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;ins&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;class&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;adsbygoogle&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;style&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;display:inline-block;width:728px;height:90px&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;data-ad-client&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;ca-pub-XXXXXXXXXXXXXXXXX&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;data-ad-slot&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;XXXXXXXXX&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;ins&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;A lot shorter, isn’t it? This will create an empty slot in which the ad will be displayed after the page is fully rendered. To accomplish that, a new script like the one below must be added (assuming jQuery is present on the website):&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;async-ads.js:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// Create a script reference
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; addScript(src, &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;async&lt;/span&gt;, callback) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;var&lt;/span&gt; js = &lt;span style=&#34;color:#038&#34;&gt;document&lt;/span&gt;.createElement(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;script&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    js.type = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;text/javascript&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;async&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        js.&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;async&lt;/span&gt; = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;true&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; (callback)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        js.onload = callback;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    js.src = src;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#038&#34;&gt;document&lt;/span&gt;.body.appendChild(js);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// Called when document is ready
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;$(&lt;span style=&#34;color:#038&#34;&gt;document&lt;/span&gt;).ready(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// Wait for one second to ensure the user started browsing
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    setTimeout(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (adsbygoogle = &lt;span style=&#34;color:#038&#34;&gt;window&lt;/span&gt;.adsbygoogle || []);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        $(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;ins.adsbygoogle&amp;#34;&lt;/span&gt;).each(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            $(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&amp;lt;script&amp;gt;(adsbygoogle = window.adsbygoogle || []).push({})&amp;lt;/script&amp;gt;&amp;#34;&lt;/span&gt;).insertAfter($(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;this&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        addScript(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;true&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }, &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1000&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This code will wait for one second once the document is ready, and then leave instructions for Google to push a new ad for each slot. Finally, the AdSense external script will be loaded so that Google will read the instructions and start filling all the slots with ads.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Tip&lt;/b&gt;: Enabling balancing from your AdSense dashboard may improve the average load speed as well as the user’s experience since ads will not be shown when the expected revenue is deprecable. And if you’re still on the fence about showing fewer ads, &lt;a href=&#34;https://fatstacksblog.com/adsense-ad-balance-experiment/&#34;&gt;try out an experiment&lt;/a&gt; like I did. A balance of 50% worked well in my case, but the right balance will depend on your niche and website characteristics.&lt;/p&gt;
&lt;h3 id=&#34;lazy-load-for-images&#34;&gt;Lazy load for images&lt;/h3&gt;
&lt;p&gt;Because the user will most likely spend the majority of the visit reading above-the-fold content (and may even leave before scrolling at all), loading all images from content below-the-fold at first is impractical. Implementing a custom lazy-loading script (also referred to as deferred-loading or loading-on-scroll) for images can be an easy process. Even though changes to the backend would be likely, the concept of this approach is simple:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Replacing the &lt;code&gt;src&lt;/code&gt; attributes from all images that will have lazy loading with a custom attribute such as &lt;code&gt;data-src&lt;/code&gt; (this part will probably require backend changes) and set a custom class for them, like &lt;code&gt;lazy&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Creating a script that will copy the &lt;code&gt;data-src&lt;/code&gt; content into the &lt;code&gt;src&lt;/code&gt; attribute as we scroll through the page.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;lazy-load.js:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;;(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt;($) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    $.fn.lazy = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt;(threshold, callback) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;var&lt;/span&gt; $w = $(&lt;span style=&#34;color:#038&#34;&gt;window&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        th = threshold || &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        attrib = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;data-src&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        images = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;this&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        loaded;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;this&lt;/span&gt;.one(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;lazy&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;var&lt;/span&gt; source = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;this&lt;/span&gt;.getAttribute(attrib);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            source = source || &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;this&lt;/span&gt;.getAttribute(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;data-src&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; (source) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;this&lt;/span&gt;.setAttribute(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;src&amp;#34;&lt;/span&gt;, source);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;typeof&lt;/span&gt; callback === &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;function&amp;#34;&lt;/span&gt;) callback.call(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;this&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; lazy() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;var&lt;/span&gt; inview = images.filter(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;var&lt;/span&gt; $e = $(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;this&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; ($e.is(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;:hidden&amp;#34;&lt;/span&gt;)) &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;var&lt;/span&gt; wt = $w.scrollTop(),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                wb = wt + $w.height(),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                et = $e.offset().top,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                eb = et + $e.height();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; eb &amp;gt;= wt - th &amp;amp;&amp;amp; et &amp;lt;= wb + th;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            loaded = inview.trigger(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;lazy&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            images = images.not(loaded);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        $w.scroll(lazy);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        $w.resize(lazy);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        lazy();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;this&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;})(&lt;span style=&#34;color:#038&#34;&gt;window&lt;/span&gt;.jQuery);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$(&lt;span style=&#34;color:#038&#34;&gt;document&lt;/span&gt;).ready(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    $(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;.lazy&amp;#39;&lt;/span&gt;).each(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; () {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        $(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;this&lt;/span&gt;).lazy(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        $(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;this&lt;/span&gt;).load(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;this&lt;/span&gt;.style.opacity = &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// Set the correct attribute when printing
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;var&lt;/span&gt; beforePrint = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    $(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;img.lazy&amp;#34;&lt;/span&gt;).each(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        $(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;this&lt;/span&gt;).trigger(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;lazy&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;this&lt;/span&gt;.style.opacity = &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#038&#34;&gt;window&lt;/span&gt;.matchMedia) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;var&lt;/span&gt; mediaQueryList = &lt;span style=&#34;color:#038&#34;&gt;window&lt;/span&gt;.matchMedia(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;print&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    mediaQueryList.addListener(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt;(mql) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; (mql.matches)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            beforePrint();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;window&lt;/span&gt;.onbeforeprint = beforePrint;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This script will search for all &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tags with class &lt;code&gt;lazy&lt;/code&gt;, and change the &lt;code&gt;data-src&lt;/code&gt; attribute to the &lt;code&gt;src&lt;/code&gt; attribute once the image becomes visible due to scrolling. It also includes some additional logic to set the &lt;code&gt;src&lt;/code&gt; attribute before printing the page.&lt;/p&gt;
&lt;h3 id=&#34;server-side-caching&#34;&gt;Server-side caching&lt;/h3&gt;
&lt;p&gt;Instead of performing all the backend rendering calculations every time, server-side caching allows you to output the same content to the clients over a period of time from a temporary copy of the response. This not only results in a decreased response time but also saves some resources on the server.&lt;/p&gt;
&lt;p&gt;There are several ways to enable server-side caching, depending on factors such as the backend language and hosting platform (e.g. Windows/IIS vs. Linux/Apache), among other things. For this example, we will use ASP.NET (C#) since I’m mostly a Windows user.&lt;/p&gt;
&lt;p&gt;The best and most efficient way to do this is by adding a declaration in the top of our ASP.NET page:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;%&lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;@&lt;/span&gt; OutputCache Duration=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;10&amp;#34;&lt;/span&gt; VaryByParam=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;id;date&amp;#34;&lt;/span&gt; %&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This declaration is telling the compiler that we want to cache the output from the server for 10 minutes, and we will save different versions based on the &lt;code&gt;id&lt;/code&gt; and &lt;code&gt;date&lt;/code&gt; URL parameters. So pages like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;https://www.your-url.com/cached-page/?id=1&amp;amp;date=2020-01-01&lt;/li&gt;
&lt;li&gt;https://www.your-url.com/cached-page/?id=2&amp;amp;date=2020-01-01&lt;/li&gt;
&lt;li&gt;https://www.your-url.com/cached-page/?id=2&amp;amp;date=2020-02-01&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;will be saved and then served from different cache copies. If we only set the &lt;code&gt;id&lt;/code&gt; parameter as a source for caching, pages with different dates will be served from the same cache source (this can be useful as the &lt;code&gt;date&lt;/code&gt; parameter is only evaluated on frontend scripts and ignored in the backend).&lt;/p&gt;
&lt;p&gt;There are other configurations in ASP.NET to set our output cache policy. The output can be set to be based on the browser, the request headers, or even custom strings. &lt;a href=&#34;https://www.c-sharpcorner.com/UploadFile/chinnasrihari/Asp-Net-mvc-framework-server-side-html-caching-techniques/&#34;&gt;This page&lt;/a&gt; has more useful information on this subject.&lt;/p&gt;
&lt;h3 id=&#34;gzip-compression&#34;&gt;GZip compression&lt;/h3&gt;
&lt;p&gt;GZip compression—when the client supports it—allows compressing the response before sending it over the network. In this way, more than 70% of the bandwidth can be saved when loading the website. Enabling GZip compression for dynamic and static content on a Windows Server with IIS is simple: Just go to the “Compression” section on the IIS Manager and check the options “Enable dynamic/static content compression”.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/01/decreasing-website-load-time/enabling-compression-iis.jpg&#34; alt=&#34;Enabling compression in IIS&#34;&gt;&lt;/p&gt;
&lt;p&gt;However, if you are running an ASP.NET MVC/WebForms website, this won’t be enough. For all backend responses to be compressed before sending them to the client, some custom code will also need to be added to the &lt;code&gt;global.asax&lt;/code&gt; file in the website root:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;global.asax:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;%&lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;@&lt;/span&gt; Application Language=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;C#&amp;#34;&lt;/span&gt; %&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;script runat=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;server&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;void&lt;/span&gt; Application_PreRequestHandlerExecute(&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;object&lt;/span&gt; sender, EventArgs e)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        HttpApplication app = sender &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;as&lt;/span&gt; HttpApplication;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;string&lt;/span&gt; acceptEncoding = app.Request.Headers[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Accept-Encoding&amp;#34;&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        System.IO.Stream prevUncompressedStream = app.Response.Filter;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; (app.Context.CurrentHandler == &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;null&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; (!(app.Context.CurrentHandler &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;is&lt;/span&gt; System.Web.UI.Page ||
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            app.Context.CurrentHandler.GetType().Name == &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;SyncSessionlessHandler&amp;#34;&lt;/span&gt;) ||
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            app.Request[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;HTTP_X_MICROSOFTAJAX&amp;#34;&lt;/span&gt;] != &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;null&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; (acceptEncoding == &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;null&lt;/span&gt; || acceptEncoding.Length == &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; (Request.ServerVariables[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;SCRIPT_NAME&amp;#34;&lt;/span&gt;].ToLower().Contains(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;.axd&amp;#34;&lt;/span&gt;)) &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; (Request.ServerVariables[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;SCRIPT_NAME&amp;#34;&lt;/span&gt;].ToLower().Contains(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;.js&amp;#34;&lt;/span&gt;)) &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; (Request.QueryString.ToString().Contains(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;_TSM_HiddenField_&amp;#34;&lt;/span&gt;)) &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        acceptEncoding = acceptEncoding.ToLower();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; (acceptEncoding.Contains(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;deflate&amp;#34;&lt;/span&gt;) || acceptEncoding == &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;*&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            app.Response.Filter = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; System.IO.Compression.DeflateStream(prevUncompressedStream,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                System.IO.Compression.CompressionMode.Compress);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            app.Response.AppendHeader(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Content-Encoding&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;deflate&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;else&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; (acceptEncoding.Contains(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;gzip&amp;#34;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            app.Response.Filter = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; System.IO.Compression.GZipStream(prevUncompressedStream,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                System.IO.Compression.CompressionMode.Compress);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            app.Response.AppendHeader(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Content-Encoding&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;gzip&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To make sure our code is working properly, an external tool like &lt;a href=&#34;https://www.giftofspeed.com/gzip-test/&#34;&gt;this&lt;/a&gt; will inform you if GZip is enabled or not.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/01/decreasing-website-load-time/gzip-compression-enabled.jpg&#34; alt=&#34;It works!&#34;&gt;&lt;/p&gt;
&lt;h3 id=&#34;summary&#34;&gt;Summary&lt;/h3&gt;
&lt;p&gt;While there are many ways of decreasing the load time of a website, most are common and expensive. However, with a few minor tweaks, we can offer a better user experience in addition to improve our position in the search engine results. Every bit of optimization counts towards the goal with SEO. Load time is a very important factor (to both the developer and the user), especially on mobile platforms where users expect to get what they want instantly.&lt;/p&gt;
&lt;p&gt;The image below is a Google Analytics report from one of my websites where, over several months, I implemented most of these formulas. A month ago, I made the latest change of deferring ad loading, which had an observable impact on the average loading speed of the page:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/01/decreasing-website-load-time/analytics-average-page-load.jpg&#34; alt=&#34;Report from Google Analytics&#34;&gt;&lt;/p&gt;
&lt;p&gt;Do you have any other page load optimization techniques? &lt;b&gt;Leave a comment below!&lt;/b&gt;&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Extensible Binary Encoding with CBOR</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2019/03/extensible-binary-encoding-with-cbor/"/>
      <id>https://www.endpointdev.com/blog/2019/03/extensible-binary-encoding-with-cbor/</id>
      <published>2019-03-18T00:00:00+00:00</published>
      <author>
        <name>Matt Vollrath</name>
      </author>
      <content type="html">
        &lt;img src=&#34;/blog/2019/03/extensible-binary-encoding-with-cbor/convert-crop.png&#34; alt=&#34;illustration of man converting something in a machine&#34; /&gt;
&lt;p&gt;CBOR is a relatively new IETF draft standard extensible binary data format. Compared to similar formats like MessagePack and BSON, CBOR was developed from the ground up with clear goals:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;unambiguous encoding of most common data formats from Internet standards&lt;/li&gt;
&lt;li&gt;code compactness for encoder and decoder&lt;/li&gt;
&lt;li&gt;no schema description needed&lt;/li&gt;
&lt;li&gt;reasonably compact serialization&lt;/li&gt;
&lt;li&gt;applicability to constrained and unconstrained applications&lt;/li&gt;
&lt;li&gt;good JSON conversion&lt;/li&gt;
&lt;li&gt;extensibility&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;—&lt;a href=&#34;https://tools.ietf.org/html/rfc7049&#34;&gt;RFC 7049&lt;/a&gt; Appendix E, Copyright © 2013 IETF Trust, Bormann &amp;amp; Hoffman&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;In the context of data storage and messaging, most developers can relate to CBOR as a binary drop-in replacement for JSON. While CBOR doesn’t share the human readability of JSON, it can efficiently and unambiguously encode types of data that JSON struggles with. CBOR can also be extended with tags to optimize serialization beyond its standard primitives.&lt;/p&gt;
&lt;h3 id=&#34;encoding-binary-data&#34;&gt;Encoding Binary Data&lt;/h3&gt;
&lt;p&gt;JSON is a ubiquitous data format for web and beyond, for many good reasons, but encoding blobs of binary data is an area where JSON falters. For example, if you are designing a JSON protocol to wrap the storage or transfer of arbitrary objects, your options are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Require that all input data can be represented as JSON. When possible this is potentially a reasonable solution, but limits the types of data that can be encoded. Notable exceptions include most popular image encodings, excluding SVG.&lt;/li&gt;
&lt;li&gt;Base64 encode any binary data values to a string. This can encode any binary data, but increases the size of the data by a minimum of 1/3, incurs encoding and decoding cost, and requires magic to indicate that the string is Base64 encoded.&lt;/li&gt;
&lt;li&gt;Encode the bytes as an array of numbers or a hex string. These are probably not things you should do, but it seemed worth mentioning that these techniques increase the size of the data by anywhere from 2x to 5x and also require magic to indicate that the data is really binary.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With CBOR, binary blobs of any length are supported out of the box and are encoded 1:1. Encoding and decoding CBOR byte strings is extremely fast, even in higher-level languages. In the case of JavaScript, CBOR byte strings can be transcoded to and from the very fast and efficient &lt;code&gt;Uint8Array&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For example, let’s try encoding a simple object with two fields: “name” and “data”. An abstract view of this object:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;name: &amp;#34;Strawberry Pie&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;data: &amp;lt;00 01 02 03 04 05 06 07 08 09&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;A C struct of this raw data would be absolute minimum of 24 bytes.&lt;/p&gt;
&lt;p&gt;If you Base64 the data into JSON, your output might look like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Strawberry Pie&amp;#34;&lt;/span&gt;,&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;data&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;AAECAwQFBgcICQ==&amp;#34;&lt;/span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Length: 51 bytes.&lt;/p&gt;
&lt;p&gt;This illustrates the “magic” problem with Base64 encoding binary data in JSON. Unless you have a JSON schema or special protocol fields, the decoder has no indication that the data in this object needs to be Base64 decoded, because it looks like a string. It is not self-describing.&lt;/p&gt;
&lt;p&gt;The CBOR representation of the same input data, as a hex string:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;a2646e616d656e5374726177626572727920506965696a7065675f646174614a00010203040506070809&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Whoa, there. What do all these numbers and letters mean? I miss my JSON! The Node &lt;a href=&#34;https://www.npmjs.com/package/cbor&#34;&gt;cbor&lt;/a&gt; package has a handy &lt;a href=&#34;https://github.com/hildjj/node-cbor/blob/master/bin/cbor2comment&#34;&gt;&lt;code&gt;cbor2comment&lt;/code&gt; tool&lt;/a&gt; to annotate this hex string for us.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  a2                -- Map, 2 pairs
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    64              -- String, length: 4
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      6e616d65      -- {Key:0}, &amp;#34;name&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    6e              -- String, length: 14
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      5374726177626572727920506965 -- {Val:0}, &amp;#34;Strawberry Pie&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    64              -- String, length: 4
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      64617461 -- {Key:1}, &amp;#34;data&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    4a              -- Bytes, length: 10
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      00010203040506070809 -- {Val:1}, 00010203040506070809&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Length: 35 bytes.&lt;/p&gt;
&lt;p&gt;Now let’s &lt;a href=&#34;https://github.com/mvollrath/cbor-bench&#34;&gt;benchmark JSON against CBOR using real JPEG data&lt;/a&gt;. For this test I used a &lt;a href=&#34;https://github.com/mvollrath/cbor-js/tree/fast_byte_array_encoding&#34;&gt;modified version of cbor-js&lt;/a&gt;, a library compatible with both Node and browsers, and encoded a 910,226 byte JPEG of strawberry rhubarb pie.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;|                           | JSON      | CBOR    |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| :------------------------ | --------: | ------: |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| Median encoding time (ms) | 3.983     | 0.538   |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| Median decoding time (ms) | 3.151     | 0.006   |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| Encoded size (bytes)      | 1,213,676 | 910,262 |&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As the numbers show, CBOR is both faster and more concise for this particular data. Also, CBOR pie tastes better.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2019/03/extensible-binary-encoding-with-cbor/strawberry_pie.jpg&#34; alt=&#34;strawberry rhubarb pie&#34;&gt;&lt;/p&gt;
&lt;p&gt;Food always looks good in pictures!&lt;/p&gt;
&lt;h3 id=&#34;optimizing-cbor-with-tags&#34;&gt;Optimizing CBOR with Tags&lt;/h3&gt;
&lt;p&gt;In the case of encoding homogeneous numeric arrays, CBOR encoders can struggle with optimizing the packing of the data. For example, if you have an array of floating point numbers in a higher-level language like Python or JavaScript, the CBOR encoder implementation won’t necessarily determine how many bits are required to encode the numbers, defaulting to the largest available. Additionally, each value in the array will be individually described as a floating point number. This increases the cost and size of the data considerably.&lt;/p&gt;
&lt;p&gt;CBOR has an answer to this problem. The &lt;a href=&#34;https://datatracker.ietf.org/doc/draft-ietf-cbor-array-tags/?include_text=1&#34;&gt;Draft Typed Array Tags&lt;/a&gt; spec includes tags specifying typed arrays which happen to match JavaScript &lt;code&gt;TypedArray&lt;/code&gt; flavors.&lt;/p&gt;
&lt;p&gt;For example, let’s say you have a &lt;code&gt;Float32Array&lt;/code&gt; with a few values:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; floats = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; Float32Array([&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1.234567&lt;/span&gt;, &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;2.345678&lt;/span&gt;, &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;3.456789&lt;/span&gt;]);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;JSON has a really funny way of encoding a &lt;code&gt;Float32Array&lt;/code&gt;, lookit all those bytes:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;0&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1.2345670461654663&lt;/span&gt;,&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;2.3456780910491943&lt;/span&gt;,&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;2&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;3.456789016723633&lt;/span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Length: 69 bytes.&lt;/p&gt;
&lt;p&gt;If we were to help the encoder by sending it a regular &lt;code&gt;Array&lt;/code&gt; it would still be pretty verbose, but the precision we weren’t using is truncated, so the overall length will vary wildly depending on the values in the &lt;code&gt;Array&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[1.234567,2.345678,3.456789]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Length: 28 bytes.&lt;/p&gt;
&lt;p&gt;The well-meaning Node cbor library can encode the &lt;code&gt;Float32Array&lt;/code&gt; directly, but doesn’t try to optimize for size:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  83                -- Array, 3 items
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    fb              -- Float, next 8 bytes
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      3ff3c0c960000000 -- [0], 1.2345670461654663
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    fb              -- Float, next 8 bytes
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      4002c3f2e0000000 -- [1], 2.3456780910491943
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    fb              -- Float, next 8 bytes
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      400ba78100000000 -- [2], 3.456789016723633&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Length: 28 bytes.&lt;/p&gt;
&lt;p&gt;Look at all that wasted precision, 64 bits for each 32-bit float, this just won’t do! The CBOR spec allows you to “tag” data for special treatment. According to the &lt;a href=&#34;https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml&#34;&gt;list of registered CBOR tags&lt;/a&gt;, “IEEE 754 binary32, little endian, Typed Array” is tag 85. The consecutive bytes of the three numbers follow.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  d8                --  next 1 byte
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    55              -- Tag #85
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      4c            -- Bytes, length: 12
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        4b069e3f971f1640083c5d40 -- 4b069e3f971f1640083c5d40&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Length: 15 bytes.&lt;/p&gt;
&lt;p&gt;The contained 12 byte string is equivalent to the values in the ArrayBuffer underneath our Float32Array.&lt;/p&gt;
&lt;p&gt;The CBOR decoder will spit out the tag alongside its associated blob, so our optimization is self-described. Now we need to ensure that a byte string with this tag is correctly converted to the typed array, minding endianness. In a mature JavaScript implementation, this is both easy and very fast. Because we encoded the values of the &lt;code&gt;ArrayBuffer&lt;/code&gt; underneath a &lt;code&gt;Float32Array&lt;/code&gt;, we can construct a new &lt;code&gt;Float32Array&lt;/code&gt; from the &lt;code&gt;ArrayBuffer&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; floats = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; Float32Array(bytes);  &lt;span style=&#34;color:#888&#34;&gt;// assuming platform and bytes are same endianness!
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is a very scalable way to read and write arrays of numeric values into JavaScript.&lt;/p&gt;
&lt;h3 id=&#34;when-to-use-cbor&#34;&gt;When to Use CBOR&lt;/h3&gt;
&lt;p&gt;As &lt;a href=&#34;https://youtu.be/9FBmHB-YoQw?t=57&#34;&gt;King Crimson teaches us&lt;/a&gt;, “it doesn’t mean you should / just because you can.”&lt;/p&gt;
&lt;p&gt;I’ve found CBOR useful as an alternative to JSON for large (&amp;gt;2kB) chunks of raw data or numeric arrays. In many other cases JSON is equivalent or superior, because most languages have native JSON encoding and decoding in their standard libraries. CBOR encoders are not always so optimized, but the gap is closing with wider adoption. Run your own tests and benchmarks on real data against real libraries before deciding to use CBOR.&lt;/p&gt;
&lt;p&gt;Many readers will recognize that schemaful formats such as protocol buffers are an endgame for structured data. If the infrastructure demands make sense for your application, this is also a good way to go. If you’re working with an application that already uses JSON, the difference in development and maintenance costs of porting it to CBOR or protobuf-likes should be measured against the size and performance gains of each approach.&lt;/p&gt;
&lt;p&gt;It’s tempting to let numbers alone decide what format to use, but don’t underestimate the value of JSON’s human readability for structured data. The value of human readability diminishes quickly when dealing with large numeric arrays or binary blobs, so once again CBOR is an appealing choice for these data.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Roundup of some useful websites</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2018/12/roundup-of-some-useful-websites/"/>
      <id>https://www.endpointdev.com/blog/2018/12/roundup-of-some-useful-websites/</id>
      <published>2018-12-21T00:00:00+00:00</published>
      <author>
        <name>Jon Jensen</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;a href=&#34;/blog/2018/12/roundup-of-some-useful-websites/squoosh-demo-20181220b.png&#34;&gt;&lt;img src=&#34;/blog/2018/12/roundup-of-some-useful-websites/squoosh-demo-20181220a.jpg&#34; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The world is a big place, and the Internet has gotten pretty big too. There are always new projects being created, and I want to share some useful and interesting ones from my growing list:&lt;/p&gt;
&lt;h3 id=&#34;squoosh-image-compressor&#34;&gt;Squoosh image compressor&lt;/h3&gt;
&lt;p&gt;Squoosh, hosted at &lt;a href=&#34;https://squoosh.app/&#34;&gt;squoosh.app&lt;/a&gt;, is an open source in-browser tool for experimenting with image compression, made by the Chrome development team.&lt;/p&gt;
&lt;p&gt;With Squoosh you can load an image in your browser, convert it to different image file formats (JPEG, WebP, PNG, BMP) using various compression algorithms and settings, and compare the result side-​by-​side with either the original image or the image compressed using other options.&lt;/p&gt;
&lt;p&gt;The screenshot above demonstrates Squoosh running in Firefox 64 on Linux. Click on it to see a larger, lossless PNG screenshot. The photo was taken by my son Phin in northern Virginia, and is a typical imperfect mobile phone photo. On the left is the original, and on the right I am showing how bad gradients in the sky can look when compressed too much—​maybe a quality level of 12 (out of 100) was too low. It does make for a very compact file size, though. 😄&lt;/p&gt;
&lt;p&gt;Squoosh’s interface has a convenient slider bar so you can compare any part of the two versions of the image side by side. You can zoom and pan the image as well.&lt;/p&gt;
&lt;p&gt;It is neat to see JavaScript tools (in this case TypeScript specifically) doing work in the browser that has traditionally been done by native apps.&lt;/p&gt;
&lt;h3 id=&#34;nerd-fonts&#34;&gt;Nerd Fonts&lt;/h3&gt;
&lt;p&gt;If you want access to an amazing number of symbols in a font, check out &lt;a href=&#34;https://nerdfonts.com/&#34;&gt;nerdfonts.com&lt;/a&gt;. There you can mix and match symbols from many popular developer-​oriented fonts such as Font Awesome, Powerline Symbols, Material Design, etc.&lt;/p&gt;
&lt;p&gt;I probably should have chosen some fun symbols to demonstrate it here, but I could tell that was a rabbit hole I would not soon emerge from!&lt;/p&gt;
&lt;h3 id=&#34;glotio-code-pastebin&#34;&gt;glot.io code pastebin&lt;/h3&gt;
&lt;p&gt;There are many public pastebins these days, but &lt;a href=&#34;https://glot.io/&#34;&gt;glot.io&lt;/a&gt; distinguishes itself by allowing you to run real code on their server in nearly 50 languages.&lt;/p&gt;
&lt;p&gt;It offers both public and private pastes, has an API, and is open source.&lt;/p&gt;
&lt;h3 id=&#34;firefox-send&#34;&gt;Firefox Send&lt;/h3&gt;
&lt;p&gt;Firefox Send at &lt;a href=&#34;https://send.firefox.com/&#34;&gt;send.firefox.com&lt;/a&gt; is a browser-​based service for securely sharing files temporarily, for only one download during a maximum of 24 hours.&lt;/p&gt;
&lt;p&gt;Handy for keeping unwanted bloat out of email, chat, or shared file storage for ephemeral files.&lt;/p&gt;
&lt;h3 id=&#34;transfersh-command-line-file-sharing&#34;&gt;transfer.sh command-​line file sharing&lt;/h3&gt;
&lt;p&gt;Similarly, &lt;a href=&#34;https://transfer.sh/&#34;&gt;transfer.sh&lt;/a&gt; is a terminal-​based file upload and download tool.&lt;/p&gt;
&lt;p&gt;As a command-​line tool it easily integrates with other standard tools, so you can pipe output from other programs directly to it. If you have sensitive data to share you don’t need to trust the service—​you can pipe your data through gpg or some other encryption tool before it leaves your computer.&lt;/p&gt;
&lt;p&gt;transfer.sh is open source and can be self-​hosted too.&lt;/p&gt;
&lt;p&gt;It even has a Tor onion service so uploads and/​or downloads can be as private as possible in hostile environments.&lt;/p&gt;
&lt;h3 id=&#34;doing-what-you-dont-want-to-do&#34;&gt;Doing what you don’t want to do&lt;/h3&gt;
&lt;p&gt;And finally, some timeless tips for making our human “software” work.&lt;/p&gt;
&lt;p&gt;Often just one or two annoying little things can block us from making progress on larger projects that overall we really enjoy. How can you motivate yourself to push ahead when you have work that needs to be done, but you don’t want to do it?&lt;/p&gt;
&lt;p&gt;Read the brief but helpful article &lt;a href=&#34;https://zenhabits.net/unwanted/&#34;&gt;10 Ways to Do What You Don’t Want to Do&lt;/a&gt; by Leo Babauta to get some good ideas. A few of the points mentioned especially resonate with me:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Why do I need to do it?&lt;/li&gt;
&lt;li&gt;What is stopping me?&lt;/li&gt;
&lt;li&gt;Embrace that it won’t be fun and do it anyway.&lt;/li&gt;
&lt;li&gt;Set constraints.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Then do at least a little bit of the work to get started. As our co-worker &lt;a href=&#34;/team/mike-heins/&#34;&gt;Mike Heins&lt;/a&gt; has said to me on a few occasions over the years, you’ll never finish until you start.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Learning from data basics II: simple Bayesian Networks</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2016/04/learning-from-data-basics-ii-simple/"/>
      <id>https://www.endpointdev.com/blog/2016/04/learning-from-data-basics-ii-simple/</id>
      <published>2016-04-12T00:00:00+00:00</published>
      <author>
        <name>Kamil Ciemniewski</name>
      </author>
      <content type="html">
        &lt;p&gt;In my &lt;a href=&#34;/blog/2016/03/learning-from-data-basics-naive-bayes/&#34;&gt;last article&lt;/a&gt; I presented an approach that simplifies computations of very complex probability models. It makes these complex models viable by shrinking the amount of needed memory and improving the speed of computing probabilities. The approach we were exploring is called the &lt;strong&gt;Naive Bayes model&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The context was the e-commerce feature in which a user is presented with the promotion box. The box shows the product category the user is most likely to buy.&lt;/p&gt;
&lt;p&gt;Though the results we got were quite good, I promised to present an approach that gives much better ones. While the Naive Bayes approach may not be acceptable in some scenarios due to the gap between approximated and real values, the approach presented in this article will make this distance much, much smaller.&lt;/p&gt;
&lt;h3 id=&#34;naive-bayes-as-a-simple-bayesian-network&#34;&gt;Naive Bayes as a simple Bayesian Network&lt;/h3&gt;
&lt;p&gt;When exploring  the Naive Bayes model, we said that there is a probabilistic assumption the model makes in order to simplify the computations. In the last article I wrote:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The Naive Bayes assumption says that the distribution factorizes the way we did it &lt;strong&gt;only if the features are conditionally independent given the category&lt;/strong&gt;.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h4 id=&#34;expressing-variable-dependencies-as-a-graph&#34;&gt;Expressing variable dependencies as a graph&lt;/h4&gt;
&lt;p&gt;Let’s imagine the visual representation of the relations between the random variables in the Naive Bayes model. Let’s make it into a directed acyclic graph. Let’s mark the dependence of one variable on another as a graph edge from the parent node pointing to it’s dependent node.&lt;/p&gt;
&lt;p&gt;Because of the assumption the Naive Bayes model enforces, its structure as a graph looks like the following:&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;&lt;a href=&#34;/blog/2016/04/learning-from-data-basics-ii-simple/image-0.png&#34; imageanchor=&#34;1&#34; style=&#34;margin-left: 1em; margin-right: 1em;&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2016/04/learning-from-data-basics-ii-simple/image-0.png&#34;/&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;You can notice there are no lines between all the “evidence” nodes. The assumption says that knowing the category, we have all needed knowledge about every single evidence node. This makes category the parent of all the other nodes. Intuitively, we can say that knowing the &lt;strong&gt;class&lt;/strong&gt; (in this example, the category) we know everything about all &lt;strong&gt;features&lt;/strong&gt;. It’s easy to notice that this assumption doesn’t hold in this example.&lt;/p&gt;
&lt;p&gt;In our fake data generator, we made it so that e.g. relationship status depends on age. We’ve also made the category depend on sex and age directly. This way we can’t say that knowing category we know everything about e. g. age. The random variables age and sex are not independent even if we know the value of category. It is clear that the above graph does not model the dependency relationships between these random variables.&lt;/p&gt;
&lt;p&gt;Let’s draw a graph that represents our fake data model better:&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;&lt;a href=&#34;/blog/2016/04/learning-from-data-basics-ii-simple/image-1.png&#34; imageanchor=&#34;1&#34; style=&#34;margin-left: 1em; margin-right: 1em;&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2016/04/learning-from-data-basics-ii-simple/image-1.png&#34;/&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;The combination of a graph like the one above and the probability distribution that follows the independencies it describes are known as a &lt;strong&gt;Bayesian Network&lt;/strong&gt;.&lt;/p&gt;
&lt;h4 id=&#34;using-the-graph-representation-in-practice---the-chain-rule-for-bayesian-networks&#34;&gt;Using the graph representation in practice - the chain rule for Bayesian Networks&lt;/h4&gt;
&lt;p&gt;The fact that our distribution is part of the Bayesian Network, allows us to use the formula for simplifying the distribution itself. The formula is called the &lt;strong&gt;chain rule for Bayesian Networks&lt;/strong&gt; and for our particular example looks like the following:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;p(cat, sex, age, rel, loc) = p(sex) * p(age) * p(loc) * p(rel | age) * p(cat | sex, age)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can notice that the equation is just a product of a number of factors. There’s one factor for each random variable. The factors for variables that in the graph don’t have any parents are expressed as p(var) while those that do are expressed as p(var | par) or p(var | par1, par2&amp;hellip;).&lt;/p&gt;
&lt;p&gt;Notice that the Naive Bayes model fits perfectly into this equation. If you were to take the first graph presented in this article—​for the Naive Bayes, and use the above equation, you’d get exactly the formula we used in the last article.&lt;/p&gt;
&lt;h3 id=&#34;coding-the-updated-probabilistic-model&#34;&gt;Coding the updated probabilistic model&lt;/h3&gt;
&lt;p&gt;Before going further, I strongly advise you to make sure you read the &lt;a href=&#34;/blog/2016/03/learning-from-data-basics-naive-bayes/&#34;&gt;previous article - about the Naive Bayes model&lt;/a&gt; - to fully understand the classes used in the code in this section.&lt;/p&gt;
&lt;p&gt;Let’s take our chain rule equation and simplify it:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;p(cat, sex, age, rel, loc) = p(sex) * p(age) * p(loc) * p(rel | age) * p(cat | sex, age)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Again a conditional distrubution can be expressed as:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;p(a | b) = p(a, b) / p(b)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This gives us:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;p(cat, sex, age, rel, loc) = p(sex) * p(age) * p(loc) * (p(rel, age)/ p(age)) * (p(cat, sex, age) / p(sex, age))&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We can easily factor out the p(age) with:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;p(cat, sex, age, rel, loc) = p(sex) * p(loc) * p(rel, age) * (p(cat, sex, age) / p(sex, age))&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Let’s define needed random variables and factors:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;category = &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;RandomVariable&lt;/span&gt;.new &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:category&lt;/span&gt;, [ &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:veggies&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:snacks&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:meat&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:drinks&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:beauty&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:magazines&lt;/span&gt; ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;age      = &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;RandomVariable&lt;/span&gt;.new &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:age&lt;/span&gt;,      [ &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:teens&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:young_adults&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:adults&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:elders&lt;/span&gt; ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sex      = &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;RandomVariable&lt;/span&gt;.new &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:sex&lt;/span&gt;,      [ &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:male&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:female&lt;/span&gt; ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;relation = &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;RandomVariable&lt;/span&gt;.new &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:relation&lt;/span&gt;, [ &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:single&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:in_relationship&lt;/span&gt; ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;location = &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;RandomVariable&lt;/span&gt;.new &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:location&lt;/span&gt;, [ &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:us&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:canada&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:europe&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:asia&lt;/span&gt; ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;loc_dist     = &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Factor&lt;/span&gt;.new [ location ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sex_dist     = &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Factor&lt;/span&gt;.new [ sex ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;rel_age_dist = &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Factor&lt;/span&gt;.new [ relation, age ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cat_age_sex_dist = &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Factor&lt;/span&gt;.new [ category, age, sex ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;age_sex_dist = &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Factor&lt;/span&gt;.new [ age, sex ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;full_dist = &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Factor&lt;/span&gt;.new [ category, age, sex, relation, location ]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The learning part is as trivial as in the Naive Bayes case. The only difference is the set of distributions involved:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Model&lt;/span&gt;.generate(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1000&lt;/span&gt;).each &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |user|
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  user.baskets.each &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |basket|
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    basket.line_items.each &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |item|
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      loc_dist.observe! &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;location&lt;/span&gt;: user.location
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      sex_dist.observe! &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;sex&lt;/span&gt;: user.sex
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      rel_age_dist.observe! &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;relation&lt;/span&gt;: user.relationship, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;age&lt;/span&gt;: user.age
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      cat_age_sex_dist.observe! &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;category&lt;/span&gt;: item.category, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;age&lt;/span&gt;: user.age, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;sex&lt;/span&gt;: user.sex
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      age_sex_dist.observe! &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;age&lt;/span&gt;: user.age, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;sex&lt;/span&gt;: user.sex
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      full_dist.observe! &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;category&lt;/span&gt;: item.category, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;age&lt;/span&gt;: user.age, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;sex&lt;/span&gt;: user.sex,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;relation&lt;/span&gt;: user.relationship, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;location&lt;/span&gt;: user.location
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The inference part is also very similar to the one from the previous article. Here too the only difference are the distributions involved:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;infer = -&amp;gt; (age, sex, rel, loc) &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  all = category.values.map &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |cat|
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    pl  = loc_dist.value_for &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;location&lt;/span&gt;: loc
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ps  = sex_dist.value_for &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;sex&lt;/span&gt;: sex
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    pra = rel_age_dist.value_for &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;relation&lt;/span&gt;: rel, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;age&lt;/span&gt;: age
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    pcas = cat_age_sex_dist.value_for &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;category&lt;/span&gt;: cat, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;age&lt;/span&gt;: age, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;sex&lt;/span&gt;: sex
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    pas = age_sex_dist.value_for &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;age&lt;/span&gt;: age, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;sex&lt;/span&gt;: sex
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    { &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;category&lt;/span&gt;: cat, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;value&lt;/span&gt;: (pl * ps * pra * pcas) / pas }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  all_full = category.values.map &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |cat|
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    val = full_dist.value_for &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;category&lt;/span&gt;: cat, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;age&lt;/span&gt;: age, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;sex&lt;/span&gt;: sex,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;relation&lt;/span&gt;: rel, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;location&lt;/span&gt;: loc
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    { &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;category&lt;/span&gt;: cat, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;value&lt;/span&gt;: val }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  win      = all.max      { |a, b| a[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:value&lt;/span&gt;] &amp;lt;=&amp;gt; b[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:value&lt;/span&gt;] }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  win_full = all_full.max { |a, b| a[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:value&lt;/span&gt;] &amp;lt;=&amp;gt; b[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:value&lt;/span&gt;] }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#038&#34;&gt;puts&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Best match for &lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;#{&lt;/span&gt;[ age, sex, rel, loc ]&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;:&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#038&#34;&gt;puts&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;   &lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;#{&lt;/span&gt;win[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:category&lt;/span&gt;]&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt; =&amp;gt; &lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;#{&lt;/span&gt;win[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:value&lt;/span&gt;]&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#038&#34;&gt;puts&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Full pointed at:&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#038&#34;&gt;puts&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;   &lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;#{&lt;/span&gt;win_full[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:category&lt;/span&gt;]&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt; =&amp;gt; &lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;#{&lt;/span&gt;win_full[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:value&lt;/span&gt;]&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\n\n&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;the-results&#34;&gt;The results&lt;/h3&gt;
&lt;p&gt;Now let’s run the inference procedure with the same set of examples as in the previous post to compare the results:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;infer.call &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:teens&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:male&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:single&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:us&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;infer.call &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:young_adults&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:male&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:single&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:asia&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;infer.call &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:adults&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:female&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:in_relationship&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:europe&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;infer.call &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:elders&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:female&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:in_relationship&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:canada&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Which yields:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Best match for [:teens, :male, :single, :us]:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   snacks =&amp;gt; 0.020610837341908994
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Full pointed at:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   snacks =&amp;gt; 0.02103999999999992
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Best match for [:young_adults, :male, :single, :asia]:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   meat =&amp;gt; 0.001801062449999991
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Full pointed at:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   meat =&amp;gt; 0.0010700000000000121
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Best match for [:adults, :female, :in_relationship, :europe]:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   beauty =&amp;gt; 0.0007693377820183494
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Full pointed at:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   beauty =&amp;gt; 0.0008300000000000074
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Best match for [:elders, :female, :in_relationship, :canada]:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   veggies =&amp;gt; 0.0024346445741176875
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Full pointed at:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   veggies =&amp;gt; 0.0034199999999999886&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Just as with using the Naive Bayes, we got correct values for all cases. When you look closer though, you can notice that the resulting probability values were much closer to the original, full distribution ones. The approach we took here makes the values differ only a couple times in 10000. That result could make a difference in the e-commerce shop from the example if it were visited by millions of customers each month.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Learning from data basics: the Naive Bayes model</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2016/03/learning-from-data-basics-naive-bayes/"/>
      <id>https://www.endpointdev.com/blog/2016/03/learning-from-data-basics-naive-bayes/</id>
      <published>2016-03-23T00:00:00+00:00</published>
      <author>
        <name>Kamil Ciemniewski</name>
      </author>
      <content type="html">
        &lt;p&gt;Have you ever wondered what is the machinery behind some of the algorithms for doing seemingly very intelligent tasks? How is it possible that the computer program can recognize faces in photos, turn an image into a text or even classify some emails as legitimate or as spam?&lt;/p&gt;
&lt;p&gt;Today, I’d like to present one of the simplest models for performing classification tasks. The model enables extremely fast execution, making it very practical in many use cases. The example I’ll choose will enable us to extend the discussion about the most optimal approach to another blog post.&lt;/p&gt;
&lt;h3 id=&#34;the-problem&#34;&gt;The problem&lt;/h3&gt;
&lt;p&gt;Imagine that you’re working on an e-commerce store for your client. One of the requirements is to present the currently logged in user with a “promotion box” somewhere on the page. The goal is to maximize our chances of having the user put the product from the box into the basket. There’s one promotional box and a couple of different categories of products to choose the actual product from.&lt;/p&gt;
&lt;h3 id=&#34;thinking-about-the-solutionusing-probability-theory&#34;&gt;Thinking about the solution—​using probability theory&lt;/h3&gt;
&lt;p&gt;One of the obvious directions we may want to turn towards is to use probability theory. If we could collect the data about the user’s previous choices and his or her characteristics, we can use probability to select the product category best suited for the current user. We would then choose a product from this category that currently has an active promotion.&lt;/p&gt;
&lt;h3 id=&#34;quick-theory-refresher-for-programmers&#34;&gt;Quick theory refresher for programmers&lt;/h3&gt;
&lt;p&gt;As we’ll be exploring the probability approaches using Ruby code, I’d like to very quickly walk you through some of the basic concepts we will be using from now on.&lt;/p&gt;
&lt;h4 id=&#34;random-variables&#34;&gt;Random variables&lt;/h4&gt;
&lt;p&gt;The simplest probability scenario many of us are already accustomed with is the coin toss results distribution. Here we’re throwing the coin, noting whether we get heads or tails. In this experiment, we call “got heads” and “got tails” probability events. We can also shift the terminology a bit by calling them: two values of the “toss result” &lt;strong&gt;random variable&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;So in this case we’d have a random variable—​let’s call it &lt;strong&gt;T&lt;/strong&gt; (for “toss”) that can take values of: “heads” or “tails”. We then define the probability distribution P(T) as a function from the random variable value to a real number between 0 and 1 inclusively on both sides. In real world the probability values after e. g 10000 tosses might look like the following:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-------+---------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| toss  | value               |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-------+---------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| heads | 0.49929999999999947 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| tails |   0.500699999999998 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-------+---------------------+&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;These values are nearing 0.5 more and more with the greater number of tosses.&lt;/p&gt;
&lt;h4 id=&#34;factors-and-probability-distributions&#34;&gt;Factors and probability distributions&lt;/h4&gt;
&lt;p&gt;We’ve shown a simple probability distribution. To ease the comprehension of the Ruby code we’ll be working with, let me introduce the notion of the &lt;strong&gt;factor&lt;/strong&gt;. We called the “table” from the last example a probability distribution. The table represented a function from a random variable’s value to a real number between [0, 1]. The &lt;strong&gt;factor&lt;/strong&gt; is a generalization of that notion. It’s a function from the same domain, but returning any real number. We’ll explore the usability of this notion in some of our next articles.&lt;/p&gt;
&lt;p&gt;The probability distribution is a factor that adds two constraints:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;its values are always in the range [0, 1] inclusively&lt;/li&gt;
&lt;li&gt;the sum of all it’s values is exactly 1&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;simple-ruby-modeling-of-random-variables-and-factors&#34;&gt;Simple Ruby modeling of random variables and factors&lt;/h3&gt;
&lt;p&gt;We need to have some ways of computing probability distributions. Let’s define some simple tools we’ll be using in this blog series:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;# Let&amp;#39;s define a simple version of the random variable&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;# - one that will hold discrete values&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;RandomVariable&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080&#34;&gt;attr_accessor&lt;/span&gt; &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:values&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:name&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;initialize&lt;/span&gt;(&lt;span style=&#34;color:#038&#34;&gt;name&lt;/span&gt;, values)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#33b&#34;&gt;@name&lt;/span&gt; = &lt;span style=&#34;color:#038&#34;&gt;name&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#33b&#34;&gt;@values&lt;/span&gt; = values
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;# The following class really represents here a probability&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;# distribution. We&amp;#39;ll adjust it in the next posts to make&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;# it match the definition of a &amp;#34;factor&amp;#34;. We&amp;#39;re naming it this&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;# way right now as every probability distribution is a factor&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;# too.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Factor&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080&#34;&gt;attr_accessor&lt;/span&gt; &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:_table&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:_count&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:variables&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;initialize&lt;/span&gt;(variables)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#33b&#34;&gt;@_table&lt;/span&gt; = {}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#33b&#34;&gt;@_count&lt;/span&gt; = &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;.&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#33b&#34;&gt;@variables&lt;/span&gt; = variables
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    initialize_table
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;# We&amp;#39;re choosing to represent the factor / distribution&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;# here as a table with value combinations in one column&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;# and probability values in another. Technically, we&amp;#39;re using&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;# Ruby&amp;#39;s Hash. The following method builds the the initial hash&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;# with all the possible keys and values assigned to 0:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;initialize_table&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    variables_values = &lt;span style=&#34;color:#33b&#34;&gt;@variables&lt;/span&gt;.map &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |var|
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      var.values.map &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |val|
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        { var.name.to_sym =&amp;gt; val }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;.flatten
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt; &lt;span style=&#34;color:#888&#34;&gt;# [ [ { name: value } ] ]   &lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#33b&#34;&gt;@_table&lt;/span&gt; = variables_values[&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;..(variables_values.count)].inject(variables_values.first) &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |all_array, var_arrays|
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      all_array = all_array.map &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |ob|
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        var_arrays.map &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |var_val|
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          ob.merge var_val
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;.flatten
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      all_array
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;.inject({}) { |m, item| m[item] = &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;; m }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;# The following method adjusts the factor by adding information&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;# about observed combination of values. This in turn adjusts probability&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;# values for all the entries:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;observe!&lt;/span&gt;(observation)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; !&lt;span style=&#34;color:#33b&#34;&gt;@_table&lt;/span&gt;.has_key? observation
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;raise&lt;/span&gt; &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;ArgumentError&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Doesn&amp;#39;t fit the factor - &lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;#{&lt;/span&gt;&lt;span style=&#34;color:#33b&#34;&gt;@variables&lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt; for observation: &lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;#{&lt;/span&gt;observation&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#33b&#34;&gt;@_count&lt;/span&gt; += &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#33b&#34;&gt;@_table&lt;/span&gt;.keys.each &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |key|
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      observed = key == observation
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#33b&#34;&gt;@_table&lt;/span&gt;[key] = (&lt;span style=&#34;color:#33b&#34;&gt;@_table&lt;/span&gt;[key] * (&lt;span style=&#34;color:#33b&#34;&gt;@_count&lt;/span&gt; == &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt; ? &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt; : (&lt;span style=&#34;color:#33b&#34;&gt;@_count&lt;/span&gt; - &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;)) + 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       (observed ? &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt; : &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;)) / 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         (&lt;span style=&#34;color:#33b&#34;&gt;@_count&lt;/span&gt; == &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt; ? &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt; : &lt;span style=&#34;color:#33b&#34;&gt;@_count&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#038&#34;&gt;self&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;# Helper method for getting all the possible combinations&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;# of random variable assignments&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;entries&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#33b&#34;&gt;@_table&lt;/span&gt;.each
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;# Helper method for testing purposes. Sums the values for the whole&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;# distribution - it should return 1 (close to 1 due to how computers&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;# handle floating point operations)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;sum&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#33b&#34;&gt;@_table&lt;/span&gt;.values.inject(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:+&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;# Returns a probability of a given combination happening&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;# in the experiment&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;value_for&lt;/span&gt;(key)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#33b&#34;&gt;@_table&lt;/span&gt;[key].nil?
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;raise&lt;/span&gt; &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;ArgumentError&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Doesn&amp;#39;t fit the factor - &lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;#{&lt;/span&gt;&lt;span style=&#34;color:#33b&#34;&gt;@varables&lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt; for: &lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;#{&lt;/span&gt;key&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#33b&#34;&gt;@_table&lt;/span&gt;[key]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;# Helper method for testing purposes. Returns a table object&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;# ready to be printed to stdout. It shows the whole distribution&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;# as a table with some columns being random variables values and&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;# the last one being the probability value&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;table&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    rows = &lt;span style=&#34;color:#33b&#34;&gt;@_table&lt;/span&gt;.keys.map &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |key|
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      key.values &amp;lt;&amp;lt; &lt;span style=&#34;color:#33b&#34;&gt;@_table&lt;/span&gt;[key]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    table = &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Terminal&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Table&lt;/span&gt;.new &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;rows&lt;/span&gt;: rows, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;headings&lt;/span&gt;: ( &lt;span style=&#34;color:#33b&#34;&gt;@variables&lt;/span&gt;.map(&amp;amp;&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:name&lt;/span&gt;) &amp;lt;&amp;lt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;value&amp;#34;&lt;/span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    table.align_column(&lt;span style=&#34;color:#33b&#34;&gt;@variables&lt;/span&gt;.count, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:right&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    table
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080&#34;&gt;protected&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;entries&lt;/span&gt;=(_entries)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    _entries.each &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |entry|
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#33b&#34;&gt;@_table&lt;/span&gt;[entry.keys.first] = entry.values.first
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;count&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#33b&#34;&gt;@_count&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;count&lt;/span&gt;=(_count)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#33b&#34;&gt;@_count&lt;/span&gt; = _count
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Notice that we’re using here the &lt;strong&gt;terminal-table&lt;/strong&gt; gem as a helper for printing out the factors in an easy to grasp fashion. You’ll need the following requires:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;require&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;rubygems&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;require&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;terminal-table&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;the-scenario-setup&#34;&gt;The scenario setup&lt;/h3&gt;
&lt;p&gt;Let’s imagine that we have the following categories to choose from:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;category = &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;RandomVariable&lt;/span&gt;.new &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:category&lt;/span&gt;, [ &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:veggies&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:snacks&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:meat&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:drinks&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:beauty&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:magazines&lt;/span&gt; ]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And the following user features on each request:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;age      = &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;RandomVariable&lt;/span&gt;.new &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:age&lt;/span&gt;,      [ &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:teens&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:young_adults&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:adults&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:elders&lt;/span&gt; ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sex      = &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;RandomVariable&lt;/span&gt;.new &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:sex&lt;/span&gt;,      [ &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:male&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:female&lt;/span&gt; ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;relation = &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;RandomVariable&lt;/span&gt;.new &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:relation&lt;/span&gt;, [ &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:single&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:in_relationship&lt;/span&gt; ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;location = &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;RandomVariable&lt;/span&gt;.new &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:location&lt;/span&gt;, [ &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:us&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:canada&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:europe&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:asia&lt;/span&gt; ]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Let’s define the data model that resembles logically the one we could have in our real e-commerce application:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;LineItem&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080&#34;&gt;attr_accessor&lt;/span&gt; &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:category&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;initialize&lt;/span&gt;(category)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#038&#34;&gt;self&lt;/span&gt;.category = category
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Basket&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080&#34;&gt;attr_accessor&lt;/span&gt; &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:line_items&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;initialize&lt;/span&gt;(line_items)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#038&#34;&gt;self&lt;/span&gt;.line_items = line_items
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;User&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080&#34;&gt;attr_accessor&lt;/span&gt; &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:age&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:sex&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:relationship&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:location&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:baskets&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;initialize&lt;/span&gt;(age, sex, relationship, location, baskets)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#038&#34;&gt;self&lt;/span&gt;.age = age
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#038&#34;&gt;self&lt;/span&gt;.sex = sex
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#038&#34;&gt;self&lt;/span&gt;.relationship = relationship
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#038&#34;&gt;self&lt;/span&gt;.location = location
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#038&#34;&gt;self&lt;/span&gt;.baskets = baskets
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We want to utilize a user’s baskets in order to infer the most probable value for a category, given a set of user’s features. In our example, we can imagine that we’re offering authentication via Facebook. We can grab info about a user’s sex, location, age and whether she/he is in relationship or not. We want to find a category that’s being chosen the most by users with a given set of features.&lt;/p&gt;
&lt;p&gt;As we don’t have any real data to play with, we’ll need a generator to create fake data of certain characteristics. Let’s first define a helper class with a method, that will allow us to choose a value out of a given list of options along with their weights:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Generator&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;self&lt;/span&gt;.&lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;pick&lt;/span&gt;(options)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    items = options.inject([]) &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |memo, keyval|
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      key, val = keyval
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      memo &amp;lt;&amp;lt; &lt;span style=&#34;color:#038&#34;&gt;Array&lt;/span&gt;.new(val, key)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      memo
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;.flatten
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    items.sample
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;With all the above we can define a random data generation model:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Model&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;# Let&amp;#39;s generate `num` users (1000 by default)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;self&lt;/span&gt;.&lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;generate&lt;/span&gt;(num = &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1000&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    num.times.to_a.map &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |user_index|
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      gen_user
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;# Returns a user with randomly selected traits and baskets&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;self&lt;/span&gt;.&lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;gen_user&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    age = gen_age
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    sex = gen_sex
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    rel = gen_rel(age)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    loc = gen_loc
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    baskets = gen_baskets(age, sex)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;User&lt;/span&gt;.new age, sex, rel, loc, baskets
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;# Randomly select a sex with 40% chance for getting a male&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;self&lt;/span&gt;.&lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;gen_sex&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Generator&lt;/span&gt;.pick &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;male&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;4&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;female&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;6&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;# Randomly select an age with 50% chance for getting a teen&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;# (among other options and weights)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;self&lt;/span&gt;.&lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;gen_age&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Generator&lt;/span&gt;.pick &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;teens&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;5&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;young_adults&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;2&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;adults&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;2&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;elders&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;# Randomly select a relationship status.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;# Depend the chance of getting a given option on the user&amp;#39;s age&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;self&lt;/span&gt;.&lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;gen_rel&lt;/span&gt;(age)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;case&lt;/span&gt; age
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;when&lt;/span&gt; &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:teens&lt;/span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;then&lt;/span&gt; &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Generator&lt;/span&gt;.pick &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;single&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;7&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;in_relationship&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;when&lt;/span&gt; &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:young_adults&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;then&lt;/span&gt; &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Generator&lt;/span&gt;.pick &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;single&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;4&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;in_relationship&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;6&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;else&lt;/span&gt;                    &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Generator&lt;/span&gt;.pick &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;single&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;8&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;in_relationship&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;# Randomly select a location with 40% chance for getting a united states&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;# (among other options and weights)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;self&lt;/span&gt;.&lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;gen_loc&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Generator&lt;/span&gt;.pick &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;us&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;4&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;canada&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;3&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;europe&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;asia&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;# Randomly select 20 basket line items.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;# Depend the chance of getting a given option on the user&amp;#39;s age and sex&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;self&lt;/span&gt;.&lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;gen_items&lt;/span&gt;(age, sex)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    num = &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;20&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    num.times.to_a.map &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |i|
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; (age == &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:teens&lt;/span&gt; || age == &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:young_adults&lt;/span&gt;) &amp;amp;&amp;amp; sex == &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:female&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Generator&lt;/span&gt;.pick &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;veggies&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;snacks&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;3&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;meat&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;drinks&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;beauty&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;9&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;magazines&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;6&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;elsif&lt;/span&gt; age == &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:teens&lt;/span&gt;  &amp;amp;&amp;amp; sex == &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:male&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Generator&lt;/span&gt;.pick &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;veggies&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;snacks&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;6&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;meat&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;4&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;drinks&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;beauty&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;magazines&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;elsif&lt;/span&gt; (age == &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:young_adults&lt;/span&gt; || age == &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:adults&lt;/span&gt;) &amp;amp;&amp;amp; sex == &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:male&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Generator&lt;/span&gt;.pick &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;veggies&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;snacks&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;4&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;meat&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;6&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;drinks&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;6&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;beauty&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;magazines&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;elsif&lt;/span&gt; (age == &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:young_adults&lt;/span&gt; || age == &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:adults&lt;/span&gt;) &amp;amp;&amp;amp; sex == &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:female&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Generator&lt;/span&gt;.pick &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;veggies&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;4&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;snacks&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;4&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;meat&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;2&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;drinks&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;beauty&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;6&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;magazines&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;elsif&lt;/span&gt; age == &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:elders&lt;/span&gt; &amp;amp;&amp;amp; sex == &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:male&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Generator&lt;/span&gt;.pick &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;veggies&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;6&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;snacks&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;2&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;meat&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;2&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;drinks&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;2&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;beauty&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;magazines&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;elsif&lt;/span&gt; age == &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:elders&lt;/span&gt; &amp;amp;&amp;amp; sex == &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:female&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Generator&lt;/span&gt;.pick &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;veggies&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;8&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;snacks&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;meat&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;2&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;drinks&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;beauty&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;4&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;magazines&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Generator&lt;/span&gt;.pick &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;veggies&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;snacks&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;meat&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;drinks&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;beauty&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;magazines&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;.map &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |cat|
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;LineItem&lt;/span&gt;.new cat
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;# Randomly select 5 baskets depending the traits of the basket on user&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;# age and sex&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;self&lt;/span&gt;.&lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;gen_baskets&lt;/span&gt;(age, sex)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    num = &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    num.times.to_a.map &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |i|
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Basket&lt;/span&gt;.new gen_items(age, sex)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;where-is-the-complexity&#34;&gt;Where is the complexity?&lt;/h3&gt;
&lt;p&gt;The approach described above doesn’t seem that exciting or complex. Usually reading about probability theory applied in the field of machine learning requires going through quite a dense set of mathematical notions. The field is also being actively worked on by researchers. This implies a huge complexity—​certainly not the simple definition of probability that we got used to in high school.&lt;/p&gt;
&lt;p&gt;The problem becomes a bit more complex if you consider efficiency of computing the probabilities. In our example, the joined probability distribution—​to fully describe the scenario—​needs to specify probability values for 383 cases:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;p&lt;/span&gt;(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:veggies&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:teens&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:male&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:single&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:us&lt;/span&gt;) &lt;span style=&#34;color:#888&#34;&gt;# one of 384 combinations&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Given that the probability distributions have to sum up to 1, the last case can be fully inferred from the sum of all the others. This means that we need 6 * 4 * 2 * 2 * 4 - 1 = 383 parameters in the model: 6 categories, 4 age classes, 2 sexes, 2 relationship kinds and 4 locations. Imagine adding one additional, 4 valued feature (a season). This would grow our number of parameters to &lt;strong&gt;1535&lt;/strong&gt;. And this is a very simple training example. We could have a model with close to 100 different features. The number of parameters would clearly be unmanageable even on the biggest servers we could put them in. This approach would also make it very painful to add additional features to the model.&lt;/p&gt;
&lt;h3 id=&#34;very-simple-but-powerful-optimization-the-naive-bayes-model&#34;&gt;Very simple but powerful optimization: The Naive Bayes model&lt;/h3&gt;
&lt;p&gt;In this section I’m going to present you with an equation we’ll be working with when optimizing our example. I’m not going to explain the mathematics behind it as you can easily read about them on e. g. Wikipedia.&lt;/p&gt;
&lt;p&gt;The approach is called the &lt;strong&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Naive_Bayes_classifier&#34;&gt;Naive Bayes model&lt;/a&gt;&lt;/strong&gt;. It is being used e .g. in spam filters. It also has been used in the past in medical diagnosis field.&lt;/p&gt;
&lt;p&gt;It allows us to present the full probability distribution as a product of factors:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;p&lt;/span&gt;(cat, age, sex, rel, loc) == &lt;span style=&#34;color:#038&#34;&gt;p&lt;/span&gt;(cat) * &lt;span style=&#34;color:#038&#34;&gt;p&lt;/span&gt;(age | cat) * &lt;span style=&#34;color:#038&#34;&gt;p&lt;/span&gt;(sex | cat) * &lt;span style=&#34;color:#038&#34;&gt;p&lt;/span&gt;(rel | cat) * &lt;span style=&#34;color:#038&#34;&gt;p&lt;/span&gt;(loc | cat)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Where e. g. p(age | cat) represents the probability of a user being a certain age given that this user selects cat products most frequently. This is called the “posterior probability”. The above equation states that we can simplify the distribution to be a product of some number of much more easily manageable factors.&lt;/p&gt;
&lt;p&gt;The category from our example is often called a &lt;strong&gt;class&lt;/strong&gt; and the rest of random variables in the distribution are often called &lt;strong&gt;features&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;In our example, the number of parameters we’ll need to manage when presenting the distribution in this form drops to:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;6&lt;/span&gt; - &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;) + (&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;6&lt;/span&gt; * &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;4&lt;/span&gt; - &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;) + (&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;6&lt;/span&gt; * &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;2&lt;/span&gt; - &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;) + (&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;6&lt;/span&gt; * &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;2&lt;/span&gt; - &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;) + (&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;6&lt;/span&gt; * &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;4&lt;/span&gt; - &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;) == &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;73&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;That’s just around 19% of the original amount! Also, adding another variable (season) would only add 23 new parameters (compared to 1152 in the full distribution case).&lt;/p&gt;
&lt;p&gt;The Naive Bayes model limits the number of parameters we have to manage but it comes with very strong assumptions about the variables involved: in our example, that the user features are conditionally independent given the resulting category. Later on I’ll show why this isn’t true in this case even though the results will still be quite okay.&lt;/p&gt;
&lt;h3 id=&#34;implementing-the-naive-bayes-model&#34;&gt;Implementing the Naive Bayes model&lt;/h3&gt;
&lt;p&gt;As we now have all the tools we need, let’s get back to the probability theory to figure out how best to model the Naive Bayes in terms of the Ruby blocks we now have.&lt;/p&gt;
&lt;p&gt;The approach says that under the assumptions we discussed we can approximate the original distribution to be the product of factors:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;p&lt;/span&gt;(cat, age, sex, rel, loc) = &lt;span style=&#34;color:#038&#34;&gt;p&lt;/span&gt;(cat) * &lt;span style=&#34;color:#038&#34;&gt;p&lt;/span&gt;(age | cat) * &lt;span style=&#34;color:#038&#34;&gt;p&lt;/span&gt;(sex | cat) * &lt;span style=&#34;color:#038&#34;&gt;p&lt;/span&gt;(rel | cat) * &lt;span style=&#34;color:#038&#34;&gt;p&lt;/span&gt;(loc | cat)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Given the definition of the conditional probability we have that:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;p&lt;/span&gt;(a | b) = &lt;span style=&#34;color:#038&#34;&gt;p&lt;/span&gt;(a, b) / &lt;span style=&#34;color:#038&#34;&gt;p&lt;/span&gt;(b)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Thus, we can express the approximation as:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;p&lt;/span&gt;(cat, age, sex, rel, loc) = &lt;span style=&#34;color:#038&#34;&gt;p&lt;/span&gt;(cat) * ( &lt;span style=&#34;color:#038&#34;&gt;p&lt;/span&gt;(age, cat) / &lt;span style=&#34;color:#038&#34;&gt;p&lt;/span&gt;(cat) ) * ( &lt;span style=&#34;color:#038&#34;&gt;p&lt;/span&gt;(sex, cat) / &lt;span style=&#34;color:#038&#34;&gt;p&lt;/span&gt;(cat) ) * ( &lt;span style=&#34;color:#038&#34;&gt;p&lt;/span&gt;(rel, cat) / &lt;span style=&#34;color:#038&#34;&gt;p&lt;/span&gt;(cat) ) * ( &lt;span style=&#34;color:#038&#34;&gt;p&lt;/span&gt;(loc, cat) / &lt;span style=&#34;color:#038&#34;&gt;p&lt;/span&gt;(cat) )&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And then simplify it even further as:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;p&lt;/span&gt;(cat, age, sex, rel, loc) = &lt;span style=&#34;color:#038&#34;&gt;p&lt;/span&gt;(age, cat) * ( &lt;span style=&#34;color:#038&#34;&gt;p&lt;/span&gt;(sex, cat) / &lt;span style=&#34;color:#038&#34;&gt;p&lt;/span&gt;(cat) ) * ( &lt;span style=&#34;color:#038&#34;&gt;p&lt;/span&gt;(rel, cat) / &lt;span style=&#34;color:#038&#34;&gt;p&lt;/span&gt;(cat) ) * ( &lt;span style=&#34;color:#038&#34;&gt;p&lt;/span&gt;(loc, cat) / &lt;span style=&#34;color:#038&#34;&gt;p&lt;/span&gt;(cat) )&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Let’s define all the factors we will need:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cat_dist     = &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Factor&lt;/span&gt;.new [ category ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;age_cat_dist = &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Factor&lt;/span&gt;.new [ age, category ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sex_cat_dist = &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Factor&lt;/span&gt;.new [ sex, category ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;rel_cat_dist = &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Factor&lt;/span&gt;.new [ relation, category ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;loc_cat_dist = &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Factor&lt;/span&gt;.new [ location, category ]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Also, we want a full distribution to compare the results:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;full_dist = &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Factor&lt;/span&gt;.new [ category, age, sex, relation, location ]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Let’s generate 1000 random users and looping through them and their baskets - adjust probability distributions for combinations of product categories and user traits:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Model&lt;/span&gt;.generate(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1000&lt;/span&gt;).each &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |user|
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  user.baskets.each &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |basket|
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    basket.line_items.each &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |item|
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      cat_dist.observe! &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;category&lt;/span&gt;: item.category
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      age_cat_dist.observe! &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;age&lt;/span&gt;: user.age, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;category&lt;/span&gt;: item.category
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      sex_cat_dist.observe! &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;sex&lt;/span&gt;: user.sex, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;category&lt;/span&gt;: item.category
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      rel_cat_dist.observe! &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;relation&lt;/span&gt;: user.relationship, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;category&lt;/span&gt;: item.category
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      loc_cat_dist.observe! &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;location&lt;/span&gt;: user.location, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;category&lt;/span&gt;: item.category
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      full_dist.observe! &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;category&lt;/span&gt;: item.category, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;age&lt;/span&gt;: user.age, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;sex&lt;/span&gt;: user.sex,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;relation&lt;/span&gt;: user.relationship, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;location&lt;/span&gt;: user.location
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We can now print the distributions as tables to have an insight about the data:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[ cat_dist, age_cat_dist, sex_cat_dist, rel_cat_dist, 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  loc_cat_dist, full_dist ].each &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |dist|
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#038&#34;&gt;puts&lt;/span&gt; dist.table
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;# Let&amp;#39;s print out the sum of all entries to ensure the&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;# algorithm works well:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#038&#34;&gt;puts&lt;/span&gt; dist.sum
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#038&#34;&gt;puts&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\n\n&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Which yields the following to the console (the full distribution is truncated due to its size):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-----------+---------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| category  | value               |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-----------+---------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| veggies   |             0.10866 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| snacks    | 0.19830999999999863 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| meat      |             0.14769 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| drinks    | 0.10115999999999989 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| beauty    |             0.24632 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| magazines | 0.19785999999999926 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-----------+---------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;0.9999999999999978
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+--------------+-----------+----------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| age          | category  | value                |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+--------------+-----------+----------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| teens        | veggies   |  0.02608000000000002 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| teens        | snacks    |  0.11347999999999969 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| teens        | meat      |  0.06282999999999944 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| teens        | drinks    |   0.0263200000000002 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| teens        | beauty    |   0.1390699999999995 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| teens        | magazines |  0.13322000000000103 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| young_adults | veggies   | 0.010250000000000023 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| young_adults | snacks    |  0.03676000000000003 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| young_adults | meat      |  0.03678000000000005 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| young_adults | drinks    |  0.03670000000000045 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| young_adults | beauty    |  0.05172999999999976 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| young_adults | magazines | 0.035779999999999916 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| adults       | veggies   | 0.026749999999999927 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| adults       | snacks    |  0.03827999999999962 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| adults       | meat      | 0.034600000000000505 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| adults       | drinks    | 0.028190000000000038 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| adults       | beauty    |  0.03892000000000036 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| adults       | magazines |  0.02225999999999998 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| elders       | veggies   |  0.04558000000000066 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| elders       | snacks    | 0.009790000000000047 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| elders       | meat      | 0.013480000000000027 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| elders       | drinks    | 0.009949999999999931 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| elders       | beauty    | 0.016600000000000226 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| elders       | magazines | 0.006600000000000025 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+--------------+-----------+----------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;1.0000000000000013
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+--------+-----------+----------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| sex    | category  | value                |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+--------+-----------+----------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| male   | veggies   |  0.03954000000000044 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| male   | snacks    |   0.1132499999999996 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| male   | meat      |  0.10851000000000031 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| male   | drinks    |                0.073 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| male   | beauty    | 0.023679999999999857 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| male   | magazines |  0.05901999999999993 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| female | veggies   |  0.06911999999999997 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| female | snacks    |  0.08506000000000069 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| female | meat      |  0.03918000000000006 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| female | drinks    |  0.02816000000000005 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| female | beauty    |  0.22264000000000062 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| female | magazines |  0.13884000000000046 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+--------+-----------+----------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;1.000000000000002
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-----------------+-----------+----------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| relation        | category  | value                |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-----------------+-----------+----------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| single          | veggies   |  0.07722000000000082 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| single          | snacks    |  0.13090999999999794 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| single          | meat      |  0.09317000000000061 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| single          | drinks    | 0.059979999999999915 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| single          | beauty    |  0.16317999999999971 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| single          | magazines |  0.13054000000000135 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| in_relationship | veggies   | 0.031440000000000336 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| in_relationship | snacks    |  0.06740000000000032 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| in_relationship | meat      | 0.054520000000000006 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| in_relationship | drinks    |  0.04118000000000009 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| in_relationship | beauty    |  0.08314000000000002 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| in_relationship | magazines |  0.06732000000000182 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-----------------+-----------+----------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;1.000000000000003
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+----------+-----------+----------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| location | category  | value                |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+----------+-----------+----------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| us       | veggies   |  0.04209000000000062 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| us       | snacks    |  0.07534000000000109 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| us       | meat      | 0.055059999999999984 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| us       | drinks    |  0.03704000000000108 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| us       | beauty    |  0.09879000000000099 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| us       | magazines |  0.07867999999999964 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| canada   | veggies   | 0.027930000000000062 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| canada   | snacks    |  0.05745999999999996 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| canada   | meat      |  0.04288000000000003 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| canada   | drinks    |  0.03078999999999948 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| canada   | beauty    |  0.06397999999999997 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| canada   | magazines | 0.053959999999999675 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| europe   | veggies   | 0.013110000000000132 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| europe   | snacks    |   0.0223200000000001 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| europe   | meat      |  0.01730000000000005 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| europe   | drinks    | 0.011859999999999964 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| europe   | beauty    | 0.025490000000000183 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| europe   | magazines | 0.020920000000000164 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| asia     | veggies   |  0.02552999999999989 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| asia     | snacks    |  0.04319000000000044 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| asia     | meat      |  0.03244999999999966 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| asia     | drinks    |  0.02147000000000005 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| asia     | beauty    |  0.05805999999999953 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| asia     | magazines |   0.0442999999999999 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+----------+-----------+----------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;1.0000000000000029
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-----------+--------------+--------+-----------------+----------+------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| category  | age          | sex    | relation        | location | value                  |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-----------+--------------+--------+-----------------+----------+------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| veggies   | teens        | male   | single          | us       |  0.0035299999999999936 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| veggies   | teens        | male   | single          | canada   |  0.0024500000000000073 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| veggies   | teens        | male   | single          | europe   |  0.0006999999999999944 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| veggies   | teens        | male   | single          | asia     |  0.0016699999999999899 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| veggies   | teens        | male   | in_relationship | us       |   0.001340000000000006 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| veggies   | teens        | male   | in_relationship | canada   |  0.0010099999999999775 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| veggies   | teens        | male   | in_relationship | europe   |  0.0006499999999999989 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| veggies   | teens        | male   | in_relationship | asia     |   0.000819999999999994 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(... many rows ...)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| magazines | elders       | male   | in_relationship | asia     | 0.00012000000000000163 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| magazines | elders       | female | single          | us       |  0.0007399999999999966 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| magazines | elders       | female | single          | canada   |  0.0007000000000000037 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| magazines | elders       | female | single          | europe   |  0.0003199999999999965 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| magazines | elders       | female | single          | asia     |  0.0005899999999999999 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| magazines | elders       | female | in_relationship | us       |  0.0004899999999999885 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| magazines | elders       | female | in_relationship | canada   | 0.00027000000000000114 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| magazines | elders       | female | in_relationship | europe   | 0.00012000000000000014 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| magazines | elders       | female | in_relationship | asia     | 0.00012000000000000014 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-----------+--------------+--------+-----------------+----------+------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;1.0000000000000004&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Let’s define a Proc for inferring categories based on user traits as evidence:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;infer = -&amp;gt; (age, sex, rel, loc) &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;# Let&amp;#39;s map through the possible categories and the probability&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;# values the distibutions assign to them:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  all = category.values.map &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |cat|
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    pc  = cat_dist.value_for &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;category&lt;/span&gt;: cat
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    pac = age_cat_dist.value_for &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;age&lt;/span&gt;: age, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;category&lt;/span&gt;: cat
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    psc = sex_cat_dist.value_for &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;sex&lt;/span&gt;: sex, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;category&lt;/span&gt;: cat
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    prc = rel_cat_dist.value_for &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;relation&lt;/span&gt;: rel, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;category&lt;/span&gt;: cat
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    plc = loc_cat_dist.value_for &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;location&lt;/span&gt;: loc, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;category&lt;/span&gt;: cat
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    { &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;category&lt;/span&gt;: cat, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;value&lt;/span&gt;: (pac * psc/pc * prc/pc * plc/pc) }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;# Let&amp;#39;s do the same with the full distribution to be able to compare&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;# the results:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  all_full = category.values.map &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |cat|
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    val = full_dist.value_for &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;category&lt;/span&gt;: cat, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;age&lt;/span&gt;: age, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;sex&lt;/span&gt;: sex,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;relation&lt;/span&gt;: rel, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;location&lt;/span&gt;: loc
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    { &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;category&lt;/span&gt;: cat, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;value&lt;/span&gt;: val }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;# Here&amp;#39;s we&amp;#39;re getting the most probable categories based on the&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;# Naive Bayes distribution approximation model and based on the full&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;# distribution:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  win      = all.max      { |a, b| a[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:value&lt;/span&gt;] &amp;lt;=&amp;gt; b[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:value&lt;/span&gt;] }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  win_full = all_full.max { |a, b| a[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:value&lt;/span&gt;] &amp;lt;=&amp;gt; b[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:value&lt;/span&gt;] }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#038&#34;&gt;puts&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Best match for &lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;#{&lt;/span&gt;[ age, sex, rel, loc ]&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;:&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#038&#34;&gt;puts&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;   &lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;#{&lt;/span&gt;win[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:category&lt;/span&gt;]&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt; =&amp;gt; &lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;#{&lt;/span&gt;win[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:value&lt;/span&gt;]&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#038&#34;&gt;puts&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Full pointed at:&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#038&#34;&gt;puts&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;   &lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;#{&lt;/span&gt;win_full[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:category&lt;/span&gt;]&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt; =&amp;gt; &lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;#{&lt;/span&gt;win_full[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:value&lt;/span&gt;]&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\n\n&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;the-results&#34;&gt;The results&lt;/h3&gt;
&lt;p&gt;We’re ready now to use the model and see how well the Naive Bayes model performs in this particular scenario:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;infer.call &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:teens&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:male&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:single&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:us&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;infer.call &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:young_adults&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:male&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:single&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:asia&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;infer.call &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:adults&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:female&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:in_relationship&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:europe&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;infer.call &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:elders&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:female&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:in_relationship&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:canada&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This gave the following results on the console:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Best match for [:teens, :male, :single, :us]:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   snacks =&amp;gt; 0.016252573282200262
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Full pointed at:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   snacks =&amp;gt; 0.01898999999999971
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Best match for [:young_adults, :male, :single, :asia]:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   meat =&amp;gt; 0.0037455794492659757
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Full pointed at:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   meat =&amp;gt; 0.0017000000000000016
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Best match for [:adults, :female, :in_relationship, :europe]:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   beauty =&amp;gt; 0.0012287311061725868
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Full pointed at:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   beauty =&amp;gt; 0.0003000000000000026
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Best match for [:elders, :female, :in_relationship, :canada]:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   veggies =&amp;gt; 0.002156365730474441
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Full pointed at:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   veggies =&amp;gt; 0.0013500000000000022&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;That’s quite impressive! Even though we’re using a simplified model to approximate the original distribution, the algorithm managed to infer the correct values in all cases. You can notice also that the results differ only by a couple of cases in 1000.&lt;/p&gt;
&lt;p&gt;The approximation like that would certainly be very useful in a more complex e-commerce scenario, in the case where the number of evidence variables would be big enough to be unmanageable using the full distribution. There are use cases though, where a couple of errors in 1000 cases would be too many—​the traditional example is medical diagnosis. There are also cases where the number of errors would be much greater just because the Naive Bayes assumption of conditional independence of variables is not always a fair an assumption. Is there a way to improve?&lt;/p&gt;
&lt;p&gt;The Naive Bayes assumption says that the distribution factorizes the way we did it &lt;strong&gt;only if the features are conditionally independent given the category&lt;/strong&gt;. The notion of &lt;strong&gt;conditional independence&lt;/strong&gt; (apart from the formal mathematical definition) suggests that if some variables a and b are conditionally independent given c, then if we know the value of c then no additional information about b can alter our knowledge about a. In our example, knowing the category, let say :beauty doesn’t mean that e. g sex is independent from age. In real world examples, it’s often very hard to find a use case for Naive Bayes that would follow the assumption in all the cases.&lt;/p&gt;
&lt;p&gt;There are alternative approaches that allow us to apply the assumptions that more rigidly follow the chosen data set. We will explore these in the next articles, building on top of what we saw here.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Temporary tables in SQL query optimization</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2015/02/temporary-tables-in-sql-query/"/>
      <id>https://www.endpointdev.com/blog/2015/02/temporary-tables-in-sql-query/</id>
      <published>2015-02-25T00:00:00+00:00</published>
      <author>
        <name>Jeff Boes</name>
      </author>
      <content type="html">
        &lt;p&gt;SQL queries can get complex in a big hurry. If you are querying multiple tables, and in particular if your query involves operations like UNION and INTERSECT, then you can find yourself in a big, messy pile of SQL. Worse, that big pile will often run slowly, sometimes to the point where a web application times out!&lt;/p&gt;
&lt;p&gt;I won’t inflict a real-world example on you; that would be cruel. So let’s look at a “toy” problem, keeping in mind that this won’t illustrate any time-savings, just the technique involved.&lt;/p&gt;
&lt;p&gt;Here’s the original SQL:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;SELECT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;p.*&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;FROM&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;products&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;p&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;JOIN&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;SELECT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;*&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;FROM&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;inventory&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;WHERE&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#888&#34;&gt;/* complex clause here */&lt;/span&gt;)&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;i&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;USING&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;(sku)&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;UNION&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;ALL&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;SELECT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;p.*&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;FROM&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;clearance_products&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;p&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;JOIN&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;SELECT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;*&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;FROM&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;inventory&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;WHERE&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#888&#34;&gt;/* complex clause here */&lt;/span&gt;)&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;i&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;USING&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;(sku)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Bonus hint: using “UNION ALL“ instead of just “UNION” will allow the query processor to skip an unnecessary step here. “UNION ALL” says you know the rows on either side of the clause are unique. “UNION” means the results will be post-processed to remove duplicates. This might save you more than a smidgen of time, depending on how large the two sub-queries get.&lt;/p&gt;
&lt;p&gt;Now, many times the query optimizer will just do the right thing here. But sometimes (cough, cough-MySQL), your database isn’t quite up to the task. So you have to shoulder the burden and help out. That’s where we can apply a temporary table.&lt;/p&gt;
&lt;p&gt;Temporary tables are created for the length of the database session; that’s different than a transaction. For a web application, that’s usually (not always) the length of the request (i.e., from the time your web application opens a database connection, until it explicitly closes it, or until it returns control to the web server, usually by passing it a completed page). For a script, it’s a similar duration, e.g. until the script exits.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;CREATE&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;TEMPORARY&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;TABLE&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;cross_inventory&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;AS&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;SELECT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;*&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;FROM&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;inventory&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;WHERE&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#888&#34;&gt;/* complex clause here */&lt;/span&gt;;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;CREATE&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;INDEX&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;cross_inv_sku&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;ON&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;cross_inventory(sku);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;There’s no significant difference for our purposes between a “permanent” and a “temporary” table. However, you do have to keep in mind that these tables are created without indexes, so if your goal is to improve the speed of queries involving the data here, adding an index after creating the table is usually desirable.&lt;/p&gt;
&lt;p&gt;With all this in place, now we can:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;SELECT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;p.*&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;FROM&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;products&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;p&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;JOIN&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;cross_inventory&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;i&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;USING&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;(sku)&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;UNION&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;SELECT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;p.*&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;FROM&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;clearance_products&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;p&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;JOIN&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;cross_inventory&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;i&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;USING&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;(sku)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Sometimes your temporary table will be built up not by a straightforward “CREATE &amp;hellip; AS SELECT &amp;hellip;”, but by your application:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;CREATE&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;TEMPORARY&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;TABLE&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;tmp_inventory&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;AS&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;SELECT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;*&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;FROM&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;inventory&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;WHERE&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;false&lt;/span&gt;;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;CREATE&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;INDEX&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;tmp_inv_sku&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;ON&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;tmp_inventory(sku);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And then within the application:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#c00;font-weight:bold&#34;&gt;# Pseudocode
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#c00;font-weight:bold&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;while&lt;/span&gt; (more_data) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  row = &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;build_inv_record&lt;/span&gt;(more_data);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;sql_do&lt;/span&gt;(&lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;&amp;#39;&lt;/span&gt;INSERT INTO tmp_inventory &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;VALUES&lt;/span&gt; (?,?,...)&lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;&amp;#39;&lt;/span&gt;, row);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here, we are creating an empty “inventory” table template as a temporary table (“SELECT * FROM inventory WHERE false”), then adding rows to it from the application, and finally running our query. Note that in a practical application of this, it’s not likely to be a lot faster, because the individual INSERT statements will take time. But this approach may have some utility where the existing “inventory” table doesn’t have the data we want to JOIN against, or has the data, but not in a way we can easily filter.&lt;/p&gt;
&lt;p&gt;I’ve used temporary tables (in a MySQL/Interchange/Perl environment) to speed up a query by a factor of two or more. It’s usually in those cases where you have a complex JOIN that appears in two or more parts of the query (again, usually a UNION). I’ve even had big-win situations where the same temporary table was used in two different queries during the same session.&lt;/p&gt;
&lt;p&gt;A similar approach is the &lt;a href=&#34;https://www.postgresql.org/docs/9.4/static/queries-with.html&#34;&gt;Common Table Expression&lt;/a&gt; (CTE) found in PostgreSQL starting with version 8.4. This allows you to identify the rows you would be pouring into your temporary table as a named result-set, then reference it in your query. Our “toy” example would become:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;WITH&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;cross_inventory&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;AS&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;SELECT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;*&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;FROM&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;inventory&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;WHERE&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#888&#34;&gt;/* complex clause here */&lt;/span&gt;)&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;SELECT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;p.*&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;FROM&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;products&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;p&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;JOIN&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;cross_inventory&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;i&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;USING&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;(sku)&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;UNION&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;SELECT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;p.*&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;FROM&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;clearance_products&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;p&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;JOIN&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;cross_inventory&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;i&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;USING&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;(sku)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I’ve not had an opportunity to use CTEs yet, and of course they aren’t available in MySQL, so the temporary-table technique will still have a lot of value for me in the foreseeable future.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Interchange Loop Optimization</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2015/02/interchange-loop-optimization/"/>
      <id>https://www.endpointdev.com/blog/2015/02/interchange-loop-optimization/</id>
      <published>2015-02-09T00:00:00+00:00</published>
      <author>
        <name>Mark Johnson</name>
      </author>
      <content type="html">
        &lt;p&gt;It’s important to understand both how loops work in Interchange and the (very) fundamental differences between interpolating Interchange tag language (ITL) and the special loop tags (typically referred to as [PREFIX-*] in the literature). Absent this sometimes arcane knowledge, it is very easy to get stuck with inefficient loops even with relatively small loop sets. I’ll discuss both the function of loops and interpolation differences between the tag types while working through a [query] example. While all loop tags&amp;ndash;[item-list], [loop], [search-list], and [query]&amp;ndash;process similarly, it is to [query] where most complex loops will gravitate over time (to optimize the initiation phase of entering the loop) and where we have the most flexibility for coming up with alternative strategies to mitigate sluggish loop-processing.&lt;/p&gt;
&lt;h3 id=&#34;loop-processing&#34;&gt;Loop Processing&lt;/h3&gt;
&lt;p&gt;All loop tags are &lt;em&gt;container&lt;/em&gt; tags in Interchange, meaning they have an open and close tag, and in between is the body. Only inside this body is it valid to define [PREFIX-&lt;em&gt;] tags (notable exception of [PREFIX-quote] for the sql arg of [query]). This is because the [PREFIX-&lt;/em&gt;] tags are not true ITL. They are tightly coupled with the structure of the underlying rows of data and they are processed by distinct, optimized regular expressions serially. Outside the context of the row data from a result set, they are meaningless.&lt;/p&gt;
&lt;p&gt;Moreover, the body of a loop tag is slurped into a scalar variable (as all bodies of container tags are handled via the ITL parser) and for each row in the record set of the loop, the contents are acted upon according to the [PREFIX-&lt;em&gt;] tags defined within the body. The first important distinction to recognize here is, the per-row action on this scalar is limited to &lt;em&gt;only&lt;/em&gt; the [PREFIX-&lt;/em&gt;] tags. The action occurring at loop time ignores any embedded ITL.&lt;/p&gt;
&lt;p&gt;At the end of each row’s processing, the copy of the body tied to that one row is then concatenated to the results of all previous rows thus processed. For a loop with N rows (assuming no suppression by [if-PREFIX-*] conditionals) that means every instance of ITL originally placed into the loop body is now present N times in the fully assembled body string. Simple example:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[loop list=&amp;#39;1 2 3&amp;#39;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[tmp junk][loop-code][/tmp]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[if scratch junk == 2]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;I declare [loop-code] to be special!
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[else]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Meh. [loop-code] is ordinary.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[/else]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[/if]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[/loop]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Once this result set with N=3 is processed, but &lt;strong&gt;before&lt;/strong&gt; Interchange returns the results, the assembled return looks like the following string:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[tmp junk]1[/tmp]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[if scratch junk == 2]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;I declare 1 to be special!
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[else]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Meh. 1 is oridinary
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[/else]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[/if]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[tmp junk]2[/tmp]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[if scratch junk == 2]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;I declare 2 to be special!
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[else]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Meh. 2 is oridinary
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[/else]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[/if]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[tmp junk]3[/tmp]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[if scratch junk == 2]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;I declare 3 to be special!
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[else]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Meh. 3 is oridinary
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[/else]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[/if]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Some important observations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It doesn’t take much ITL to turn a loop body into a monster interpolation process. One must consider the complexity of the ITL in the body by a factor of the number of rows (total, or the “ml” matchlimit value).&lt;/li&gt;
&lt;li&gt;ITL does &lt;em&gt;nothing&lt;/em&gt; to short-circuit action of the [PREFIX-*] tags. Having [PREFIX-param], [PREFIX-calc], etc. inside an ITL [if] means all those loop tags parse regardless of the truth of the if condition.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;itl-vs-loop-tags&#34;&gt;ITL vs. Loop Tags&lt;/h3&gt;
&lt;p&gt;ITL maps to routines, both core and user-defined, determined at compile time. They are processed in order of discovery within the string handed to the ::interpolate_html() routine and have varied and complex attributes that must be resolved for each individual tag. Further, for many (if not most) tags, the return value is itself passed through a new call to ::interpolate_html(), acting on all embedded tags, in an action referred to as &lt;em&gt;reparse&lt;/em&gt;. There is, relatively speaking, a good deal of overhead in processing through ::interpolate_html(), particularly with reparse potentially spawning off a great many more ::interpolate_html() calls.&lt;/p&gt;
&lt;p&gt;Loop tags, by contrast, map to a pre-compiled set of regular expressions. In contrast to crawling the string and acting upon the tags in the order of discovery, each regex in turn is applied globally to the string. The size of the string is limited to the exact size of the single loop body, and there is no analogue to ITL’s reparse. Further, given this processing pattern, a careful observer might have noted that the order of operations can impact the structure. Specifically, tags processed earlier cannot depend on tags processed later. E.g., [PREFIX-param] processes ahead of [PREFIX-pos], and so:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[if-PREFIX-pos 2 eq [PREFIX-param bar]]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;will work, but:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[if-PREFIX-param bar eq [PREFIX-pos 2]]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;will not. While the above is a somewhat contrived exampled, the impacts of loop tag processing can be more easily seen in an example using [PREFIX-next]:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[PREFIX-next][PREFIX-param baz][/PREFIX-next]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Code I only want to run when baz is false, like this [PREFIX-exec foo][/PREFIX-exec] call&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Because [PREFIX-next] is the absolute last loop tag to run, &lt;em&gt;every other loop tag in the block is run&lt;/em&gt; before the next condition is checked. All [PREFIX-next] does is suppress the resulting body from the return, unlike Perl’s next, which short-circuits the remaining code in the loop block.&lt;/p&gt;
&lt;h3 id=&#34;an-optimization-example&#34;&gt;An Optimization Example&lt;/h3&gt;
&lt;p&gt;As long as you’re familiar with the idiosyncracies of [PREFIX-*] tags, you should make every effort to use them instead of ITL because they are substantially lighter weight and faster to process. A classic case that can yield remarkable performance gains is to directly swap an embedded [perl] or [calc] block with an equivalent [PREFIX-calc] block.&lt;/p&gt;
&lt;p&gt;Let’s take a typical query with little consideration given to whether we use loop tags or ITL, not unlike many I’ve seen where the resource has just become unusably slow. This code originally was developed processing 50 records per page view, but the team using it has requested over time to increase that count.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[query
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    list=1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ml=500 [comment]Ouch! That&amp;#39;s a big N[/comment]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    sql=&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        SELECT *
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        FROM transactions
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        WHERE status = &amp;#39;pending&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ORDER BY order_date DESC
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Order [sql-param order_number]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[if global DEVELOPMENT]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Show [sql-exec stats_crunch][sql-param stats][/sql-exec], only of interest to developers
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[/if]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Date: [convert-date format=&amp;#34;%b %d, %Y at %T&amp;#34;][sql-param order_date][/convert-date]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[if cgi show_inventory]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Inv:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[if cgi show_inventory eq all]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;* Shipped: [either][sql-param is_shipped][or]pending[/either]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;* Count: [inventory type=shipped sku=[sql-param sku]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[/if]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;* On Hand: [inventory type=onhand sku=[sql-param sku]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;* Sold: [inventory type=sold sku=[sql-param sku]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;* Shipping: [inventory type=shipping sku=[sql-param sku]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[/if]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Order details:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;a href=&amp;#34;[area
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                href=order_view
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                form=&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    order=[sql-param order_number]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    show_status=[either][cgi show_status][or]active[/either]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    show_inventory=[cgi show_inventory]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            ]&amp;#34;&amp;gt;View [sql-param order_number]&amp;lt;/a&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[/query]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Considering this block out of context, it doesn’t seem all that unreasonable. However, let’s look at some of the pieces individually and see what can be done.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We use [if] in 3 different circumstances in the block. However, those values they test are static. They don’t change on any iteration. (We are excluding the potential of any other ITL present in the block from changing their values behind the scenes.)&lt;/li&gt;
&lt;li&gt;[convert-date] may be convenient, but it is only one of a number of ways to address date formatting. Our database itself almost certainly has date-formatting routines, but one of the benefits of [convert-date] is you could have a mixed format underlying the data and it can make sense out of the date to some degree. So perhaps that’s why the developer has used [convert-date] here.&lt;/li&gt;
&lt;li&gt;Good chance that stats_crunch() is pretty complicated and that’s why the developer wrote a catalog or global subroutine to handle it. Since we only want to see it in the development environment, it’d be nice if it only ran when it was needed. Right now, because of ITL happening on reparse, stats_crunch() fires for every row even if we have no intention of using its output.&lt;/li&gt;
&lt;li&gt;We need that link to view our order, but on reparse it means ::interpolate_html() has to parse 500 [area] tags along with [either] and [cgi] x 500. All of these tags are lightweight, but parsing numbers are really going to catch up to us here.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Our goal here is to replace any ITL we can with an equivalent use of a loop tag or, absent the ability to remove ITL logically, to wrap that ITL into a subroutine that can itself be called in loop context with [PREFIX-exec]. The first thing I want to address are those [if] and [either] tags, the lowest hanging fruit:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[query
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    list=1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ml=500
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    sql=&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;b&amp;gt;SELECT *,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &amp;#39;[if global DEVELOPMENT]1[/if]&amp;#39; AS is_development,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            [sql-quote][cgi show_inventory][/sql-quote] AS show_inventory,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            COALESCE(is_shipped,&amp;#39;pending&amp;#39;) AS show_inventory_shipped&amp;lt;/b&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        FROM transactions
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        WHERE status = &amp;#39;pending&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ORDER BY order_date DESC
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Order [sql-param order_number]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;b&amp;gt;[if-sql-param is_development]&amp;lt;/b&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Show [sql-exec stats_crunch][sql-param stats][/sql-exec], only of interest to developers
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;b&amp;gt;[/if-sql-param]&amp;lt;/b&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Date: [convert-date format=&amp;#34;%b %d, %Y at %T&amp;#34;][sql-param order_date][/convert-date]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;b&amp;gt;[if-sql-param show_inventory]&amp;lt;/b&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Inv:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;b&amp;gt;[if-sql-param show_inventory eq all]&amp;lt;/b&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;* Shipped: &amp;lt;b&amp;gt;[sql-param show_inventory_shipped]&amp;lt;/b&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;* Count: [inventory type=shipped sku=[sql-param sku]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;b&amp;gt;[/if-sql-param]&amp;lt;/b&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;* On Hand: [inventory type=onhand sku=[sql-param sku]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;* Sold: [inventory type=sold sku=[sql-param sku]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;* Shipping: [inventory type=shipping sku=[sql-param sku]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;b&amp;gt;[/if-sql-param]&amp;lt;/b&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Order details:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;a href=&amp;#34;[area
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                href=order_view
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                form=&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    order=[sql-param order_number]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    show_status=[either][cgi show_status][or]active[/either]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    show_inventory=[cgi show_inventory]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            ]&amp;#34;&amp;gt;View [sql-param order_number]&amp;lt;/a&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[/query]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;By moving those evaluations into the SELECT list of the query, we’ve reduced the number of interpolations to arrive at those static values to 1 or, in the case of the [either] tag, 0 as we’ve offloaded the calculation entirely to the database. If is_shipped could be something perly false but not null, we would have to adjust our field accordingly, but in either case could still be easily managed as a database calculation. Moreover, by swapping in [if-sql-param is_development] for [if global DEVELOPMENT], we have kept stats_crunch() from running at all when in the production environment.&lt;/p&gt;
&lt;p&gt;Next, we’ll consider [convert-date]:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Date: [convert-date format=&amp;#34;%b %d, %Y at %T&amp;#34;][sql-param order_date][/convert-date]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;My first attempt would be to address this similarly to the [if] and [either] conditions, and try to render the formatted date from a database function as an aliased field. However, let’s assume the underlying structure of the data varies and that’s not easily accomplished, and we still want [convert-date]. Luckily, Interchange supports that same tag as a filter, and [PREFIX-filter] is a loop tag:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Date: &amp;lt;b&amp;gt;[sql-filter convert_date.&amp;#34;%b %d, %Y at %T&amp;#34;]&amp;lt;/b&amp;gt;[sql-param order_date]&amp;lt;b&amp;gt;[/sql-filter]&amp;lt;/b&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;[PREFIX-filter] is very handy to keep in mind as many transformation tags have a filter wrapper for them. E.g., [currency] -&amp;gt; [PREFIX-filter currency]. And if the one you’re looking at doesn’t, you can build your own, easily.&lt;/p&gt;
&lt;p&gt;Now to look at that [inventory] tag. The most direct approach assumes that the code inside [inventory] can be run in Safe, which often it can even if [inventory] is global. However, if [inventory] does run-time un-Safe things (such as creating an object) then it may not be possible. In such a case, we would want to create a global sub, like our hypothetical stats_crunch(), and invoke it via [PREFIX-exec]. However, let us assume we can safely (as it were) invoke it via the $Tag object to demonstrate another potent loop option: [PREFIX-sub].&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[if-sql-param show_inventory]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;b&amp;gt;[sql-sub show_inventory]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    my $arg = shift;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    return $Tag-&amp;gt;inventory({ type =&amp;gt; $arg, sku =&amp;gt; $Row-&amp;gt;{sku} });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[/sql-sub]&amp;lt;/b&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Inv:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[if-sql-param show_inventory eq all]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;* Shipped: [sql-param show_inventory_shipped]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;* Count: &amp;lt;b&amp;gt;[sql-exec show_inventory]shipped[/sql-exec]&amp;lt;/b&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[/if-sql-param]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;* On Hand: &amp;lt;b&amp;gt;[sql-exec show_inventory]on_hand[/sql-exec]&amp;lt;/b&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;* Sold: &amp;lt;b&amp;gt;[sql-exec show_inventory]sold[/sql-exec]&amp;lt;/b&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;* Shipping: &amp;lt;b&amp;gt;[sql-exec show_inventory]shipping[/sql-exec]&amp;lt;/b&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[/if-sql-param]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Let’s go over what this gives us:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;[PREFIX-sub] creates an in-line catalog sub that is compiled at the start of processing, before looping actually begins. As such, the [PREFIX-sub] definitions can occur anywhere within the loop body and are then removed from the body to be parsed.&lt;/li&gt;
&lt;li&gt;The body of the [PREFIX-exec] is passed to the sub as the first argument. We use that here for our static values to the &amp;ldquo;type&amp;rdquo; arg. If we also wanted to access [sql-param sku] from the call, we would have to include that in the body and set up a parser to extract it out of the one (and only) arg we can pass in. Instead, we can reference the $Row hash within the sub body just as we can do when using a [PREFIX-calc], with one minor adjustment to our [query] tag&amp;ndash;we have to indicate to [query] we are operating on a row-hash basis instead of the default row-array basis. We do that by adding the hashref arg to the list:&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[query
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    list=1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ml=500
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;b&amp;gt;hashref=1&amp;lt;/b&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;We still have access to the full functionality of [inventory] but we’ve removed the impact of having to parse that tag 2000 times (in the worst-case scenario) if left as ITL in the query body. If we run into Safe issues, that same sub body can either be created as a pre-compiled global sub or, if available, we can set our catalog AllowGlobal in which case catalog subs will no longer run under Safe.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Finally, all we have left to address is [area] and its args which themselves have ITL. I will leverage [PREFIX-sub] again as an easy way to manage the issue:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;b&amp;gt;[sql-sub area_order_view]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    my $show_status = $CGI-&amp;gt;{show_status} || &amp;#39;active&amp;#39;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    return $Tag-&amp;gt;area({
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        href =&amp;gt; &amp;#39;order_view&amp;#39;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        form =&amp;gt; &amp;#34;order=$Row-&amp;gt;{order_number}\n&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              . &amp;#34;show_status=$show_status\n&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              . &amp;#34;show_inventory=$CGI-&amp;gt;{show_inventory}&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[/sql-sub]&amp;lt;/b&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Order details:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;a href=&amp;#34;&amp;lt;b&amp;gt;[sql-exec area_order_view][/sql-exec]&amp;lt;/b&amp;gt;&amp;#34;&amp;gt;View [sql-param order_number]&amp;lt;/a&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;By packaging all of [area]’s requirements into the sub body, I can address all of the ITL at once.&lt;/p&gt;
&lt;p&gt;So now, let’s put together the entire [query] rewrite to see the final product:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[query
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    list=1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ml=500
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    hashref=1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    sql=&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        SELECT *,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &amp;#39;[if global DEVELOPMENT]1[/if]&amp;#39; AS is_development,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            [sql-quote][cgi show_inventory][/sql-quote] AS show_inventory,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            COALESCE(is_shipped,&amp;#39;pending&amp;#39;) AS show_inventory_shipped
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        FROM transactions
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        WHERE status = &amp;#39;pending&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ORDER BY order_date DESC
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Order [sql-param order_number]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[if-sql-param is_development]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Show [sql-exec stats_crunch][sql-param stats][/sql-exec], only of interest to developers
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[/if-sql-param]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Date: [sql-filter convert_date.&amp;#34;%b %d, %Y at %T&amp;#34;][sql-param order_date][/sql-filter]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[if-sql-param show_inventory]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[sql-sub show_inventory]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    my $arg = shift;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    return $Tag-&amp;gt;inventory({ type =&amp;gt; $arg, sku =&amp;gt; $Row-&amp;gt;{sku} });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[/sql-sub]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Inv:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[if-sql-param show_inventory eq all]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;* Shipped: [sql-param show_inventory_shipped]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;* Count: [sql-exec show_inventory]shipped[/sql-exec]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[/if-sql-param]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;* On Hand: [sql-exec show_inventory]on_hand[/sql-exec]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;* Sold: [sql-exec show_inventory]sold[/sql-exec]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;* Shipping: [sql-exec show_inventory]shipping[/sql-exec]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[/if-sql-param]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[sql-sub area_order_view]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    my $show_status = $CGI-&amp;gt;{show_status} || &amp;#39;active&amp;#39;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    return $Tag-&amp;gt;area({
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        href =&amp;gt; &amp;#39;order_view&amp;#39;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        form =&amp;gt; &amp;#34;order=$Row-&amp;gt;{order_number}\n&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              . &amp;#34;show_status=$show_status\n&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              . &amp;#34;show_inventory=$CGI-&amp;gt;{show_inventory}&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[/sql-sub]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Order details:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;a href=&amp;#34;[sql-exec area_order_view][/sql-exec]&amp;#34;&amp;gt;View [sql-param order_number]&amp;lt;/a&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[/query]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Voila! Our new query body is functionally identical to the original body, though admittedly a little more complicated to set up. However, the trade-off in efficiency is likely to be substantial.&lt;/p&gt;
&lt;p&gt;I recently worked on a refactor for a client that was overall very similar to the above example, with a desired N value of 250. The code prior to refactoring took ~70s to complete. Once we had completed the refactor using the same tools as I’ve identified here, we brought down processing time to just under 3s, losing no functionality.&lt;/p&gt;
&lt;p&gt;Time taken optimizing Interchange loops will almost always pay dividends.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>WebP images experiment on End Point website</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2014/01/webp-images-experiment-on-end-point/"/>
      <id>https://www.endpointdev.com/blog/2014/01/webp-images-experiment-on-end-point/</id>
      <published>2014-01-28T00:00:00+00:00</published>
      <author>
        <name>Jon Jensen</name>
      </author>
      <content type="html">
        &lt;p&gt;WebP is an image format for RGB images on the web that supports both lossless (like PNG) and lossy (like JPEG) compression. It was released by Google in September 2010 with open source reference software available under the BSD license, accompanied by a royalty-free public patent license, making it clear that they want it to be widely adopted by any and all without any encumbrances.&lt;/p&gt;
&lt;p&gt;Its main attraction is smaller file size at similar quality level. It also supports an alpha channel (transparency) and animation for both lossless and lossy images. Thus it is the first image format that offers the transparency of PNG in lossy images at much smaller file size, and animation only available in the archaic limited-color GIF format.&lt;/p&gt;
&lt;h3 id=&#34;comparing-quality--size&#34;&gt;Comparing quality &amp;amp; size&lt;/h3&gt;
&lt;p&gt;While considering WebP for an experiment on our own website, we were very impressed by its file size to quality ratio. In our tests it was even better than generally claimed. Here are a few side-by-side examples from our site. You’ll only see the WebP version if your browser supports it:&lt;/p&gt;
&lt;table cellspacing=&#34;10&#34;&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;img alt=&#34;&#34; height=&#34;133&#34; src=&#34;/blog/2014/01/webp-images-experiment-on-end-point/marina_lohova.jpg&#34; width=&#34;133&#34;/&gt;&lt;br/&gt;13,420 bytes JPEG&lt;/td&gt;
&lt;td&gt;&lt;img alt=&#34;&#34; height=&#34;133&#34; src=&#34;/blog/2014/01/webp-images-experiment-on-end-point/marina_lohova.webp&#34; width=&#34;133&#34;/&gt;&lt;br/&gt;2776 bytes WebP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img alt=&#34;&#34; height=&#34;133&#34; src=&#34;/blog/2014/01/webp-images-experiment-on-end-point/josh_williams.jpg&#34; width=&#34;133&#34;/&gt;&lt;br/&gt;14,734 bytes JPEG&lt;/td&gt;
&lt;td&gt;&lt;img alt=&#34;&#34; height=&#34;133&#34; src=&#34;/blog/2014/01/webp-images-experiment-on-end-point/josh_williams.webp&#34; width=&#34;133&#34;/&gt;&lt;br/&gt;3386 bytes WebP&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;The original PNG images were converted by ImageMagick to JPEG, and by &lt;code&gt;cwebp -q 80&lt;/code&gt; to WebP. I think we probably should increase the WebP quality a bit to keep a little of the facial detail that flattens out, but it’s amazing how good these images look for file sizes that are only 17% and 23% of the JPEG equivalent.&lt;/p&gt;
&lt;p&gt;One of our website’s background patterns has transparency, making the PNG format a necessity, but it also has a gradient, which PNG compression is particularly inefficient with. WebP is a major improvement there, at 13% the size of the PNG. The image is large so I won’t show it here, but you can follow the links if you’d like to see it:&lt;/p&gt;
&lt;table cellspacing=&#34;15&#34;&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td align=&#34;right&#34;&gt;337,186 bytes&lt;/td&gt;&lt;td&gt;&lt;a href=&#34;/blog/2014/01/webp-images-experiment-on-end-point/container-pattern.png&#34;&gt;container-pattern.png&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&#34;right&#34;&gt;43,270 bytes&lt;/td&gt;&lt;td&gt;&lt;a href=&#34;/blog/2014/01/webp-images-experiment-on-end-point/container-pattern.webp&#34;&gt;container-pattern.webp&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h3 id=&#34;browser-support&#34;&gt;Browser support&lt;/h3&gt;
&lt;p&gt;So, what is the downside? WebP is currently natively supported only in Chrome and Opera among the major browsers, though amazingly, support for other browsers can be added via WebPJS, a JavaScript WebP renderer.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update! As of 2021, all current major browsers support WebP image rendering.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Why don’t the other browsers add support given the liberal license? Especially Firefox you’d expect to support it. In fact a patch has been pending for years, and a debate about adding support still smolders. Why?&lt;/p&gt;
&lt;p&gt;WebP does not yet support progressive rendering, Exif tagging, non-RGB color spaces such as CMYK, and is limited to 16,384 pixels per side. Some Firefox developers feel that it would do the Internet community a disservice to support an image format still under development and cause uncertain levels of support in various clients, so they will not accept WebP in its current state.&lt;/p&gt;
&lt;p&gt;Many batch image-processing tools now support WebP, and there is a free Photoshop plug-in for it. Some websites are quietly using it just because of the cost savings due to reduced bandwidth.&lt;/p&gt;
&lt;p&gt;For our first experiment serving WebP images from the End Point website, I decided to serve WebP images only to browsers that claim to be able to support it. They advertise that support in this HTTP request header:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Accept: image/webp,*/*;q=0.8&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;That says explicitly that the browser can render image/webp, so we just need to configure the server to send WebP images. One way to do that is in the application server, by having it send URLs pointing to WebP files.&lt;/p&gt;
&lt;p&gt;Let’s plan to have both common format (JPEG or PNG) and WebP files side by side, and then try a way that is transparent to the application and can be enabled or disabled very easily.&lt;/p&gt;
&lt;h3 id=&#34;web-server-rewrites&#34;&gt;Web server rewrites&lt;/h3&gt;
&lt;p&gt;It’s possible to set up the web server to transparently serve WebP instead of JPEG or PNG if a matching file exists. Based on some examples other people posted, we used this nginx configuration:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;set $webp &amp;#34;&amp;#34;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;set $img &amp;#34;&amp;#34;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;if ($http_accept ~* &amp;#34;image/webp&amp;#34;) { set $webp &amp;#34;can&amp;#34;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;if ($request_filename ~* &amp;#34;(.*)\.(jpe?g|png)$&amp;#34;) { set $img $1.webp; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;if (-f $img) { set $webp &amp;#34;$webp-have&amp;#34;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;if ($webp = &amp;#34;can-have&amp;#34;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    add_header Vary Accept;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    rewrite &amp;#34;(.*)\.\w+$&amp;#34; $1.webp break;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    break;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It’s also good to add to /etc/nginx/mime.types:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;image/webp .webp&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;so that .webp files are served with the correct MIME type instead of the default application/octet-stream, or worse, text/plain with perhaps a bogus character set encoding.&lt;/p&gt;
&lt;p&gt;Then we just make sure identically-named .webp files match .png or .jpg files, such as those for our examples above:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;-rw-rw-r-- 337186 Nov  6 14:10 container-pattern.png
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;-rw-rw-r--  43270 Jan 28 08:14 container-pattern.webp
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;-rw-rw-r--  14734 Nov  6 14:10 josh_williams.jpg
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;-rw-rw-r--   3386 Jan 28 08:14 josh_williams.webp
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;-rw-rw-r--  13420 Nov  6 14:10 marina_lohova.jpg
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;-rw-rw-r--   2776 Jan 28 08:14 marina_lohova.webp&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;A request for a given $file.png will work as normal in browsers that don’t advertise WebP support, while those that do will instead receive the $file.webp image.&lt;/p&gt;
&lt;p&gt;The image is still being requested with a name ending in .jpg or .png, but that’s just a name as far as both browser and server are concerned, and the image type is determined by the MIME type in the HTTP response headers (and/or by looking at the file’s magic numbers). So the browser will have a file called $something.jpg in the DOM and in its cache, but it will actually be a WebP file. That’s ok, but could be confusing to users who save the file for whatever reason and find it isn’t actually the JPEG they were expecting.&lt;/p&gt;
&lt;h3 id=&#34;301302-redirect-option&#34;&gt;301/302 redirect option&lt;/h3&gt;
&lt;p&gt;One remedy for that is to serve the WebP file via a 301 or 302 redirect instead of transparently in the response, so that the browser knows it’s dealing with a different file named $something.webp. To do that we changed the nginx configuration like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;rewrite &amp;#34;(.*)\.\w+$&amp;#34; $1.webp permanent;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;That adds a little bit of overhead, around 100-200 bytes unless large cookies are sent in the request headers, and another network round-trip or two, though it’s still a win with the reduced file sizes we saw. However, I found that it isn’t even necessary right now due to an interesting behavior in Chrome that may even be intentional to cope with this very situation. (Or it may be a happy accident.)&lt;/p&gt;
&lt;h3 id=&#34;chrome-image-download-behavior&#34;&gt;Chrome image download behavior&lt;/h3&gt;
&lt;p&gt;Versions of Chrome I tested only send the Accept: image/webp [etc.] request header when fetching images from an HTML page, not when you manually request a single file or asking the browser to save the image from the page by right-clicking or similar. In those cases the Accept header is not sent, so the server doesn’t know the browser supports WebP, so you get the JPEG or PNG you asked for. That was actually a little confusing to hunt down by sniffing the HTTP traffic on the wire, but it may be a nice thing for users as long as WebP is still less-known.&lt;/p&gt;
&lt;h3 id=&#34;batch-conversion&#34;&gt;Batch conversion&lt;/h3&gt;
&lt;p&gt;It’s fun to experiment, but we needed to actually get all the images converted for our website. Surprisingly, even converting from JPEG isn’t too bad, though you need a higher quality setting and the file size will be larger. Still, for best image quality at the smallest file size, we wanted to start with original PNG images, not recompress JPEGs.&lt;/p&gt;
&lt;p&gt;To make that easy, we wrote &lt;a href=&#34;https://gist.github.com/jonjensen/8677031&#34;&gt;two shell scripts&lt;/a&gt; for Linux, bash, and cwebp. We found a few exceptional images that were larger in WebP than in PNG or JPEG, so the script deletes any WebP file that is not smaller, and our nginx configuration will in that case not find a .webp file and will serve the original PNG or JPEG.&lt;/p&gt;
&lt;h3 id=&#34;full-page-download-sizes-compared&#34;&gt;Full-page download sizes compared&lt;/h3&gt;
&lt;p&gt;Here are performance tests run by WebPageTest.org using Chrome 32 on Windows 7 on a simulated cable Internet connection. The total download size difference is most impressive, and on a slower mobile network or with higher latency (greater distance from the server) would affect the download time more.&lt;/p&gt;
&lt;table id=&#34;pagespeeds&#34;&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;th align=&#34;center&#34; rowspan=&#34;2&#34;&gt;Page URL&lt;/th&gt;
&lt;th align=&#34;center&#34; colspan=&#34;2&#34;&gt;With WebP&lt;/th&gt;
&lt;th align=&#34;center&#34; colspan=&#34;2&#34;&gt;Without WebP&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th align=&#34;center&#34;&gt;Bytes&lt;/th&gt;
&lt;th align=&#34;center&#34;&gt;Time&lt;/th&gt;
&lt;th align=&#34;center&#34;&gt;Bytes&lt;/th&gt;
&lt;th align=&#34;center&#34;&gt;Time&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;https://www.endpointdev.com/&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;374 KB&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;2.9s&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;850 KB&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;3.4s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;https://www.endpointdev.com/team/&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;613 KB&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;3.6s&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;1308 KB&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;4.1s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;This article is not even close to a comprehensive shootout between WebP and other image types. There are other sites that consider the image format technical details more closely and have well-chosen sample images.&lt;/p&gt;
&lt;p&gt;My purpose here was to convert a real website in bulk to WebP without hand-tuning individual images or spending too much time on the project overall, and to see if the overall infrastructure is easy enough to set up, and the download size and speed improved enough to make it worth the trouble, and get real-world experience with it to see if we can recommend it for our clients, and in which situations.&lt;/p&gt;
&lt;p&gt;So far it seems worth it, and we plan to continue using WebP on our website. With empty browser caches, visit &lt;a href=&#34;/&#34;&gt;www.endpointdev.com&lt;/a&gt; using Chrome and then one of the browsers that doesn’t support WebP, and see if you notice a speed difference on first load, or any visual difference.&lt;/p&gt;
&lt;p&gt;I hope to see WebP further developed and more widely supported.&lt;/p&gt;
&lt;h3 id=&#34;further-reading&#34;&gt;Further reading&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://developers.google.com/speed/webp/&#34;&gt;WebP project home&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://en.wikipedia.org/wiki/WebP&#34;&gt;Wikipedia on WebP&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Firefox debate: &lt;a href=&#34;https://bugzilla.mozilla.org/show_bug.cgi?id=600919&#34;&gt;bug #600919&lt;/a&gt;, &lt;a href=&#34;https://bugzilla.mozilla.org/show_bug.cgi?id=856375&#34;&gt;bug #856375&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://webpjs.appspot.com/&#34;&gt;WebPJS&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

      </content>
    </entry>
  
    <entry>
      <title>The Real Cost of Data Roundtrip</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2012/10/the-real-cost-of-data-roundtrip/"/>
      <id>https://www.endpointdev.com/blog/2012/10/the-real-cost-of-data-roundtrip/</id>
      <published>2012-10-03T00:00:00+00:00</published>
      <author>
        <name>Szymon Lipiński</name>
      </author>
      <content type="html">
        &lt;p&gt;Sometimes you need to perform some heavy database operations. I don&amp;rsquo;t know why very often programmers are afraid of using databases for that. They usually have some fancy ORM which performs all the operations, and the only way to change the data is to make some SELECT * from a table, create a bunch of unneeded objects, change one field, convert those changed objects into queries and send that to the database.&lt;/p&gt;
&lt;p&gt;Have you ever thought about the cost of the roundtrip of data? The cost of getting all the data from database just to send changed data into the database? Why do that if there would be much faster way of achieving the same results?&lt;/p&gt;
&lt;p&gt;Imagine that you have quite a heavy operation. Let&amp;rsquo;s make something which normally databases cannot do, some more complicated operation. Many programmers just don&amp;rsquo;t know that there is any other way than writing this in the application code. Let&amp;rsquo;s change all the HTML entities into real characters.&lt;/p&gt;
&lt;p&gt;The HTML entities are a way of writing many different characters in HTML. This way you can write for instance the Euro currency sign &amp;ldquo;€&amp;rdquo; in HTML even if you don&amp;rsquo;t have it on your keyboard. You just have to write € or € instead. I don&amp;rsquo;t have to, as when I use UTF-8 encoding and write this character directly, it should be showed normally. What&amp;rsquo;s more I have this character on my keyboard.&lt;/p&gt;
&lt;p&gt;I will convert the text stored in database changing all the htmlentities into real unicode characters. I will do it using three different methods.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The first will be a simple query run inside PostgreSQL&lt;/li&gt;
&lt;li&gt;The second will be an external program which downloads the text column from database, changes it externally and loads into database.&lt;/li&gt;
&lt;li&gt;The third method will be almost the same as the second, however it will download whole rows.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;generate-data&#34;&gt;Generate Data&lt;/h3&gt;
&lt;p&gt;So, for this test I need to have some data. Let&amp;rsquo;s write a simple data generator.&lt;/p&gt;
&lt;p&gt;First, a simple function for returning a random number within the given range.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;CREATE&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;FUNCTION&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;random(&lt;span style=&#34;color:#038&#34;&gt;INTEGER&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#038&#34;&gt;INTEGER&lt;/span&gt;)&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;RETURNS&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#038&#34;&gt;INTEGER&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;AS&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;$$&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;SELECT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;floor&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;(&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;+&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;(&lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;2&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;-&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;+&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;)&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;*&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;random())::&lt;span style=&#34;color:#038&#34;&gt;INTEGER&lt;/span&gt;;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;$$&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;LANGUAGE&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;SQL&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now the function for generating random texts of random length filled with the HTML entities.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;CREATE&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;FUNCTION&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;generate_random_string()&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;RETURNS&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#038&#34;&gt;TEXT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;AS&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;$$&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;DECLARE&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;items&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#038&#34;&gt;TEXT&lt;/span&gt;[]&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;=&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#038&#34;&gt;ARRAY&lt;/span&gt;[&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;AAAA&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;BBBB&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;CCCC&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;DDDD&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;EEEE&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;FFFF&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;GGGG&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;HHHH&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;IIII&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;JJJJ&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;KKKK&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;LLLL&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;MMMM&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;NNNN&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;OOOO&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;PPPP&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;QQQQ&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;RRRR&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;SSSS&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;TTTT&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;UUUU&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;VVVV&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;WWWW&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;XXXX&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;YYYY&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;ZZZZ&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;amp;amp;&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;amp;#34;&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;amp;#39;&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;amp;#38;&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;amp;#60;&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;amp;#62;&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;amp;#162;&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;amp;#163;&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;amp;#164;&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;amp;#165;&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;amp;#166;&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;amp;#167;&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;amp;#168;&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;amp;#169;&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;amp;#170;&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;amp;#171;&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;amp;#172;&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;amp;#173;&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;amp;#174;&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;amp;#175;&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;amp;#176;&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;amp;#177;&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;amp;#178;&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;amp;#179;&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;amp;#180;&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;amp;#181;&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;amp;#182;&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;amp;#183;&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;amp;#184;&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;amp;#185;&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;amp;#186;&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;amp;#187;&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;amp;#188;&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;amp;#189;&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;amp;#190;&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;];&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;length&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#038&#34;&gt;INTEGER&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;:=&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;random(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;500&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1500&lt;/span&gt;);&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;result&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#038&#34;&gt;TEXT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;:=&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;items_length&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#038&#34;&gt;INTEGER&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;:=&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;array_length(items,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;);&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;BEGIN&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;FOR&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;x&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;IN&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;..&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;length&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;LOOP&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;result&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;:=&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;result&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;||&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;items[&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;random(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;items_length)&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;];&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;END&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;LOOP;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;RETURN&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;result&lt;/span&gt;;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;END&lt;/span&gt;;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;$$&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;LANGUAGE&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;PLPGSQL;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The table for storing the data is created with the following query:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;CREATE&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;TABLE&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;data&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;(&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;id&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#038&#34;&gt;SERIAL&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;PRIMARY&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;KEY&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;padding&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#038&#34;&gt;TEXT&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;t&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#038&#34;&gt;TEXT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then I filled this table using a query generating 50k rows with random data:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;INSERT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;INTO&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;data&lt;/span&gt;(payload,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;t)&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;SELECT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;generate_random_string(),&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;generate_random_string()&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;FROM&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;generate_series(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;50&lt;/span&gt;*&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1000&lt;/span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Let&amp;rsquo;s check the table size:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;SELECT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;pg_size_pretty(pg_relation_size(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;data&amp;#39;&lt;/span&gt;));&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;pg_size_pretty&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#888&#34;&gt;----------------
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;207&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;MB&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;row&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As the table is filled with random data, I need to have two tables with exactly the same data.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;CREATE&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;TABLE&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;query&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;(id&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#038&#34;&gt;SERIAL&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;PRIMARY&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;KEY&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;payload&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#038&#34;&gt;TEXT&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;t&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#038&#34;&gt;TEXT&lt;/span&gt;);&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;CREATE&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;TABLE&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;script&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;(id&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#038&#34;&gt;SERIAL&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;PRIMARY&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;KEY&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;payload&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#038&#34;&gt;TEXT&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;t&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#038&#34;&gt;TEXT&lt;/span&gt;);&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;CREATE&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;TABLE&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;script_full&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;(id&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#038&#34;&gt;SERIAL&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;PRIMARY&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;KEY&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;payload&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#038&#34;&gt;TEXT&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;t&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#038&#34;&gt;TEXT&lt;/span&gt;);&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;INSERT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;INTO&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;query&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;SELECT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;*&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;FROM&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;data&lt;/span&gt;;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;INSERT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;INTO&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;script&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;SELECT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;*&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;FROM&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;data&lt;/span&gt;;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;INSERT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;INTO&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;script_full&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;SELECT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;*&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;FROM&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;data&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;the-tests&#34;&gt;The Tests&lt;/h3&gt;
&lt;h4 id=&#34;sql&#34;&gt;SQL&lt;/h4&gt;
&lt;p&gt;Many programmers think that such operations are not normally available inside a database. However PostgreSQL has quite a nice feature, it can execute functions written in many different languages. For the purpose of this test I will use the language pl/perlu which allows me to use external libraries. I will also use HTML::Entities package for the conversion.&lt;/p&gt;
&lt;p&gt;The function I wrote is quite simple:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;CREATE&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;FUNCTION&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;decode_html_entities(&lt;span style=&#34;color:#038&#34;&gt;text&lt;/span&gt;)&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;RETURNS&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#038&#34;&gt;TEXT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;AS&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;$$&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;use&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;HTML::Entities;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;decode_entities(&lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;$&lt;/span&gt;_[&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;]);&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;$$&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;LANGUAGE&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;plperlu;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The update of the data can be done using the following query:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;UPDATE&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;query&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;SET&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;t&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;=&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;decode_html_entities(t);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;application&#34;&gt;Application&lt;/h4&gt;
&lt;p&gt;In order to have those tests comparable, I will write a simple perl script using exactly the same package for converting html entities.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-perl&#34; data-lang=&#34;perl&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;#!/usr/bin/env perl&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;DBI&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;HTML::Entities&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Encode&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$dbh&lt;/span&gt; = &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;DBI&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#038&#34;&gt;connect&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;DBI:Pg:dbname=test;host=localhost&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;szymon&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;RaiseError&amp;#39;&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;pg_utf8_strings&amp;#39;&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$dbh&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;BEGIN&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$upd&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$dbh&lt;/span&gt;-&amp;gt;prepare(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;UPDATE script SET t = ? WHERE id = ?&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$sth&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$dbh&lt;/span&gt;-&amp;gt;prepare(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;SELECT id, t FROM script&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$sth&lt;/span&gt;-&amp;gt;execute();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;while&lt;/span&gt;(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$row&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$sth&lt;/span&gt;-&amp;gt;fetchrow_hashref()) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$t&lt;/span&gt; = decode_entities( &lt;span style=&#34;color:#369&#34;&gt;$row&lt;/span&gt;-&amp;gt;{&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;t&amp;#39;&lt;/span&gt;} );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$t&lt;/span&gt; = encode(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;UTF-8&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$t&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$upd&lt;/span&gt;-&amp;gt;execute( &lt;span style=&#34;color:#369&#34;&gt;$t&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$row&lt;/span&gt;-&amp;gt;{&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;id&amp;#39;&lt;/span&gt;} );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$dbh&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;COMMIT&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$dbh&lt;/span&gt;-&amp;gt;disconnect();&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;the-worst-application&#34;&gt;The Worst Application&lt;/h4&gt;
&lt;p&gt;There is another terrible idea implemented by programmers too often. Why select only the column you want to change? Let&amp;rsquo;s select all the rows and send them back to database.&lt;/p&gt;
&lt;p&gt;This script will look like this (the important changes are in lines 17 and 23)&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-perl&#34; data-lang=&#34;perl&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;#!/usr/bin/env perl&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;DBI&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;HTML::Entities&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Encode&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$dbh&lt;/span&gt; = &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;DBI&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#038&#34;&gt;connect&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;DBI:Pg:dbname=test;host=localhost&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;szymon&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;RaiseError&amp;#39;&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;pg_utf8_strings&amp;#39;&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$dbh&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;BEGIN&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$upd&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$dbh&lt;/span&gt;-&amp;gt;prepare(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;UPDATE script_all SET t = ? WHERE id = ?&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$sth&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$dbh&lt;/span&gt;-&amp;gt;prepare(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;SELECT id, payload, t FROM script_all&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$sth&lt;/span&gt;-&amp;gt;execute();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;while&lt;/span&gt;(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$row&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$sth&lt;/span&gt;-&amp;gt;fetchrow_hashref()) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$t&lt;/span&gt; = decode_entities( &lt;span style=&#34;color:#369&#34;&gt;$row&lt;/span&gt;-&amp;gt;{&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;t&amp;#39;&lt;/span&gt;} );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$t&lt;/span&gt; = encode(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;UTF-8&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$t&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$upd&lt;/span&gt;-&amp;gt;execute( &lt;span style=&#34;color:#369&#34;&gt;$t&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$row&lt;/span&gt;-&amp;gt;{&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;payload&amp;#39;&lt;/span&gt;}, &lt;span style=&#34;color:#369&#34;&gt;$row&lt;/span&gt;-&amp;gt;{&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;id&amp;#39;&lt;/span&gt;} );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$dbh&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;COMMIT&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$dbh&lt;/span&gt;-&amp;gt;disconnect();&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;results&#34;&gt;Results&lt;/h3&gt;
&lt;p&gt;The query using &lt;strong&gt;pl/perlu function&lt;/strong&gt; executed in &lt;strong&gt;26 seconds&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;script&lt;/strong&gt; changing data externally execuded in &lt;strong&gt;2 minutes 10 seconds (5 times slower)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;worst script&lt;/strong&gt; getting and resending whole rows finished in &lt;strong&gt;4 minutes 35 seconds (10 times slower)&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;I used quite a small number of rows. There were just 50k rows (about 200MB). On production servers the numbers are much bigger.&lt;/p&gt;
&lt;p&gt;Just imagine that the code you developed for changing data could run 10 times faster if you&amp;rsquo;d do this in the database.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Speeding Up Integration Tests with PostgreSQL - Follow Up</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2012/06/speeding-up-integration-tests-postgresql_01/"/>
      <id>https://www.endpointdev.com/blog/2012/06/speeding-up-integration-tests-postgresql_01/</id>
      <published>2012-06-27T00:00:00+00:00</published>
      <author>
        <name>Szymon Lipiński</name>
      </author>
      <content type="html">
        &lt;p&gt;Last week I wrote a blog article about &lt;a href=&#34;/blog/2012/06/speeding-up-integration-tests-postgresql/&#34;&gt;speeding up integration tests using PostgreSQL&lt;/a&gt;. I proposed there changing a couple of PostgreSQL cluster settings. The main drawback of this method is that those settings need to be changed for the whole cluster. When you have some important data in other databases, you can have a trouble.&lt;/p&gt;
&lt;p&gt;In one of the comments Greg proposed using the unlogged table. This feature appeared in PostgreSQL 9.1. The whole difference is that you should use CREATE UNLOGGED TABLE instead of CREATE TABLE for creating all your tables.&lt;/p&gt;
&lt;p&gt;For the unlogged table, the data is not written to the write-ahead log. All inserts, updates and deletes are much faster, however the table will be truncated at the server crash or unclean shutdown. Such table is not replicated to standby servers, which is obvious as there are replicated write-ahead logs. What is more important, the indexes created on unlogged tables are unlogged as well.&lt;/p&gt;
&lt;p&gt;All the things I describe here are for integrations tests. When database crashes, then all the tests should be restarted and should prepare the database before running, so I really don’t care what happens with the data when something crashes.&lt;/p&gt;
&lt;p&gt;The bad thing about unlogged tables is that you cannot change normal table to unlogged. There is nothing like:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;ALTER&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;TABLE&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;SET&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;UNLOGGED;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The easiest way which I found for changing the table into unlogged was to create a database dump and add UNLOGGED to all the table creation commands. To have it a little bit faster, I used this command:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pg_dump pbench | sed &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;s/^CREATE TABLE/CREATE UNLOGGED TABLE/&amp;#39;&lt;/span&gt; &amp;gt; pbench.dump.sql&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This time I will just delete all the tables in the database and load this dump before running the tests instead of using pg_bench for generating the data:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;psql -c &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;drop database pbench&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;psql -c &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;create database pbench&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;psql pbench &amp;lt; pbench.dump.sql&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Time for tests. The previous tests results are in the previous blog article. I’m using standard PostgreSQL settings (the secure ones) and the same scale value for pg_bench.&lt;/p&gt;
&lt;p&gt;The tests were made using exactly the same command as last time:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;./pgbench -h localhost -c &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt; -j &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt; -T &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;300&lt;/span&gt; pbench&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Below are results combined with the results from previous article.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;table id=&amp;#34;testres&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;thead&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &amp;lt;tr&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;th class=&amp;#34;noborder&amp;#34;&amp;gt; &amp;lt;/th&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;th colspan=&amp;#34;3&amp;#34;&amp;gt; number of clients and threads &amp;lt;/th&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &amp;lt;/tr&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &amp;lt;tr&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;th class=&amp;#34;noborder&amp;#34;&amp;gt; &amp;lt;/th&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;th&amp;gt;1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;2
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;3
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;/thead&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;tbody&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &amp;lt;tr&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;td class=&amp;#34;name&amp;#34;&amp;gt;normal settings&amp;lt;/td&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;td&amp;gt;78 tps&amp;lt;/td&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;td&amp;gt;80 tps&amp;lt;/td&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;td&amp;gt;99 tps&amp;lt;/td&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &amp;lt;/tr&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &amp;lt;tr&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;td class=&amp;#34;name&amp;#34;&amp;gt;dangerous settings&amp;lt;/td&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;td&amp;gt;414 tps&amp;lt;/td&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;td&amp;gt;905 tps&amp;lt;/td&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;td&amp;gt;1215 tps&amp;lt;/td&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &amp;lt;/tr&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &amp;lt;tr&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;td class=&amp;#34;name&amp;#34;&amp;gt;unlogged table&amp;lt;/td&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;td&amp;gt;420 tps&amp;lt;/td&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;td&amp;gt;720 tps&amp;lt;/td&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;td&amp;gt;1126 tps&amp;lt;/td&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &amp;lt;/tr&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;/tbody&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/table&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As you can see, the efficiency with unlogged tables is almost as good as with the unsafe settings. The great thing is that it doesn’t influence other databases in the cluster, so you can use safe/default settings for other databases, and only use unlogged tables for the integration tests, which should be much faster now.&lt;/p&gt;
&lt;p&gt;This solution works only with the PostgreSQL 9.1 and newer. If you have older PostgreSQL you have to use the previous method with unsafe settings, or better: just upgrade the database.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Website Performance Boot Camp at UTOSC 2012</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2012/05/website-performance-boot-camp-at-utosc/"/>
      <id>https://www.endpointdev.com/blog/2012/05/website-performance-boot-camp-at-utosc/</id>
      <published>2012-05-21T00:00:00+00:00</published>
      <author>
        <name>Jon Jensen</name>
      </author>
      <content type="html">
        &lt;p&gt;I’ll keep brief my last post about this year’s Utah Open Source Conference.&lt;/p&gt;
&lt;p&gt;I was asked to give on both day one and day two a talk called “Website Performance Boot Camp” which carried this brief description:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;What’s the difference between a snappy website and a sloth that you turn away from in frustration? A lot of little things, usually. It’s rarely worth doing 100% of the optimization you could do, but getting 75% of the way isn’t hard if you know where to look.&lt;/p&gt;
&lt;p&gt;We’ll look at HTTP caching, compression, proxying, CDNs, CSS sprites, minification, and more, how to troubleshoot, and what’s best to leave alone when you have limited time or tolerance for risk.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Here is the video recording of the first time I presented the talk. (The technician noted its audio was “a little hot”.)&lt;/p&gt;
&lt;iframe allowfullscreen=&#34;&#34; frameborder=&#34;0&#34; height=&#34;315&#34; src=&#34;https://www.youtube.com/embed/ItkF8V7DzyQ?rel=0&#34; width=&#34;560&#34;&gt;&lt;/iframe&gt;
&lt;p&gt;Use this &lt;a href=&#34;https://www.youtube.com/watch?v=ItkF8V7DzyQ&#34;&gt;Website Performance Boot Camp&lt;/a&gt; direct YouTube video link if the embedded video doesn’t work for you.&lt;/p&gt;
&lt;p&gt;The slides for this &lt;a href=&#34;https://jon.swelter.net/talks/utosc-2012/website-performance-boot-camp.html&#34;&gt;Website Performance Boot Camp&lt;/a&gt; presentation are available.&lt;/p&gt;
&lt;p&gt;Thanks again to the conference organizers and the other speakers and sponsors, and the nice venue &lt;a href=&#34;https://www.uvu.edu/&#34;&gt;Utah Valley University&lt;/a&gt;, for making it a great conference!&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Interchange Search Caching with “Permanent More”</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2012/01/interchange-search-caching-with/"/>
      <id>https://www.endpointdev.com/blog/2012/01/interchange-search-caching-with/</id>
      <published>2012-01-02T00:00:00+00:00</published>
      <author>
        <name>Mark Johnson</name>
      </author>
      <content type="html">
        &lt;p&gt;Most sites that use Interchange take advantage of Interchange’s “more lists”. These are built-in tools that support an Interchange “search” (either the search/scan action, or result of direct SQL via [query]) to make it very easy to paginate results. Under the hood, the more list is a drill-in to a cached “search object”, so each page brings back a slice from the cache of the original search. There are extensive ways to modify the look and behavior of more lists and, with a bit of effort, they can be configured to meet design requirements.&lt;/p&gt;
&lt;p&gt;Where more lists tend to fall short, however, is with respect to SEO. There are two primary SEO deficiencies that get business stakeholders’ attention:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;There is little control over the construction of the URLs for more lists. They leverage the scan actionmap and contain a hash key for the search object and numeric data to identify the slice and page location. They possess no intrinsic value in identifying the content they reference.&lt;/li&gt;
&lt;li&gt;The search cache by default is ephemeral and session-specific. This means all those results beyond page 1 the search engine has cataloged will result in dead links for search users who try to land directly on the more-listed pages.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It is the latter issue that I wish to address because there is—​and has been for some time now—​a simple mechanism called “permanent more” to remedy the default behavior.&lt;/p&gt;
&lt;p&gt;You can leverage “permanent more” by adding the boolean &lt;strong&gt;mv_more_permanent&lt;/strong&gt;, or the shorthand &lt;strong&gt;pm&lt;/strong&gt;, to your search conditions. E.g.:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Link:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;a href=&amp;#34;[area search=&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        co=1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sf=category
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        se=Foo
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        op=rm
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        more=1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ml=5
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;b&amp;gt;pm=1&amp;lt;/b&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;#34;]&amp;#34;&amp;gt;All Foos&amp;lt;/a&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Loop:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [loop search=&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        co=1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sf=category
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        se=Foo
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        op=rm
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        more=1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ml=5
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;b&amp;gt;pm=1&amp;lt;/b&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;#34;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ...loop body with [more-list]...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [/loop]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Query:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [query
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        list=1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        more=1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ml=10
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;b&amp;gt;pm=1&amp;lt;/b&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sql=&amp;#34;SELECT * FROM products WHERE category LIKE &amp;#39;%Foo%&amp;#39;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ...same as loop but with 10 matches/page...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [/query]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If the initial search is defined with the “permanent more” setting, it will produce the following adjustments:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The hash key used to store and identify the search cache is deterministic based on the search conditions. Many searches for Interchange are category driven. Thus, all end users who wish to browse a category end up clicking identical links, which create duplicate search caches, belonging uniquely to them. With permanent more, they all share the same cache, with the same identifier. As long as the search conditions don’t change, neither does the cache identifier. Even as the cache is refreshed with new executions of the search, the object remains in the same location. Thus, the results a search engine produced this morning reference links still valid now, tomorrow, or next week, provided they reference the same search conditions.&lt;/li&gt;
&lt;li&gt;The cached search object has no session affinity. Any link referencing the cache with the correct hash key has access to the content.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Taken together, “permanent more” removes (for the most part, addressed later) dead links from more lists cataloged by search engines. There are, however, other benefits to “permanent more” beyond those intended as described above:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;As stated in passing, standard Interchange search caching produces duplicate search objects for common search conditions. For a busy site, these caches can have an impact on storage. Typically, maintenance is implemented to clean up cache files for all such files whose age exceeds by some amount the session duration (standard is 48 hours). With permanent more, duplicate caches are eliminated. A cache location is reused by all users with the same search requirements, keeping data-storage requirements for caches to the minimum necessary. As searches change, ophaned caches can still easily be cleaned up as they will immediately start to age with no more access to them necessary for storage.&lt;/li&gt;
&lt;li&gt;For the same reason that “permanent more” resolves search-engine links, it also resolves content management for individual sites using a reverse proxy for caching. Because most (and certainly the easiest) caching keys are based off of URL, the deterministic nature of the hash keys for “permanent more” allows assurance that the cached content in the proxy accurately reflects the search content over time, and that all users will hit the cached resource and not generate new, unique links with varying hash keys.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;One shortcoming of “permanent more” to be aware of is the impact of changing data underneath the search. Even if search conditions do not change, the count and order of matching record sets may. So, e.g., enough products may be removed from a given category to cause the last page of a more list to become empty, which would cause any specific link into that page to become dead. More minor, but still a possibility, is the introduction or removal of products so that a particularly searched-for term has been “bumped” to another page within the search cache since the last time the search engine crawled the more lists. For searches backed by particularly volatile data, “permanent more” may not be sufficient to address search-engine or caching demands.&lt;/p&gt;
&lt;p&gt;Finally, “permanent more” should be avoided for any search features that may cache data sensitive to an individual user. This is unlikely to happen as, under most circumstances, the configuration of the search itself will change based on the unique characteristics of the user executing the search (e.g., a username included in a query to review order history). However, it is still possible that context-sensitive information could be stored in the search object and, if so, all other users with access to the more lists would have access to that information.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>A Performance Case Study</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2011/02/performance-case-study/"/>
      <id>https://www.endpointdev.com/blog/2011/02/performance-case-study/</id>
      <published>2011-02-09T00:00:00+00:00</published>
      <author>
        <name>Steph Skardal</name>
      </author>
      <content type="html">
        &lt;table cellpadding=&#34;5&#34; cellspacing=&#34;0&#34; width=&#34;100%&#34;&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td valign=&#34;top&#34;&gt;
&lt;p&gt;I’m a sucker for a good performance case study. So, when I came across a general request for performance improvement suggestions at &lt;a href=&#34;https://inspiredology.com/&#34;&gt;Inspiredology&lt;/a&gt;, I couldn’t help but experiment a bit.&lt;/p&gt;
&lt;p&gt;The site runs on WordPress and is heavy on the graphics as it’s a site geared towards web designers. I inquired to the site administrators about grabbing a static copy of their home page and using it for a case study on our blog. My tools of choice for optimization were &lt;a href=&#34;https://www.webpagetest.org/&#34;&gt;webpagetest.org&lt;/a&gt; and &lt;a href=&#34;http://yslow.org/&#34;&gt;YSlow&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Here are the results of a 4-step optimization in visual form:&lt;/p&gt;
&lt;table cellpadding=&#34;0&#34; celspacing=&#34;0&#34; width=&#34;100%&#34;&gt;
&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;
&lt;img src=&#34;https://chart.apis.google.com/chart?chxl=0:|Step+%234|Step+%233|Step+%232|Step+%231|Original&amp;chxr=0,0,15|1,0,15&amp;chxt=y,t&amp;chbh=a&amp;chs=300x225&amp;cht=bhg&amp;chco=A2C180&amp;chds=0,15&amp;chd=t:13.412,11.957,10.561,10.243,9.212&amp;chtt=First+Request+Load+Time+(seconds)&#34;/&gt;
&lt;/td&gt;&lt;td&gt;
&lt;img src=&#34;https://chart.apis.google.com/chart?chxl=0:|Step+%234|Step+%233|Step+%232|Step+%231|Original&amp;chxr=0,0,15|1,0,15&amp;chxt=y,t&amp;chbh=a&amp;chs=300x225&amp;cht=bhg&amp;chco=A2C180&amp;chds=0,15&amp;chd=t:7.329,3.717,3.278,2.434,2.563&amp;chtt=Repeat+Request+Load+Time+(seconds)&#34;/&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;/td&gt;
&lt;td align=&#34;center&#34; valign=&#34;top&#34;&gt;
&lt;a href=&#34;/blog/2011/02/performance-case-study/image-2-big.png&#34; onblur=&#34;try {parent.deselectBloggerImageGracefully();} catch(e) {}&#34;&gt;&lt;img alt=&#34;&#34; border=&#34;0&#34; id=&#34;BLOGGER_PHOTO_ID_5571698267043922738&#34; src=&#34;/blog/2011/02/performance-case-study/image-2.png&#34; style=&#34;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 135px; height: 400px;&#34;/&gt;&lt;/a&gt;
&lt;p&gt;Inspiredology’s complete homepage.&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;The graph on the left shows the page load time in seconds for a first time view. Throughout optimization, page load time goes from 13.412 seconds to 9.212 seconds. Each step had a measurable impact. The graph on the right shows the page load time in seconds for a repeated view, and this goes from 7.329 seconds to 2.563 seconds throughout optimization. The first optimization step (CSS spriting and file combination) yielded a large performance improvement. I’m not sure why there’s a slight performance decrease between step 3 and step 4.&lt;/p&gt;
&lt;p&gt;And here’s a summary of the changes involved in each step:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Step 1&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Addition of CSS Sprites: I &lt;a href=&#34;/blog/2010/09/css-sprites/&#34;&gt;wrote about CSS Sprites&lt;/a&gt; a while back and A List Apart has an older but still relevant article on CSS Sprites &lt;a href=&#34;https://web.archive.org/web/20110217044912/www.alistapart.com/articles/sprites&#34;&gt;here&lt;/a&gt;. Repeating elements like navigation components, icons, and buttons are suitable for CSS sprites. Article or page-specific images are not typically suitable for CSS sprites. For Inspiredology’s site, I created two sprited images—​one with a large amount of navigation components, and one with some of their large background images. You can find a great tool for building CSS rules from a sprited image &lt;a href=&#34;https://web.archive.org/web/20110217071401/http://www.spritebox.net/&#34;&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Combination of JS and CSS files, where applicable. Any JavaScript or CSS files that are included throughout the site are suitable for combination. Files that can’t be combined include suckerfish JavaScript like Google Analytics or marketing service scripts.&lt;/li&gt;
&lt;li&gt;Moved JavaScript requests to the bottom of the HTML. This is recommended because JavaScript requests block parallel downloading. Moving them to the bottom allows page elements to be downloaded and rendered first, followed by JavaScript loading.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Step 2&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Image compression with jpegtran, pngcrush, convert. I use pngcrush often. I read about jpegtran in &lt;a href=&#34;https://developer.yahoo.com/performance/rules.html&#34;&gt;Yahoo’s Best Practices for Speeding Up Your Web Site&lt;/a&gt;. I &lt;a href=&#34;/blog/2009/12/jpeg-compression-quality-or-quantity/&#34;&gt;wrote a bit about image compression&lt;/a&gt; a while ago and briefly experimented with image compression using imagemagick on Inspiredology’s images.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Step 3&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Addition of expires headers and disabling ETags: These are standard optimization suggestions. &lt;a href=&#34;/team/jon-jensen/&#34;&gt;Jon Jensen&lt;/a&gt; wrote about using these a bit &lt;a href=&#34;/blog/2010/11/speeding-up-spree-demo-site/&#34;&gt;here&lt;/a&gt; and &lt;a href=&#34;/blog/2009/10/performance-optimization-of/&#34;&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Step 4&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Serving gzipped content with mod_deflate: Also a fairly standard optimization suggestion. Although, I should note I had some issues gzipping a couple of the files and since the site was in a temporary location, I didn’t spend much time troubleshoo&lt;/li&gt;
&lt;li&gt;A bit more cleanup of rogue html and CSS files. In particular, there was one HTML file requested that didn’t have any content in it and another that had JavaScript that I appended to the combined JavaScript file (combined&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A side-by-side comparison of webpagetest.org’s original versus step 4 results highlights the reduction of requests in the waterfall and the large reduction in requests on the repeat view:&lt;/p&gt;
&lt;table width=&#34;100%&#34;&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td valign=&#34;top&#34;&gt;
&lt;a href=&#34;/blog/2011/02/performance-case-study/image-3-big.png&#34; onblur=&#34;try {parent.deselectBloggerImageGracefully();} catch(e) {}&#34;&gt;&lt;img alt=&#34;&#34; border=&#34;0&#34; id=&#34;BLOGGER_PHOTO_ID_5571698261206853250&#34; src=&#34;/blog/2011/02/performance-case-study/image-3.png&#34; style=&#34;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 318px; height: 400px;&#34;/&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td valign=&#34;top&#34;&gt;
&lt;a href=&#34;/blog/2011/02/performance-case-study/image-4-big.png&#34; onblur=&#34;try {parent.deselectBloggerImageGracefully();} catch(e) {}&#34;&gt;&lt;img alt=&#34;&#34; border=&#34;0&#34; id=&#34;BLOGGER_PHOTO_ID_5571698261191315554&#34; src=&#34;/blog/2011/02/performance-case-study/image-4.png&#34; style=&#34;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 365px;&#34;/&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h3 id=&#34;what-next&#34;&gt;What Next?&lt;/h3&gt;
&lt;p&gt;At this point, &lt;a href=&#34;https://www.webpagetest.org/&#34;&gt;webpagetest.org&lt;/a&gt; suggests the following changes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Gzipping the remaining components has a potential of reducing total bytes of the first request by ~10%.&lt;/li&gt;
&lt;li&gt;Additional image compression has the potential of reducing total bytes of the first request by about ~6%. This metric is based on their image compression check: “JPEG—​Within 10% of a photoshop quality 50 will pass, up to 50% larger will warn and anything larger than that will fail.” Quite a few of Inspiredology’s jpgs did not pass this test and could be optimized further.&lt;/li&gt;
&lt;li&gt;Use a CDN. This is a common optimization suggestion, but the cost of a CDN isn’t always justified for smaller sites.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I would suggest:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Revisiting CSS spriting to further optimize. I only spent a short time spriting and didn’t work out all the kinks. There were a few requests that I didn’t sprite because they were repeating elements, but repeating elements can be sprited together. Another 5 requests might be eliminated with additional CSS spriting.&lt;/li&gt;
&lt;li&gt;Server-optimization: Inspiredology runs on WordPress. We’ve used the &lt;a href=&#34;https://wordpress.org/plugins/wp-cache/&#34;&gt;wp-cache&lt;/a&gt; plugin for a couple of our clients running WordPress, which I believe helps. But note that the case study presented here is a static page with static assets, so there is obviously a huge gain to be had by optimizing serving images, CSS, and JavaScript.&lt;/li&gt;
&lt;li&gt;Database optimization: Again, there’s no database in play in this static page experiment. But there’s always room for improvement on database optimization. &lt;a href=&#34;/team/josh-tolley/&#34;&gt;Josh Tolley&lt;/a&gt; recently made performance improvements for one of our clients running on Rails with postgreSQL using &lt;a href=&#34;https://bucardo.org/Pgsi/&#34;&gt;pgsi&lt;/a&gt;, our open source postgreSQL performance reporting tool, and had outrageously impressive benchmarked improvements.&lt;/li&gt;
&lt;li&gt;I just read an article about CSS selectors. The combined.css file I created for this case study has 2000 lines. Although there might be only a small win with optimization here, surely optimization and cleanup of that file can be beneficial.&lt;/li&gt;
&lt;li&gt;I recently wrote about several &lt;a href=&#34;/blog/2011/01/jquery-tips-ecommerce/&#34;&gt;jQuery tips&lt;/a&gt;, including performance optimization techniques. This isn’t going to improve the serving of static assets, but it would be another customer-facing enhancement that can improve the usability of the site.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I highly recommend reading &lt;a href=&#34;https://developer.yahoo.com/performance/rules.html&#34;&gt;Yahoo’s Best Practices on Speeding Up Your Web Site&lt;/a&gt;. They have a great summary of performance recommendations, covering the topics described in this article and lots more.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>PostgreSQL 9.0 High Performance Review</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2010/11/postgresql-90-high-performance-review/"/>
      <id>https://www.endpointdev.com/blog/2010/11/postgresql-90-high-performance-review/</id>
      <published>2010-11-30T00:00:00+00:00</published>
      <author>
        <name>David Christensen</name>
      </author>
      <content type="html">
        &lt;p&gt;I recently had the privilege of reading and reviewing the book &lt;a href=&#34;https://www.packtpub.com/big-data-and-business-intelligence/postgresql-90-high-performance&#34;&gt;PostgreSQL 9.0 High Performance&lt;/a&gt; by Greg Smith. While the title of the book suggests that it may be relevant only to PostgreSQL 9.0, there is in fact a wealth of information to be found which is relevant for all community supported versions of Postgres.&lt;/p&gt;
&lt;p&gt;Acheiving the highest performance with PostgreSQL is definitely something which touches all layers of the stack, from your specific disk hardware, OS and filesystem to the database configuration, connection/data access patterns, and queries in use. This book gathers up a lot of the information and advice that I’ve seen bandied about on the IRC channel and the PostgreSQL mailing lists and presents it in one place.&lt;/p&gt;
&lt;p&gt;While seemingly related, I believe some of the main points of the book could be summed up as:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Measure, don’t guess. From the early chapters which cover the lowest-level considerations, such as disk hardware/configuration to the later chapters which cover such topics as query optimization, replication and partitioning, considerable emphasis is placed on determining the metrics by which to measure performance before/after specific changes. This is the only way to determine the impact the changes you make have.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Tailor to your specific needs/workflows. While there are many good rules of thumb out there when it comes to configuration/tuning, this book emphasizes the process of determining/refining those more general numbers to tailoring configuration/setup to your specific database’s needs.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Review the information the database system itself gives you. Information provided by the pg_stat_* views can be useful in identifying bottlenecks in queries, unused/underused indexes.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This book also introduced me to a few goodies which I had not encountered previously. One of the more interesting ones is the pg_buffercache contrib module. This suite of functions allows you to peek at the internals of the shared_buffers cache to get a feel for which relations are heavily accessed on a block-by-block basis. The examples in the book show this being used to more accurately size shared_buffers based on the actual number of accesses to specific portions of different relations.&lt;/p&gt;
&lt;p&gt;I found the book to be well-written (always a plus when reading technical books) and felt it covered quite a bit of depth given its ambitious scope. Overall, it was an informative and enjoyable read.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Speeding up the Spree demo site</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2010/11/speeding-up-spree-demo-site/"/>
      <id>https://www.endpointdev.com/blog/2010/11/speeding-up-spree-demo-site/</id>
      <published>2010-11-02T00:00:00+00:00</published>
      <author>
        <name>Jon Jensen</name>
      </author>
      <content type="html">
        &lt;p&gt;There’s a lot that can be done to speed up Spree, and Rails apps in general. Here I’m not going to deal with most of that. Instead I want to show how easy it is to speed up page delivery using standard HTTP server tuning techniques, demonstrated on &lt;a href=&#34;https://github.com/spree/demo&#34;&gt;demo.spreecommerce.com&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;First, let’s get a baseline performance measure from the excellent webpagetest.org service using their remote Internet Explorer 7 tests:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;First page load time: &lt;strong&gt;2.1 seconds&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Repeat page load time: &lt;strong&gt;1.5 seconds&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The repeat load is faster because the browser has images, JavaScript, and CSS cached, but it still has to check back with the server to make sure they haven’t changed. Full details are &lt;a href=&#34;https://www.webpagetest.org/result/101102_ACDT/&#34;&gt;in this initial report&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The demo.spreecommerce.com site is run on a Xen VPS with 512 MB RAM, CentOS 5 i386, Apache 2.2, and Passenger 2.2. There were several things to tune in the Apache httpd.conf configuration:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;mod_deflate was already enabled. Good. That’s a big help.&lt;/li&gt;
&lt;li&gt;Enable HTTP keepalive: KeepAlive On and KeepAliveTimeout 3&lt;/li&gt;
&lt;li&gt;Limit Apache children to keep RAM available for Rails: StartServers 5, MinSpareServers 2, MaxSpareServers 5&lt;/li&gt;
&lt;li&gt;Limit Passenger pool size to 2 child processes (down from the default 6), to queue extra requests instead of using slow swap memory: PassengerMaxPoolSize 2&lt;/li&gt;
&lt;li&gt;Enable browser &amp;amp; intermediate proxy caching of static files: ExpiresActive On and ExpiresByType image/jpeg “access plus 2 hours” etc. (see below for full example)&lt;/li&gt;
&lt;li&gt;Disable ETags which aren’t necessary once Expires is enabled: FileETag None and Header unset ETag&lt;/li&gt;
&lt;li&gt;Disable unused Apache modules: free up memory by commenting out LoadModule proxy, proxy_http, info, logio, usertrack, speling, userdir, negotiation, vhost_alias, dav_fs, autoindex, most authn_* and authz_* modules&lt;/li&gt;
&lt;li&gt;Disable SSLv2 (for security and PCI compliance, not performance): SSLProtocol all -SSLv2 and SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:-LOW:-SSLv2:-EXP&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;After making these changes, without tuning Rails, Spree, or the database at all, a new webpagetest.org run reports:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;First page load time: &lt;strong&gt;1.2 seconds&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Repeat page load time: &lt;strong&gt;0.4 seconds&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That’s an easy improvement, a reduction of 0.9 seconds for the initial load and 1.1 seconds for a repeat load! Complete details are in &lt;a href=&#34;https://www.webpagetest.org/result/101102_ACF7/&#34;&gt;this follow-on report&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The biggest wins came from enabling HTTP keepalive, which allows serving multiple files from a single HTTP connection, and enabling static file caching which eliminates the majority of requests once the images, JavaScript, and CSS are cached in the browser.&lt;/p&gt;
&lt;p&gt;Note that many of the resource-limiting changes I made above to Apache and Passenger would be too restrictive if more RAM or CPU were available, as is typical on a dedicated server with 2 GB RAM or more. But when running on a memory-constrained VPS, it’s important to put such limits in place or you’ll practically undo any other tuning efforts you make.&lt;/p&gt;
&lt;p&gt;I wrote about these topics a year ago in a blog post about &lt;a href=&#34;/blog/2009/10/performance-optimization-of/&#34;&gt;Interchange ecommerce performance optimization&lt;/a&gt;. I’ve since expanded the list of MIME types I typically enable static asset caching for in Apache. Here’s a sample configuration snippet to put in the &lt;VirtualHost&gt; container in httpd.conf:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ExpiresActive On
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ExpiresByType image/gif   &amp;#34;access plus 2 hours&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ExpiresByType image/jpeg  &amp;#34;access plus 2 hours&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ExpiresByType image/png   &amp;#34;access plus 2 hours&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ExpiresByType image/tiff  &amp;#34;access plus 2 hours&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ExpiresByType text/css    &amp;#34;access plus 2 hours&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ExpiresByType image/bmp   &amp;#34;access plus 2 hours&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ExpiresByType video/x-flv &amp;#34;access plus 2 hours&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ExpiresByType video/mpeg  &amp;#34;access plus 2 hours&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ExpiresByType video/quicktime &amp;#34;access plus 2 hours&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ExpiresByType video/x-ms-asf  &amp;#34;access plus 2 hours&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ExpiresByType video/x-ms-wm   &amp;#34;access plus 2 hours&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ExpiresByType video/x-ms-wmv  &amp;#34;access plus 2 hours&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ExpiresByType video/x-ms-wmx  &amp;#34;access plus 2 hours&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ExpiresByType video/x-ms-wvx  &amp;#34;access plus 2 hours&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ExpiresByType video/x-msvideo &amp;#34;access plus 2 hours&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ExpiresByType application/postscript        &amp;#34;access plus 2 hours&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ExpiresByType application/msword            &amp;#34;access plus 2 hours&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ExpiresByType application/x-javascript      &amp;#34;access plus 2 hours&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ExpiresByType application/x-shockwave-flash &amp;#34;access plus 2 hours&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ExpiresByType image/vnd.microsoft.icon      &amp;#34;access plus 2 hours&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ExpiresByType application/vnd.ms-powerpoint &amp;#34;access plus 2 hours&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ExpiresByType text/x-component              &amp;#34;access plus 2 hours&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Of course you’ll still need to tune your Spree application and database, but why not tune the web server to get the best performance you can there?&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>CSS Sprites and a “Live” Demo</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2010/09/css-sprites/"/>
      <id>https://www.endpointdev.com/blog/2010/09/css-sprites/</id>
      <published>2010-09-06T00:00:00+00:00</published>
      <author>
        <name>Steph Skardal</name>
      </author>
      <content type="html">
        &lt;p&gt;I’ve recently recommended CSS sprites to several clients, but the majority don’t understand what CSS sprites are or what their impact is. In this article I’ll present some examples of using CSS sprites and their impact.&lt;/p&gt;
&lt;p&gt;First, an intro: CSS sprites is a technique that uses a combination of CSS rules and a single background image that is an aggregate of many smaller images to display the image elements on a webpage. The CSS rules set the boundaries and offset that define the part of the image to show. I like to refer to the technique as analogous to the “Ouija board”; the CSS acts as the little [rectangular] magnifying glass to show only a portion of the image.&lt;/p&gt;
&lt;p&gt;It’s important to choose which images should be in a sprite based on how much each image is repeated throughout a site’s design and how often it might be replaced. For example, design border images and icons will likely be included in a sprite since they may be repeated throughout a site’s appearance, but a photo on the homepage that’s replaced daily is not a good candidate to be included in a sprite. I also typically exclude a site’s logo from a sprite since it may be used by externally linking sites. End Point uses CSS sprites, but only for a few elements:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;/blog/2010/09/css-sprites/image-0-big.png&#34; onblur=&#34;try {parent.deselectBloggerImageGracefully();} catch(e) {}&#34;&gt;&lt;img alt=&#34;&#34; border=&#34;0&#34; id=&#34;BLOGGER_PHOTO_ID_5513845585604331394&#34; src=&#34;/blog/2010/09/css-sprites/image-0.png&#34; style=&#34;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 108px; height: 212px;&#34;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;End Point’s CSS sprite image only contains borders and Twitter and LinkedIn images.&lt;/p&gt;
&lt;h3 id=&#34;live-demo&#34;&gt;“Live” Demo&lt;/h3&gt;
&lt;p&gt;I wanted to implement CSS sprites to demonstrate their performance impact. I chose to examine the homepages of my favorite ski resorts in Utah, &lt;a href=&#34;https://www.snowbird.com/&#34;&gt;Snowbird&lt;/a&gt; and &lt;a href=&#34;https://www.alta.com/&#34;&gt;Alta&lt;/a&gt;. First, I used &lt;a href=&#34;https://www.webpagetest.org/&#34;&gt;WebPagetest.org&lt;/a&gt; to get a benchmark for each homepage:&lt;/p&gt;
&lt;table cellpadding=&#34;5&#34; cellspacing=&#34;0&#34; width=&#34;100%&#34;&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td align=&#34;center&#34; style=&#34;background-color:#CCEEFF;&#34;&gt;
&lt;a href=&#34;/blog/2010/09/css-sprites/image-1-big.png&#34; onblur=&#34;try {parent.deselectBloggerImageGracefully();} catch(e) {}&#34;&gt;&lt;img alt=&#34;&#34; border=&#34;0&#34; id=&#34;BLOGGER_PHOTO_ID_5513844443463352978&#34; src=&#34;/blog/2010/09/css-sprites/image-1.png&#34; style=&#34;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 200px; height: 124px;&#34;/&gt;&lt;/a&gt;
Without sprites, Alta’s homepage loaded in 3.440 seconds with a repeat request load time of 1.643 seconds. 22 requests are made on the homepage.
&lt;/td&gt;
&lt;td align=&#34;center&#34; style=&#34;background-color:#FFEECC;&#34;&gt;
&lt;a href=&#34;/blog/2010/09/css-sprites/image-2-big.png&#34; onblur=&#34;try {parent.deselectBloggerImageGracefully();} catch(e) {}&#34;&gt;&lt;img alt=&#34;&#34; border=&#34;0&#34; id=&#34;BLOGGER_PHOTO_ID_5513844445344078146&#34; src=&#34;/blog/2010/09/css-sprites/image-2.png&#34; style=&#34;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 160px; height: 200px;&#34;/&gt;&lt;/a&gt;
Without sprites, Snowbird’s homepage loaded in 7.070 seconds with a repeat request load time of 3.146 seconds. 57 requests are made on the homepage.
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;After I benchmarked the two pages, I downloaded each homepage and its files and examined the images to build a CSS sprite image. I created the sprite images shown below. I chose to exclude the logo from each sprite, in addition to other time-sensitive images. Each sprited image contains navigation elements, icons, and a few homepage specific images.&lt;/p&gt;
&lt;table cellpadding=&#34;5&#34; cellspacing=&#34;0&#34; width=&#34;100%&#34;&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td align=&#34;center&#34; style=&#34;background-color:#CCEEFF;&#34;&gt;
&lt;a href=&#34;/blog/2010/09/css-sprites/image-3-big.gif&#34; onblur=&#34;try {parent.deselectBloggerImageGracefully();} catch(e) {}&#34;&gt;&lt;img alt=&#34;&#34; border=&#34;0&#34; id=&#34;BLOGGER_PHOTO_ID_5513842518135734066&#34; src=&#34;/blog/2010/09/css-sprites/image-3.gif&#34; style=&#34;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 112px;&#34;/&gt;&lt;/a&gt;&lt;br/&gt;
My resulting sprited image for Alta.
&lt;/td&gt;
&lt;td align=&#34;center&#34; style=&#34;background-color:#FFEECC;&#34;&gt;
&lt;a href=&#34;/blog/2010/09/css-sprites/image-4-big.gif&#34; onblur=&#34;try {parent.deselectBloggerImageGracefully();} catch(e) {}&#34;&gt;&lt;img alt=&#34;&#34; border=&#34;0&#34; id=&#34;BLOGGER_PHOTO_ID_5513842528749004610&#34; src=&#34;/blog/2010/09/css-sprites/image-4.gif&#34; style=&#34;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 218px; height: 94px;&#34;/&gt;&lt;/a&gt;&lt;br/&gt;
My resulting sprited image for Snowbird.
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;I updated the HTML to remove individual image requests. Below are some examples:&lt;/p&gt;
&lt;p&gt;Before:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;a href=&amp;#34;https://www.alta.com/contact&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;img src=&amp;#34;./alta_files/banner_contact.gif&amp;#34; name=&amp;#34;ContactUs&amp;#34; width=&amp;#34;100&amp;#34; height=&amp;#34;20&amp;#34; border=&amp;#34;0&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;After:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;a href=&amp;#34;/&amp;#34; class=&amp;#34;sprite&amp;#34; id=&amp;#34;banner_contact&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Before:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;a href=&amp;#34;https://web.archive.org/web/20101128010936/http://shop.alta.com/CS/Browse.aspx?Catalog=AltaRetail&amp;amp;Category=Retail+Items&amp;#34; target=&amp;#34;_self&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;img src=&amp;#34;./alta_files/altaskishoplogo.jpg&amp;#34; alt=&amp;#34;March Madness Logo&amp;#34; width=&amp;#34;180&amp;#34; height=&amp;#34;123&amp;#34; border=&amp;#34;0&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;After:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;a href=&amp;#34;https://web.archive.org/web/20101128010936/http://shop.alta.com/CS/Browse.aspx?Catalog=AltaRetail&amp;amp;Category=Retail+Items&amp;#34; target=&amp;#34;_self&amp;#34; id=&amp;#34;skishoplogo&amp;#34; class=&amp;#34;sprite&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Before:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;div class=&amp;#34;icon&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;img src=&amp;#34;./snowbird_files/icon_less_rain.gif&amp;#34; border=&amp;#34;0&amp;#34; alt=&amp;#34;Sunny&amp;#34; title=&amp;#34;Sunny&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;After:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;div class=&amp;#34;icon sprite less_rain&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Before:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;div&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;input type=&amp;#34;image&amp;#34; src=&amp;#34;./snowbird_files/btn_check_rates.gif&amp;#34; border=&amp;#34;0&amp;#34; alt=&amp;#34;Check Rates&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;After:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;div class=&amp;#34;sprite&amp;#34; id=&amp;#34;check_rates&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;There are a few CSS tips to be aware of during CSS sprite implementation, such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Padding on sprited elements will affect the sprite position.&lt;/li&gt;
&lt;li&gt;Links must have the “display:block” rule (combined with floating rules) to enforce a height and width.&lt;/li&gt;
&lt;li&gt;Parts of the sprite that are repeating may not have any other elements along the repeating axis. For example, in End Point’s sprite, the top and bottom repeating border are in the sprite. No other images may be included in the sprite to the left or right of the borders.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The CSS for the new sprites is shown below. For all sprited elements, a height and width is set in addition to a background position. The height, width, and background position are the rules that define the region of the sprited image to show.&lt;/p&gt;
&lt;p&gt;Alta CSS sprite rules&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-css&#34; data-lang=&#34;css&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;.&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;sprite&lt;/span&gt; { &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;background-image&lt;/span&gt;: &lt;span style=&#34;color:#038&#34;&gt;url&lt;/span&gt;(&lt;span style=&#34;color:#2b2;background-color:#f0fff0&#34;&gt;sprites.gif&lt;/span&gt;); &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;display&lt;/span&gt;: &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;block&lt;/span&gt;; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;float&lt;/span&gt;: &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;left&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;a&lt;/span&gt;#&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;banner_logo&lt;/span&gt; { &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;width&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;100&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;height&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;100&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;div&lt;/span&gt;#&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;banner_top&lt;/span&gt; { &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;width&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;600&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;height&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;100&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;background-position&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;-100&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;a&lt;/span&gt;#&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;banner_home&lt;/span&gt; { &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;width&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;100&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;height&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;20&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;background-position&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;-700&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;a&lt;/span&gt;#&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;banner_contact&lt;/span&gt; { &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;width&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;100&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;height&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;20&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;background-position&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;-700&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;-20&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;a&lt;/span&gt;#&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;banner_sitemap&lt;/span&gt; { &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;width&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;100&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;height&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;20&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;background-position&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;-700&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;-40&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;a&lt;/span&gt;#&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;banner_press&lt;/span&gt; { &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;width&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;100&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;height&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;20&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;background-position&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;-700&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;-60&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;a&lt;/span&gt;#&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;banner_weather&lt;/span&gt; { &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;width&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;100&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;height&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;20&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;background-position&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;-700&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;-80&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;a&lt;/span&gt;#&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;skishoplogo&lt;/span&gt; { &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;width&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;180&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;height&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;123&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;background-position&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;-298&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;123&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;a&lt;/span&gt;#&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;skihistory&lt;/span&gt; { &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;width&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;164&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;height&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;61&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;background-position&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;-479&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;123&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;margin-right&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;40&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;a&lt;/span&gt;#&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;altaenviron&lt;/span&gt; { &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;width&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;298&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;height&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;61&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;background-position&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;123&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;; }&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Snowbird CSS sprite rules&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-css&#34; data-lang=&#34;css&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;.&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;sprite&lt;/span&gt; { &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;background&lt;/span&gt;: &lt;span style=&#34;color:#038&#34;&gt;url&lt;/span&gt;(&lt;span style=&#34;color:#2b2;background-color:#f0fff0&#34;&gt;sprites.gif&lt;/span&gt;); }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;a&lt;/span&gt;.&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;sprite&lt;/span&gt; { &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;display&lt;/span&gt;: &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;block&lt;/span&gt;; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;float&lt;/span&gt;: &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;left&lt;/span&gt;; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;margin-right&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;5&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;border&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;2&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;solid&lt;/span&gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;#FFF&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;div&lt;/span&gt;#&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;headerWeather&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;div&lt;/span&gt;.&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;icon&lt;/span&gt; { &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;width&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;36&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;height&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;26&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;div&lt;/span&gt;.&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;less_rain&lt;/span&gt; { &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;background-position&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;-118&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;-142&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;div&lt;/span&gt;.&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;sunny&lt;/span&gt; { &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;background-position&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;-177&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;-116&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;div&lt;/span&gt;.&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;partly_cloudy&lt;/span&gt; { &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;background-position&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;-138&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;-116&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;a&lt;/span&gt;#&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;facebook&lt;/span&gt; { &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;width&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;66&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;height&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;25&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;background-position&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;-142&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;a&lt;/span&gt;#&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;twitter&lt;/span&gt; { &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;width&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;55&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;height&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;25&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;background-position&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;-66&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;-142&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;a&lt;/span&gt;#&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;youtube&lt;/span&gt; { &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;width&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;48&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;height&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;25&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;background-position&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;-116&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;a&lt;/span&gt;#&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;flickr&lt;/span&gt; { &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;width&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;94&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;height&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;25&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;background-position&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;-48&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;-116&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;a&lt;/span&gt;#&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;picofday&lt;/span&gt; { &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;width&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;218&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;height&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;22&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;border&lt;/span&gt;: &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;none&lt;/span&gt;; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;margin&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;2&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;2&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;div&lt;/span&gt;#&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;check_rates&lt;/span&gt; { &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;width&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;89&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;height&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;21&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;background-position&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;-261&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;margin&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;10&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;float&lt;/span&gt;: &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;right&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;div&lt;/span&gt;#&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;br_corner_light&lt;/span&gt; { &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;width&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;17&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;height&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;14&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;background-position&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;-89&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;-268&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;div&lt;/span&gt;#&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;bl_corner_light&lt;/span&gt; { &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;width&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;17&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;height&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;14&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;background-position&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;-105&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;-268&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;; }&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;After spriting the images shown above, I returned to &lt;a href=&#34;https://www.webpagetest.org/&#34;&gt;WebPagetest.org&lt;/a&gt; to examine the impact of the sprites. In most cases, I maintained the image format to limit the performance change to spriting only. I also did not change HTML or CSS even if I noticed other performance improvement opportunities. Here are the new results:&lt;/p&gt;
&lt;table cellpadding=&#34;5&#34; cellspacing=&#34;0&#34; width=&#34;100%&#34;&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td align=&#34;center&#34; style=&#34;background-color:#CCEEFF;&#34;&gt;
&lt;a href=&#34;/blog/2010/09/css-sprites/image-5-big.png&#34; onblur=&#34;try {parent.deselectBloggerImageGracefully();} catch(e) {}&#34;&gt;&lt;img alt=&#34;&#34; border=&#34;0&#34; id=&#34;BLOGGER_PHOTO_ID_5513844658910543170&#34; src=&#34;/blog/2010/09/css-sprites/image-5.png&#34; style=&#34;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 200px; height: 88px;&#34;/&gt;&lt;/a&gt;
With sprites, Alta’s homepage loaded in 2.768 seconds with a repeat request load time of 1.093 seconds.
&lt;/td&gt;
&lt;td align=&#34;center&#34; style=&#34;background-color:#FFEECC;&#34;&gt;
&lt;a href=&#34;/blog/2010/09/css-sprites/image-6-big.png&#34; onblur=&#34;try {parent.deselectBloggerImageGracefully();} catch(e) {}&#34;&gt;&lt;img alt=&#34;&#34; border=&#34;0&#34; id=&#34;BLOGGER_PHOTO_ID_5513844659980320130&#34; src=&#34;/blog/2010/09/css-sprites/image-6.png&#34; style=&#34;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 186px; height: 200px;&#34;/&gt;&lt;/a&gt;
With sprites, Snowbird’s homepage loaded in 6.289 seconds with a repeat request load time of 2.513 seconds.
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;A summary of differences shows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Both pages decreased the number of requests by 10.&lt;/li&gt;
&lt;li&gt;Alta decreased homepage load by 0.672 seconds, or 20% of the original page load. Snowbird decreased homepage load by 0.781 seconds, or 11% of the original page load.&lt;/li&gt;
&lt;li&gt;On repeat views, Alta’s homepage load would be decreased by 0.665 seconds, or 40% of the original repeated page load. On repeat view, Snowbird’s homepage load would be decreased by 0.633 seconds, or 20% of the original repeat page load.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There is no reason to avoid sprites if a design has repeating elements or icons. An increase in performance will reduce load time for the customer. If a &lt;a href=&#34;https://en.wikipedia.org/wiki/Content_delivery_network&#34;&gt;CDN&lt;/a&gt; is in place, CSS sprites can result in a decreased bandwidth cost. The improved performance also can indirectly positively influence search engine performance since search engines may use performance as an influencing factor in search. This improved performance can also make for a better mobile browsing experience. In our examples, the use of CSS sprites decreased first request page load time by 10-20%, but this amount may vary depending on the frequency of images used in a site’s design.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Efficiency of find -exec vs. find | xargs</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2010/07/efficiency-of-find-exec-vs-find-xargs/"/>
      <id>https://www.endpointdev.com/blog/2010/07/efficiency-of-find-exec-vs-find-xargs/</id>
      <published>2010-07-28T00:00:00+00:00</published>
      <author>
        <name>Jon Jensen</name>
      </author>
      <content type="html">
        &lt;p&gt;This is a quick tip for anyone writing a cron job to purge large numbers of old files.&lt;/p&gt;
&lt;p&gt;Without xargs, this is a pretty common way to do such a purge, in this case of all files older than 31 days:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;find /path/to/junk/files -type f -mtime +31 -exec rm -f {} &lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;But that executes rm once for every single file to be removed, which adds a ton of overhead just to fork and exec rm so many times. Even on modern operating systems that are so efficient with fork, it can easily increase the I/O and load and runtime by 10 times or more than just running a single rm command with a lot of file arguments.&lt;/p&gt;
&lt;p&gt;Instead do this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;find /path/to/junk/files -type f -mtime +31 -print0 | xargs -0 -r rm -f&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;That will run xargs once for each very long list of files to be removed, so the overhead of fork &amp;amp; exec is incurred very rarely, and the job can spend most of its effort actually unlinking files. (The xargs -r option says not to run the command if there is no input to xargs.)&lt;/p&gt;
&lt;p&gt;How long can the argument list to xargs be? It depends on the system, but xargs &amp;ndash;show-limits will tell us. Here&amp;rsquo;s output from a RHEL 5 x86_64 system (using findutils 4.2.27):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;% xargs --show-limits
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Your environment variables take up 2293 bytes
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;POSIX lower and upper limits on argument length: 2048, 129024
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Maximum length of command we could actually use: 126731
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Size of command buffer we are actually using: 126731&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The numbers are similar on Debian Etch and Lenny.&lt;/p&gt;
&lt;p&gt;And here&amp;rsquo;s output from an Ubuntu 10.04 x86_64 system (using findutils 4.4.2):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;% xargs --show-limits
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Your environment variables take up 1370 bytes
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;POSIX upper limit on argument length (this system): 2093734
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;POSIX smallest allowable upper limit on argument length (all systems): 4096
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Maximum length of command we could actually use: 2092364
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Size of command buffer we are actually using: 131072&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Roughly 2 megabytes of arguments is a lot. But even the POSIX minimum of 4 kB is a lot better than processing one file at a time.&lt;/p&gt;
&lt;p&gt;It doesn&amp;rsquo;t usually make much of a difference, but we can tune even more. Make sure the maximum number of files is processed at one time by first changing to the base directory so that the relative pathnames are shorter:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;cd&lt;/span&gt; /path/to/junk/files &amp;amp;&amp;amp; find . -type f -mtime +31 -print0 | xargs -0 -r rm -f&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;That way each file argument is shorter, e.g. ./junkfile compared to /path/to/junk/files/junkfile.&lt;/p&gt;
&lt;p&gt;The above assumes you&amp;rsquo;re using GNU findutils, which includes find -print0 and xargs -0 for processing ASCII NUL-delimited filenames for safety when filenames include embedded spaces, newlines, etc.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Why is my load average so high?</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2010/07/why-is-my-load-average-so-high/"/>
      <id>https://www.endpointdev.com/blog/2010/07/why-is-my-load-average-so-high/</id>
      <published>2010-07-15T00:00:00+00:00</published>
      <author>
        <name>Josh Tolley</name>
      </author>
      <content type="html">
        &lt;p&gt;One of the most common ways people notice there’s a problem with their server is when Nagios, or some other monitoring tool, starts complaining about a high load average. Unfortunately this complaint carries with it very little information about what might be causing the problem. But there are ways around that. On Linux, where I spend most of my time, the load average represents the average number of process in either the &amp;ldquo;run&amp;rdquo; or &amp;ldquo;uninterruptible sleep&amp;rdquo; states. This code snippet will display all such processes, including their process ID and parent process ID, current state, and the process command line:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#c00;font-weight:bold&#34;&gt;#!/bin/sh
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#c00;font-weight:bold&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ps -eo pid,ppid,state,cmd |&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;&lt;/span&gt;    awk &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;$3 ~ /[RD]/ { print $0 }&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Most of the time, this script has simply confirmed what I already anticipated, such as, &amp;ldquo;PostgreSQL is trying to service 20 times as many simultaneous queries as normal.&amp;rdquo; On occasion, however, it’s very useful, such as when it points out that a backup job is running far longer than normal, or when it finds lots of &amp;ldquo;[pdflush]&amp;rdquo; operations in process, indicating that the system was working overtime to write dirty pages to disk. I hope it can be similarly useful to others.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>On Linux, noatime includes nodiratime</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2010/02/on-linux-noatime-includes-nodiratime/"/>
      <id>https://www.endpointdev.com/blog/2010/02/on-linux-noatime-includes-nodiratime/</id>
      <published>2010-02-09T00:00:00+00:00</published>
      <author>
        <name>Jon Jensen</name>
      </author>
      <content type="html">
        &lt;p&gt;Note to performance-tweaking Linux sysadmins, pointed out to me by Selena Deckelmann: On Linux, the filesystem attribute &lt;a href=&#34;https://web.archive.org/web/20100523223021/lwn.net/Articles/244941/&#34;&gt;noatime includes nodiratime&lt;/a&gt;, so there’s no need to say both “noatime,nodiratime” as I once did. (See &lt;a href=&#34;https://www.howtoforge.com/reducing-disk-io-by-mounting-partitions-with-noatime&#34;&gt;this article on atime&lt;/a&gt; for details if you’re not familiar with it.)&lt;/p&gt;
&lt;p&gt;Apparently the nodiratime attribute was added later as a subset of noatime applying only to directories to still offer a bit of performance boost in situations where noatime on files would cause trouble (as with mutt and a few other applications that care about atimes).&lt;/p&gt;
&lt;p&gt;See also the related newer relatime attribute in the mount(8) manpage.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>JPEG compression: quality or quantity?</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2009/12/jpeg-compression-quality-or-quantity/"/>
      <id>https://www.endpointdev.com/blog/2009/12/jpeg-compression-quality-or-quantity/</id>
      <published>2009-12-24T00:00:00+00:00</published>
      <author>
        <name>Daniel Browning</name>
      </author>
      <content type="html">
        &lt;p&gt;There are many aspects of JPEG files that are interesting to web site developers, such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The optimal trade off between quality and file size for any encoder and uncompressed source image.&lt;/li&gt;
&lt;li&gt;Reducing size of an existing JPEG image when the uncompressed source is unavailable, but still finding the same optimal trade-off.&lt;/li&gt;
&lt;li&gt;Comparison of different encoders and/or settings for quality at a given file size.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Two essential factors are file size and image quality. Bytes are objectively measurable, but image quality is much more nebulous. What to one person is a perfectly acceptable image is to another a grotesque abomination of artifacts. So the quality factor is subjective. For example, Steph sent me some images to compare compression artifacts. Here is the first one with three different settings in ImageMagick: 95, 50, and 8:&lt;/p&gt;
&lt;table cellpadding=&#34;0&#34; cellspacing=&#34;2&#34;&gt;
&lt;tbody&gt;&lt;tr&gt;&lt;td valign=&#34;top&#34;&gt;
&lt;a href=&#34;/blog/2009/12/jpeg-compression-quality-or-quantity/image-0.jpeg&#34;&gt;&lt;img src=&#34;/blog/2009/12/jpeg-compression-quality-or-quantity/image-0.jpeg&#34; title=&#34;size: 27K setting: 95&#34;/&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td valign=&#34;top&#34;&gt;&lt;a href=&#34;/blog/2009/12/jpeg-compression-quality-or-quantity/image-1.jpeg&#34;&gt;&lt;img src=&#34;/blog/2009/12/jpeg-compression-quality-or-quantity/image-1.jpeg&#34; title=&#34;size: 8.0K setting: 50&#34;/&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td valign=&#34;top&#34;&gt;
&lt;a href=&#34;/blog/2009/12/jpeg-compression-quality-or-quantity/image-2.jpeg&#34;&gt;&lt;img src=&#34;/blog/2009/12/jpeg-compression-quality-or-quantity/image-2.jpeg&#34; title=&#34;size: 2.9K setting: 8&#34;/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Compare the subtle (or otherwise) differences in the following images (mouseover shows the filesize and compression setting):&lt;/p&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;a href=&#34;/blog/2009/12/jpeg-compression-quality-or-quantity/image-3.jpeg&#34;&gt;&lt;img src=&#34;/blog/2009/12/jpeg-compression-quality-or-quantity/image-3.jpeg&#34; title=&#34;size: 30K setting: 95&#34;/&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href=&#34;/blog/2009/12/jpeg-compression-quality-or-quantity/image-4.jpeg&#34;&gt;&lt;img src=&#34;/blog/2009/12/jpeg-compression-quality-or-quantity/image-4.jpeg&#34; title=&#34;size: 17K setting: 85&#34;/&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;tr&gt;
&lt;td&gt;&lt;a href=&#34;/blog/2009/12/jpeg-compression-quality-or-quantity/image-5.jpeg&#34;&gt;&lt;img src=&#34;/blog/2009/12/jpeg-compression-quality-or-quantity/image-5.jpeg&#34; title=&#34;size: 7.7K setting: 50&#34;/&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href=&#34;/blog/2009/12/jpeg-compression-quality-or-quantity/image-6.jpeg&#34;&gt;&lt;img src=&#34;/blog/2009/12/jpeg-compression-quality-or-quantity/image-6.jpeg&#34; title=&#34;size: 4.1K setting: 20&#34;/&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Mouseover each image for the file size and ImageMagick compression setting. Additional comparisons are below. Each image can be opened in a separate browser tab for easy A/B comparison. I think many would find the setting of 8 to have too many artifacts, even though it’s 10 times smaller than image compressed at a setting of 95. Some would find the setting of 50 to be an acceptable tradeoff between quality and size, since it sends 3.4 times fewer bytes.&lt;/p&gt;
&lt;p&gt;Here is the code I wrote to make the comparison (shell script is great for this stuff):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#c00;font-weight:bold&#34;&gt;#!/bin/bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#c00;font-weight:bold&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;HTML_OUTFILE&lt;/span&gt;=comparison.html
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;lt;html&amp;gt;&amp;#39;&lt;/span&gt; &amp;gt; &lt;span style=&#34;color:#369&#34;&gt;$HTML_OUTFILE&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;write_img_html () {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;size&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;`&lt;/span&gt;du -h --apparent-size &lt;span style=&#34;color:#369&#34;&gt;$1&lt;/span&gt; | cut -f 1&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; [ -n &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$2&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt; ]; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#369&#34;&gt;qual&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;setting: &lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$2&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    cat &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;lt;&amp;lt;EOF &amp;gt;&amp;gt;$HTML_OUTFILE
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;lt;a href=&amp;#34;$1&amp;#34;&amp;gt;&amp;lt;img src=&amp;#34;$1&amp;#34; title=&amp;#34;size: $size $qual&amp;#34;&amp;gt;&amp;lt;/a&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;EOF&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;for&lt;/span&gt; name in image1 image2; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;orig&lt;/span&gt;=&lt;span style=&#34;color:#369&#34;&gt;$name&lt;/span&gt;-original.jpg
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;resized&lt;/span&gt;=&lt;span style=&#34;color:#369&#34;&gt;$name&lt;/span&gt;-300.png
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#038&#34;&gt;echo&lt;/span&gt; Resizing &lt;span style=&#34;color:#369&#34;&gt;$orig&lt;/span&gt; to &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;300&lt;/span&gt; on longest side: &lt;span style=&#34;color:#369&#34;&gt;$resized&lt;/span&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    convert &lt;span style=&#34;color:#369&#34;&gt;$orig&lt;/span&gt; -resize 300x300 &lt;span style=&#34;color:#369&#34;&gt;$resized&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    write_img_html &lt;span style=&#34;color:#369&#34;&gt;$resized&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;lossless&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;for&lt;/span&gt; quality in &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;100&lt;/span&gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;95&lt;/span&gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;85&lt;/span&gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;50&lt;/span&gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;20&lt;/span&gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;8&lt;/span&gt; 1; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#038&#34;&gt;echo&lt;/span&gt; Creating JPEG quality &lt;span style=&#34;color:#369&#34;&gt;$quality&lt;/span&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#369&#34;&gt;jpeg&lt;/span&gt;=&lt;span style=&#34;color:#369&#34;&gt;$name&lt;/span&gt;-300-q-&lt;span style=&#34;color:#369&#34;&gt;$quality&lt;/span&gt;.jpg
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        convert &lt;span style=&#34;color:#369&#34;&gt;$resized&lt;/span&gt; -strip -quality &lt;span style=&#34;color:#369&#34;&gt;$quality&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$jpeg&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        write_img_html &lt;span style=&#34;color:#369&#34;&gt;$jpeg&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$quality&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;done&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Another factor that often comes into play is how artifacts in the image (e.g. aliasing, ringing, noise) combine with JPEG compression artifacts to exacerbate quality problems. So one way to get smaller file sizes is to reduce the other types of artifacts in the image, thereby allowing higher JPEG compression.&lt;/p&gt;
&lt;p&gt;The most common source of artifacts is image resizing. If you are resizing the images, I strongly recommend using a program that has a high quality filter. Irfanview and ImageMagick are two good choices.&lt;/p&gt;
&lt;p&gt;The ideal situation is this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Uncompressed source image&lt;/li&gt;
&lt;li&gt;Full-resolution if you will be handling the resize&lt;/li&gt;
&lt;li&gt;Absent artifacts such as aliasing&lt;/li&gt;
&lt;li&gt;Resize performed with good software like ImageMagick&lt;/li&gt;
&lt;li&gt;JPEG compression chosen based on subjective quality assessment.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Choosing the trade-off between quality and file size is difficult in part because it varies by image content. Images with lots of small color details (e.g. bright fabric threads; AKA high spatial frequency chroma) stand less compression than images that only have medium sized details that do not have important  and minute color information.&lt;/p&gt;
&lt;p&gt;One of the settings that is important for small web images is removal of the color space profile (e.g. sRGB). The only time it is needed is when there is a good reason for using non-sRGB JPEG, such as when you are certain that your users will have color managed browsers. Removing it can shave off 5KB or so; software will assume images without profiles have an sRGB profile. It can be removed with the -strip parameter of ImageMagick.&lt;/p&gt;
&lt;p&gt;As for choosing the specific compression settings, keep in mind that there are over 30 different types of options/techniques that can be used in compressing the image. Most image programs simplify that to a sliding scale from 0 to 100, 1 to 12, or something else. Keep in mind that even when programs use the same scale (e.g. 0 to 100), they probably have different ideas of what the numbers mean. 95 in one program may be very different than 95 in another.&lt;/p&gt;
&lt;p&gt;If bandwidth is not an issue, then I use a setting of 95 on ImageMagick, because in normal images I can’t tell the difference between 95 and 100. But when file size in an important concern, I consider 85 to be the optimal setting. In this image, the difference should be clear, but I generally find that cutting filesize in half is worth it. Below 85, the artifacts are too onerous for my taste.&lt;/p&gt;
&lt;p&gt;You don’t often hear about web site visitors’ dissatisfaction with compression artifacts, so you might be tempted to just reduce file sizes even beyond the point where it gets noticable. But I think there is a subliminal effect from the reduced image quality. Visitors may not stop visiting the site immediately, but my gut feeling is it leaves them with a certain impression in their mind or taste in their mouth. I would guess that user testing might result in comments such as “the X web site is not the same high-grade quality as the Y web site”, even if they don’t put it into words as specific as “the compression artifacts make X look uglier than Y”. Even if that pet theory is true, it still has to be balanced against the benefit of faster page loading times.&lt;/p&gt;
&lt;p&gt;Ideally, the tradeoff between quality and page loading time would be a choice left to the user. Those who prefer fewer artifacts could set their browser to download larger, less-compressed image files than the default, while users with low bandwidth could set it for more compressed images to get a faster page load at the expense of quality. I could imagine an Apache module and corresponding Firefox add-on some day.&lt;/p&gt;
&lt;p&gt;Regarding the situation where you want to reduce the file size of existing JPEGs, my advice is to first try (hard) to get the original source files. You can do better (for any given quality/size tradeoff) from those than you can by just manipulating the existing files. If that’s not possible, then the suboptimal workflows like jpegtran, jpegoptim, and doing a full decompress/recompress are the only alternative.&lt;/p&gt;
&lt;p&gt;As far as comparing different encoders, I haven’t really looked into that except to compare ImageMagick and Photoshop, where I (subjectively) determined they both had about similar quality for file size (and vice-versa).&lt;/p&gt;
&lt;p&gt;Here are all the comparison images. The file size and ImageMagick quality setting are in the rollover. I suggest opening images in browser tabs for easy A/B comparison.&lt;/p&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;a href=&#34;/blog/2009/12/jpeg-compression-quality-or-quantity/image-7.png&#34;&gt;&lt;img src=&#34;/blog/2009/12/jpeg-compression-quality-or-quantity/image-7.png&#34; title=&#34;size: 87K setting: lossless&#34;/&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href=&#34;/blog/2009/12/jpeg-compression-quality-or-quantity/image-8.jpeg&#34;&gt;&lt;img src=&#34;/blog/2009/12/jpeg-compression-quality-or-quantity/image-8.jpeg&#34; title=&#34;size: 74K setting: 100&#34;/&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href=&#34;/blog/2009/12/jpeg-compression-quality-or-quantity/image-0.jpeg&#34;&gt;&lt;img src=&#34;/blog/2009/12/jpeg-compression-quality-or-quantity/image-0.jpeg&#34; title=&#34;size: 27K setting: 95&#34;/&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href=&#34;/blog/2009/12/jpeg-compression-quality-or-quantity/image-10.jpeg&#34;&gt;&lt;img src=&#34;/blog/2009/12/jpeg-compression-quality-or-quantity/image-10.jpeg&#34; title=&#34;size: 15K setting: 85&#34;/&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;tr&gt;
&lt;td&gt;&lt;a href=&#34;/blog/2009/12/jpeg-compression-quality-or-quantity/image-1.jpeg&#34;&gt;&lt;img src=&#34;/blog/2009/12/jpeg-compression-quality-or-quantity/image-1.jpeg&#34; title=&#34;size: 8.0K setting: 50&#34;/&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href=&#34;/blog/2009/12/jpeg-compression-quality-or-quantity/image-12.jpeg&#34;&gt;&lt;img src=&#34;/blog/2009/12/jpeg-compression-quality-or-quantity/image-12.jpeg&#34; title=&#34;size: 4.8K setting: 20&#34;/&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href=&#34;/blog/2009/12/jpeg-compression-quality-or-quantity/image-2.jpeg&#34;&gt;&lt;img src=&#34;/blog/2009/12/jpeg-compression-quality-or-quantity/image-2.jpeg&#34; title=&#34;size: 2.9K setting: 8&#34;/&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href=&#34;/blog/2009/12/jpeg-compression-quality-or-quantity/image-14.jpeg&#34;&gt;&lt;img src=&#34;/blog/2009/12/jpeg-compression-quality-or-quantity/image-14.jpeg&#34; title=&#34;size: 1.7K setting: 1&#34;/&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;a href=&#34;/blog/2009/12/jpeg-compression-quality-or-quantity/image-15.png&#34;&gt;&lt;img src=&#34;/blog/2009/12/jpeg-compression-quality-or-quantity/image-15.png&#34; title=&#34;size: 87K setting: lossless&#34;/&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href=&#34;/blog/2009/12/jpeg-compression-quality-or-quantity/image-16.jpeg&#34;&gt;&lt;img src=&#34;/blog/2009/12/jpeg-compression-quality-or-quantity/image-16.jpeg&#34; title=&#34;size: 80K setting: 100&#34;/&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;tr&gt;
&lt;td&gt;&lt;a href=&#34;/blog/2009/12/jpeg-compression-quality-or-quantity/image-3.jpeg&#34;&gt;&lt;img src=&#34;/blog/2009/12/jpeg-compression-quality-or-quantity/image-3.jpeg&#34; title=&#34;size: 30K setting: 95&#34;/&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href=&#34;/blog/2009/12/jpeg-compression-quality-or-quantity/image-4.jpeg&#34;&gt;&lt;img src=&#34;/blog/2009/12/jpeg-compression-quality-or-quantity/image-4.jpeg&#34; title=&#34;size: 17K setting: 85&#34;/&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;tr&gt;
&lt;td&gt;&lt;a href=&#34;/blog/2009/12/jpeg-compression-quality-or-quantity/image-5.jpeg&#34;&gt;&lt;img src=&#34;/blog/2009/12/jpeg-compression-quality-or-quantity/image-5.jpeg&#34; title=&#34;size: 7.7K setting: 50&#34;/&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href=&#34;/blog/2009/12/jpeg-compression-quality-or-quantity/image-6.jpeg&#34;&gt;&lt;img src=&#34;/blog/2009/12/jpeg-compression-quality-or-quantity/image-6.jpeg&#34; title=&#34;size: 4.1K setting: 20&#34;/&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;tr&gt;
&lt;td&gt;&lt;a href=&#34;/blog/2009/12/jpeg-compression-quality-or-quantity/image-21.jpeg&#34;&gt;&lt;img src=&#34;/blog/2009/12/jpeg-compression-quality-or-quantity/image-21.jpeg&#34; title=&#34;size: 2.2K setting: 8&#34;/&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href=&#34;/blog/2009/12/jpeg-compression-quality-or-quantity/image-22.jpeg&#34;&gt;&lt;img src=&#34;/blog/2009/12/jpeg-compression-quality-or-quantity/image-22.jpeg&#34; title=&#34;size: 1.3K setting: 1&#34;/&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;

      </content>
    </entry>
  
</feed>
