{"componentChunkName":"component---src-templates-blog-post-js","path":"/reflecting-on-reflection/","result":{"data":{"site":{"siteMetadata":{"title":"Tea & Tech (🍵)"}},"markdownRemark":{"id":"c9f6d9b9-7c59-59fc-ac40-c7fcfee2b3bf","excerpt":"The other day I was working on Day 4 of Advent of Code.\nI was pretty delighted with myself for solving this one in a handful of minutes when days 2 and 3 took…","html":"<p>The other day I was working on <a href=\"https://adventofcode.com/2019/day/4\">Day 4 of Advent of Code</a>.\nI was pretty delighted with myself for solving this one in a handful of minutes when days 2 and 3 took significantly longer.\nOnce I had my code returning the correct value for all the sample inputs, I went ahead and ran it on the main input.</p>\n<blockquote>\n<p>“Elapsed time: 9002.5386 msecs”</p>\n</blockquote>\n<p>Nine seconds.  My computer crunched numbers for 9 whole seconds before it spit out the answer.</p>\n<p>I didn’t like that. I’ll be the first to admit that I’m no Clojure expert, but my solution wasn’t <em>inelegant.</em></p>\n<div class=\"gatsby-highlight\" data-language=\"clojure\"><pre class=\"language-clojure\"><code class=\"language-clojure\"><span class=\"token punctuation\">(</span><span class=\"token keyword\">defn</span> get-digits\n  <span class=\"token string\">\"Transforms a string or a number into a list of individual digits\"</span>\n  <span class=\"token punctuation\">[</span>number<span class=\"token punctuation\">]</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">map</span> #<span class=\"token punctuation\">(</span>Character/digit % <span class=\"token number\">10</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">str</span> number<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token punctuation\">(</span><span class=\"token keyword\">defn</span> valid-number? <span class=\"token punctuation\">[</span>number<span class=\"token punctuation\">]</span>\n  <span class=\"token punctuation\">(</span><span class=\"token keyword\">let</span> <span class=\"token punctuation\">[</span>digits <span class=\"token punctuation\">(</span>get-digits number<span class=\"token punctuation\">)</span><span class=\"token punctuation\">]</span>\n    <span class=\"token punctuation\">(</span><span class=\"token keyword\">and</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">not</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">apply</span> distinct? digits<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n         <span class=\"token punctuation\">(</span><span class=\"token keyword\">apply</span> <span class=\"token keyword\">&lt;=</span> digits<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token punctuation\">(</span><span class=\"token keyword\">defn</span> part1 <span class=\"token punctuation\">[</span><span class=\"token punctuation\">[</span>start end<span class=\"token punctuation\">]</span><span class=\"token punctuation\">]</span>\n  <span class=\"token punctuation\">(</span><span class=\"token keyword\">count</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">filter</span> valid-number? <span class=\"token punctuation\">(</span><span class=\"token keyword\">range</span> start <span class=\"token punctuation\">(</span><span class=\"token keyword\">inc</span> end<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token punctuation\">(</span><span class=\"token keyword\">defn</span> -main <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span>\n  <span class=\"token punctuation\">(</span><span class=\"token keyword\">time</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">println</span> <span class=\"token string\">\"Day 04, part 1:\"</span> <span class=\"token punctuation\">(</span>part1 <span class=\"token punctuation\">[</span><span class=\"token number\">402328</span> <span class=\"token number\">864247</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span></code></pre></div>\n<p>Nice and short! And yet, when run, my computer ground its gears for nine whole seconds (on an Intel i7-8700K, no less).</p>\n<p>Well, at least I had access to part 2 of the problem now, and it looks like part 2 needed access to the same range of integers.\nMaybe generating that giant list of integers was adding a lot of time?\nI decided to exclude it from the timing calculations:</p>\n<div class=\"gatsby-highlight\" data-language=\"clojure\"><pre class=\"language-clojure\"><code class=\"language-clojure\"><span class=\"token punctuation\">(</span><span class=\"token keyword\">defn</span> part1 <span class=\"token punctuation\">[</span>numbers<span class=\"token punctuation\">]</span>\n  <span class=\"token punctuation\">(</span><span class=\"token keyword\">count</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">filter</span> valid-number? numbers<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token punctuation\">(</span><span class=\"token keyword\">defn</span> -main <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span>\n  <span class=\"token punctuation\">(</span><span class=\"token keyword\">let</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">[</span>start end<span class=\"token punctuation\">]</span> <span class=\"token punctuation\">[</span><span class=\"token number\">402328</span> <span class=\"token number\">864247</span><span class=\"token punctuation\">]</span>\n        numbers <span class=\"token punctuation\">(</span><span class=\"token keyword\">range</span> start <span class=\"token punctuation\">(</span><span class=\"token keyword\">inc</span> end<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">]</span>\n    <span class=\"token punctuation\">(</span><span class=\"token keyword\">time</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">println</span> <span class=\"token string\">\"Day 04, part 1:\"</span> <span class=\"token punctuation\">(</span>part1 numbers<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span></code></pre></div>\n<blockquote>\n<p>“Elapsed time: 9057.4244 msecs”</p>\n</blockquote>\n<p>Wait, it’s <em>slower?</em> But… oh yeah, it’s not actually generating all those numbers at once.\nFrom the <a href=\"https://clojuredocs.org/clojure.core/range\">range page</a> on clojuredocs.org:</p>\n<blockquote>\n<p>Returns a <em>lazy</em> seq of nums from start (inclusive) to end</p>\n</blockquote>\n<p>Because <code class=\"language-text\">range</code> is lazy, it’s not actually building a list of numbers before we start timing the solution to our problem.</p>\n<p>At this point, I was curious to see how much faster it would be to run our code <a href=\"https://ajpierce.com/clojure-on-metal/\">on metal</a> rather than on the JVM, so I compiled the code using the GraalVM <code class=\"language-text\">native-image</code> tool, and ran it again:</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">$ ./target/day04\nException in thread &quot;main&quot; java.lang.ClassNotFoundException: java.lang.Character\n        at com.oracle.svm.core.hub.ClassForNameSupport.forName(ClassForNameSupport.java:60)\n        at java.lang.Class.forName(DynamicHub.java:1197)\n        at clojure.lang.RT.classForName(RT.java:2211)\n        at clojure.lang.RT.classForName(RT.java:2220)\n        at advent_2019.day99$get_digits$fn__475.invoke(day99.clj:6)\n        at clojure.core$map$fn__5866.invoke(core.clj:2755)\n        at clojure.lang.LazySeq.sval(LazySeq.java:42)\n        at clojure.lang.LazySeq.seq(LazySeq.java:51)\n        at clojure.lang.RT.seq(RT.java:535)\n        ...</code></pre></div>\n<p>A stack trace?\nAt this point, I knew something in my code was to blame, and I took to the internet to see if I could find a clue.</p>\n<p>Many of the search results turning up with Clojure and <code class=\"language-text\">native-image</code> troubleshooting tips mentioned adding a check for <a href=\"https://clojuredocs.org/clojure.core/*warn-on-reflection*\">warn-on-reflection</a>, so I thought it would be a good idea to turn on and try to compile my code again.</p>\n<blockquote>\n<p>Reflection warning, advent_2019/day04.clj:6:18 - call to static method digit on java.lang.Character can’t be resolved (argument types: unknown, long).</p>\n</blockquote>\n<p>Whoa! Now we’re on to something! Apparently my code was relying on <a href=\"https://www.geeksforgeeks.org/reflection-in-java/\">reflection</a> to determine function argument types.</p>\n<p>But wait a minute — Clojure is dynamically typed. How else are you going to determine types? Is there a way to not rely on reflection in Clojure?</p>\n<p>As a matter of fact, there is! It turns out that <a href=\"https://clojure.org/reference/java_interop#typehints\">Clojure supports type hinting</a>, which was news to me! From the official Clojure reference:</p>\n<blockquote>\n<p>Clojure supports the use of type hints to assist the compiler in avoiding reflection in performance-critical areas of code.</p>\n</blockquote>\n<p>Immediately followed by a very helpful example:</p>\n<div class=\"gatsby-highlight\" data-language=\"clojure\"><pre class=\"language-clojure\"><code class=\"language-clojure\"><span class=\"token punctuation\">(</span><span class=\"token keyword\">defn</span> len <span class=\"token punctuation\">[</span>x<span class=\"token punctuation\">]</span>\n  <span class=\"token punctuation\">(</span>.length x<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token punctuation\">(</span><span class=\"token keyword\">defn</span> len2 <span class=\"token punctuation\">[</span>^String x<span class=\"token punctuation\">]</span>\n  <span class=\"token punctuation\">(</span>.length x<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n\nuser=<span class=\"token keyword\">></span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">time</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">reduce</span> <span class=\"token keyword\">+</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">map</span> len <span class=\"token punctuation\">(</span><span class=\"token keyword\">repeat</span> <span class=\"token number\">1000000</span> <span class=\"token string\">\"asdf\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n<span class=\"token string\">\"Elapsed time: 3007.198 msecs\"</span>\n<span class=\"token number\">4000000</span>\nuser=<span class=\"token keyword\">></span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">time</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">reduce</span> <span class=\"token keyword\">+</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">map</span> len2 <span class=\"token punctuation\">(</span><span class=\"token keyword\">repeat</span> <span class=\"token number\">1000000</span> <span class=\"token string\">\"asdf\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n<span class=\"token string\">\"Elapsed time: 308.045 msecs\"</span>\n<span class=\"token number\">4000000</span></code></pre></div>\n<p>Wait a minute… an order of magnitude faster?? And all I need to do is add some type hints?</p>\n<p>Let’s refactor our code a little bit:</p>\n<h3>Before</h3>\n<div class=\"gatsby-highlight\" data-language=\"clojure\"><pre class=\"language-clojure\"><code class=\"language-clojure\"><span class=\"token punctuation\">(</span><span class=\"token keyword\">defn</span> get-digits\n  <span class=\"token string\">\"Transforms a string or a number into a list of individual digits\"</span>\n  <span class=\"token punctuation\">[</span>number<span class=\"token punctuation\">]</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">map</span> #<span class=\"token punctuation\">(</span>Character/digit % <span class=\"token number\">10</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">str</span> number<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span></code></pre></div>\n<h3>After</h3>\n<div class=\"gatsby-highlight\" data-language=\"clojure\"><pre class=\"language-clojure\"><code class=\"language-clojure\"><span class=\"token punctuation\">(</span><span class=\"token keyword\">defn</span> get-digit <span class=\"token punctuation\">[</span>^Character x<span class=\"token punctuation\">]</span> <span class=\"token punctuation\">(</span>Character/digit x <span class=\"token number\">10</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token punctuation\">(</span><span class=\"token keyword\">defn</span> get-digits\n  <span class=\"token string\">\"Transforms a string or a number into a list of individual digits\"</span>\n  <span class=\"token punctuation\">[</span>^Integer number<span class=\"token punctuation\">]</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">map</span> get-digit <span class=\"token punctuation\">(</span><span class=\"token keyword\">str</span> number<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span></code></pre></div>\n<p>Having made the change, let’s run our code again:</p>\n<blockquote>\n<p>“Elapsed time: 517.1315 msecs”</p>\n</blockquote>\n<p>Holy smokes!  From 9.05s down to 0.52s!  That’s over <b>17x faster!</b></p>\n<p>Turns out that, in this case, the reflection was really expensive. The <code class=\"language-text\">get-digits</code> function is critical to our solution, and is called thousands of times. By “avoiding reflection in performance-critical areas of our code,” we saw a pretty significant improvement in run time!</p>\n<p>Since making this discovery, I have added the following to my <code class=\"language-text\">project.clj</code> file:</p>\n<div class=\"gatsby-highlight\" data-language=\"clojure\"><pre class=\"language-clojure\"><code class=\"language-clojure\"><span class=\"token operator\">:global-vars</span> <span class=\"token punctuation\">{</span>*warn-on-reflection* <span class=\"token boolean\">true</span><span class=\"token punctuation\">}</span></code></pre></div>\n<p>Now, the compiler (and my <a href=\"https://github.com/liquidz/vim-iced\">REPL of choice</a> as well!) will bark at me every time I make ambiguous typing choices.\nDoing so allowed me to find type ambiguities in my Day 02 and 03 solutions as well!</p>\n<p>In the end, with a little type hinting, it turns out my solution wasn’t so bad after all 😊</p>\n<p>…but we can still make it faster. Stay tuned for next time!</p>","frontmatter":{"title":"Reflecting on Reflection","date":"December 21, 2019","description":"Learn how to speed up Clojure 17x with this One Weird Trick!"}}},"pageContext":{"slug":"/reflecting-on-reflection/","previous":{"fields":{"slug":"/clojure-on-metal/"},"frontmatter":{"title":"Clojure on Metal","tags":["tech"]}},"next":{"fields":{"slug":"/reducing-runtime-with-reducers/"},"frontmatter":{"title":"Reducing Runtime with Reducers","tags":["tech"]}}}},"staticQueryHashes":["63159454","734670849"]}