<rss version="2.0">
  <channel>
    <title>Julia on LLBBL Blog</title>
    <link>https://llbbl.blog/categories/julia/</link>
    <description></description>
    
    <language>en</language>
    
    <lastBuildDate>Sun, 12 Jul 2026 10:00:00 -0500</lastBuildDate>
    
    <item>
      <title>Julia: High-Performance Computing Without the Two-Language Tax</title>
      <link>https://llbbl.blog/2026/07/12/julia-highperformance-computing-without-the.html</link>
      <pubDate>Sun, 12 Jul 2026 10:00:00 -0500</pubDate>
      
      <guid>http://llbbl.micro.blog/2026/07/12/julia-highperformance-computing-without-the.html</guid>
      <description>&lt;p&gt;I hadn&amp;rsquo;t heard of Julia until someone posted about it on Bluesky. The language was new to me, so I went reading, and this post is a summary of what stood out. Fair warning: I haven&amp;rsquo;t written any Julia myself yet.&lt;/p&gt;
&lt;p&gt;The thing Julia is built to solve is what people call the &lt;strong&gt;two-language problem&lt;/strong&gt;: you prototype in something pleasant like Python or R, hit a performance wall, then rewrite the slow parts in C or Fortran and glue them back together. Now you maintain two codebases and a wall between the people who know the domain and the people who know the fast code. Julia&amp;rsquo;s pitch, out of MIT in 2012, is to collapse that into one language: Python-like expressiveness with C-like speed, and no rewrite step.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;the-three-things-that-make-it-different&#34;&gt;The Three Things That Make It Different&lt;/h2&gt;
&lt;h3 id=&#34;1-multiple-dispatch&#34;&gt;1. Multiple dispatch&lt;/h3&gt;
&lt;p&gt;This part reorganizes how you think.&lt;/p&gt;
&lt;p&gt;In Python or C++, methods belong to classes. You write &lt;code&gt;object.method(arg)&lt;/code&gt;, and the language picks which method to run based on one thing: the type of &lt;code&gt;object&lt;/code&gt;. That&amp;rsquo;s single dispatch.&lt;/p&gt;
&lt;p&gt;Julia flips it. Functions are the top-level concept, and methods are implementations attached to them. When you call a function, Julia picks the exact method based on the concrete types of &lt;em&gt;all&lt;/em&gt; the arguments, not just the first one. Same function name, a different method chosen by the full type signature. This replaces the &lt;code&gt;if isinstance(...)&lt;/code&gt; ladders you&amp;rsquo;d write in Python and lets the type system route the call for you.&lt;/p&gt;
&lt;h3 id=&#34;2-jit-compilation-not-interpretation&#34;&gt;2. JIT compilation, not interpretation&lt;/h3&gt;
&lt;p&gt;Julia is dynamically typed, but it does not interpret your code. The first time you call a function with a specific combination of argument types, the JIT compiler (built on LLVM) infers the types across the whole call graph and compiles specialized native machine code. Every call after that, with those same types, runs at C speed.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s the trick behind the whole &amp;ldquo;no rewrite&amp;rdquo; promise. You didn&amp;rsquo;t add type annotations everywhere and you didn&amp;rsquo;t drop into another language. The compiler just specialized for you.&lt;/p&gt;
&lt;h3 id=&#34;3-math-that-looks-like-math&#34;&gt;3. Math that looks like math&lt;/h3&gt;
&lt;p&gt;Julia treats mathematical notation as first-class. Unicode variable names (&lt;code&gt;α&lt;/code&gt;, &lt;code&gt;β&lt;/code&gt;, &lt;code&gt;θ&lt;/code&gt;), literal coefficients like &lt;code&gt;2x + 3y&lt;/code&gt;, and native linear algebra operators. If you&amp;rsquo;re translating a paper into code, the code ends up looking like the paper:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-julia&#34; data-lang=&#34;julia&#34;&gt;α, β &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;4&lt;/span&gt;
α&lt;span style=&#34;color:#f92672&#34;&gt;^&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; β&lt;span style=&#34;color:#f92672&#34;&gt;^&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;      &lt;span style=&#34;color:#75715e&#34;&gt;# =&amp;gt; 25&lt;/span&gt;
&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;α &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;β        &lt;span style=&#34;color:#75715e&#34;&gt;# =&amp;gt; 18   (2α means 2 * α, no asterisk needed)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The strange thing about programming is how often the math matters. First-class support for mathematical notation sounds minor, but for the tasks Julia is built for, it turns out to be surprisingly useful.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;another-quick-example&#34;&gt;Another Quick Example&lt;/h2&gt;
&lt;p&gt;One Julia feature that stood out to me is broadcasting. In NumPy, chaining array operations like &lt;code&gt;sin(x) + cos(y)&lt;/code&gt; quietly allocates temporary arrays for each step. In Julia, one dot fuses the whole expression into a single loop with zero intermediate allocations:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-julia&#34; data-lang=&#34;julia&#34;&gt;x &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#ae81ff&#34;&gt;1.0&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;2.0&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;3.0&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;4.0&lt;/span&gt;]
y &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#ae81ff&#34;&gt;10.0&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;20.0&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;30.0&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;40.0&lt;/span&gt;]

result &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;@&lt;/span&gt;](https&lt;span style=&#34;color:#f92672&#34;&gt;://&lt;/span&gt;micro&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;blog&lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt; sin(x) &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; sqrt(y) &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;2.5&lt;/span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# fused, no temporaries&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;tooling-and-two-rules&#34;&gt;Tooling and Two Rules&lt;/h2&gt;
&lt;p&gt;Setup is one installer. &lt;code&gt;juliaup&lt;/code&gt; manages versions (&lt;code&gt;curl -fsSL https://install.julialang.org | sh&lt;/code&gt;, then &lt;code&gt;juliaup add release&lt;/code&gt;), and the built-in package manager lives one keystroke away: hit &lt;code&gt;]&lt;/code&gt; in the REPL to enter package mode, then &lt;code&gt;add DataFrames DifferentialEquations Makie CUDA&lt;/code&gt;. The ecosystem worth knowing: &lt;strong&gt;DifferentialEquations.jl&lt;/strong&gt; for solvers, &lt;strong&gt;JuMP.jl&lt;/strong&gt; for optimization modeling, &lt;strong&gt;Makie.jl&lt;/strong&gt; for GPU-accelerated plotting, and &lt;strong&gt;CUDA.jl&lt;/strong&gt; for writing GPU kernels without touching CUDA C++.&lt;/p&gt;
&lt;p&gt;Two rules keep your code at full speed. First, avoid untyped global variables. A non-const global forces a dynamic type lookup on every access, and it will silently gut your performance. Use &lt;code&gt;const&lt;/code&gt;, or pass values in as parameters. Second, keep your functions type-stable, meaning the return type depends on the &lt;em&gt;types&lt;/em&gt; of the arguments, not their runtime values. When something feels slow, run &lt;code&gt;@code_warntype my_function(1.0, 2.0)&lt;/code&gt; and look for the red. That&amp;rsquo;s the compiler telling you it couldn&amp;rsquo;t infer a type.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;so-when-should-you-actually-use-it&#34;&gt;So When Should You Actually Use It?&lt;/h2&gt;
&lt;p&gt;Reach for Julia when you&amp;rsquo;re building numerical simulations, differential equation solvers, actuarial or financial risk models, or custom ML architectures, basically anywhere NumPy or PyTorch hits a wall and you can feel the rewrite coming.&lt;/p&gt;
&lt;p&gt;Don&amp;rsquo;t reach for it to build a web scraper or a CRUD service. Go or Node boots faster and the JIT warmup isn&amp;rsquo;t worth it there.&lt;/p&gt;
&lt;p&gt;The pitch is simple: the language that lets you prototype is the same language that ships. No second codebase, no translation layer, no wall between your domain experts and your fast path. If you&amp;rsquo;ve ever paid the two-language tax, that&amp;rsquo;s the whole reason to give Julia a look.&lt;/p&gt;
&lt;h2 id=&#34;sources&#34;&gt;Sources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Bezanson, Edelman, Karpinski &amp;amp; Shah, &lt;a href=&#34;https://epubs.siam.org/doi/10.1137/141000671&#34;&gt;&lt;em&gt;Julia: A Fresh Approach to Numerical Computing&lt;/em&gt;&lt;/a&gt;, SIAM Review 59(1), 2017.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://docs.julialang.org/en/v1/&#34;&gt;Julia Documentation, Performance Tips&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;I&amp;rsquo;d appreciate a follow. You can subscribe with your email below. The emails go out once a week, or you can find me on Mastodon at &lt;a href=&#34;https://micro.blog/llbbl?remote_follow=1&#34;&gt;@logan@llbbl.blog&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
</description>
    </item>
    
  </channel>
</rss>