<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Alex Dremov</title><description>I&apos;m a Machine Learning Researcher and Engineer. Here I write posts on the intersection of deep learning theory and efficient machine learning</description><link>https://alexdremov.me/</link><language>en-us</language><item><title>Managing Thousands of DL Experiments and Staying Sane</title><link>https://alexdremov.me/managing-thousands-of-dl-experiments-and-staying-sane/</link><guid isPermaLink="true">https://alexdremov.me/managing-thousands-of-dl-experiments-and-staying-sane/</guid><description>At scale, launching ML research experiments is already complicated. Analyzing them without proper planning is just impossible.</description><pubDate>Tue, 07 Jul 2026 08:05:07 GMT</pubDate><content:encoded>&lt;p&gt;Not many AI researchers or engineers talk about what happens behind the scenes: how to launch (and re-launch) experiments, manage training logs, and assemble final results. This topic is highly opinionated, and everyone does things their own way. Still, if some of those tips may help you manage your research better, or make you think &quot;aha, this is neat,” my job is done.&lt;/p&gt;&lt;h2 id=&quot;codebase-choice&quot;&gt;Codebase Choice&lt;/h2&gt;&lt;p&gt;I have seen many people justify their codebase choice by saying, “Well, it is the fastest on benchmarks.” This may be an important point to consider if your goal is to just train a model fast with an already pre-existing recipe but just substituting data. This is one of the few cases when &quot;the fastest codebase&quot; makes sense.&lt;/p&gt;&lt;p&gt;If you are tweaking model architecture, doing complex data processing pipelines, curriculum, optimizers research, training dynamics analysis, and a hundred more research-related things, the fastest codebase is not your objective. &lt;strong&gt;It is actively harmful to select a research codebase based only on efficiency&lt;/strong&gt;. Think about how much time you spend writing, debugging, and changing code compared to launching a training job. Moreover, most of the research is done on relatively small model scales; code efficiency becomes secondary. Still, training speed is important, but there must be a balance. &lt;/p&gt;&lt;p&gt;Please, do not go with &lt;a href=&quot;https://github.com/nvidia/megatron-lm?ref=alexdremov.me&quot;&gt;Megatron-LM&lt;/a&gt; for your project by default. While appealing from a speed perspective, it is terrible to tweak and play with. Consider more lightweight projects that are fast enough and research-friendly:&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;It doesn’t matter which framework you pick. Just pay attention to its research-readiness. Consider Optimus-DL for this reason. 🙂&lt;/div&gt;&lt;/div&gt;&lt;ol&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/facebookresearch/lingua?ref=alexdremov.me&quot;&gt;Meta Lingua&lt;/a&gt; — small (~20 files) project for fast iteration. It is definitely on the more research-y and less training-speed spectrum. Still, it can be a good choice for small experiments. It is definitely fairseq-inspired (as it is also no longer maintained)&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/pytorch/torchtitan?ref=alexdremov.me&quot;&gt;TorchTitan&lt;/a&gt; — PyTorch-backed framework with cutting-edge PyTorch optimizations and good architecture. Definitely read its guides, sample experiments, and see if it feels right.&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/karpathy/nanochat?ref=alexdremov.me&quot;&gt;NanoChat&lt;/a&gt; — The simplest framework on the list, streamlined for GPT-2 training (which is a bit outdated for the newest research). Still, this codebase is one of the easiest to tweak but one of the worst from an architecture perspective.  &lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/alexdremov/optimus-dl?ref=alexdremov.me&quot;&gt;Optimus-DL&lt;/a&gt; — I mean, I have to advertise my project, right? 😄 This framework was created to be a good balance between training speed and research convenience. Here I list just a few features, so make sure to check out the docs and the project itself!&lt;ol&gt;&lt;li&gt;&lt;strong&gt;Clean architecture, &lt;/strong&gt;where you can replace any element for research purposes with a registry system. &lt;/li&gt;&lt;li&gt;&lt;strong&gt;Purely config-driven: &lt;/strong&gt;model, trainer, criterion, optimizer groups, loggers— you name it—it will be config-defined. This is extremely important, as you want to be able to swap components easily and do easy ablations. &lt;/li&gt;&lt;li&gt;&lt;strong&gt;Extensive train / eval metrics system&lt;/strong&gt;. We do not just train models; we analyze training data. A unified logging system allows you to calculate anything anytime with automatic distributed aggregations.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Rich data preprocessing system. &lt;/strong&gt;I really hate when a framework requires you to provide tokenized uint16 files as data and then incurs a mental breakdown if you want to modify the data mixture mid-training (hi, Megatron). Optimus&apos; data system allows you to define complex composable data processing pipelines with multi-dataset sampling. &lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fast.&lt;/strong&gt; It implements various optimizations (flat batching, torch compile, custom kernels, TP, SP, HSDP), showing a competitive performance despite being deeply configurable.&lt;/li&gt;&lt;/ol&gt;&lt;/li&gt;&lt;/ol&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://github.com/alexdremov/optimus-dl?ref=alexdremov.me&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;GitHub - alexdremov/optimus-dl: Modular, high-performance deep learning research framework&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;Modular, high-performance deep learning research framework - alexdremov/optimus-dl&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://alexdremov.me/content/images/icon/favicon-b2fa1dc1-55e8-475f-b901-5e8e44d780d8.svg&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;GitHub&lt;/span&gt;&lt;span class=&quot;kg-bookmark-publisher&quot;&gt;alexdremov&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://opengraph.githubassets.com/eac806455de256620aae483a729f9f2bfd3d91edf3bbd524b6e8fbe226420a0a/alexdremov/optimus-dl&quot; alt=&quot;&quot; onerror=&quot;this.style.display = &apos;none&apos;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;h2 id=&quot;launching-experiments&quot;&gt;Launching Experiments&lt;/h2&gt;&lt;p&gt;Nobody talks about this.&lt;/p&gt;&lt;p&gt;Yet this is one of the most important parts in the whole pipeline—no experiments, nothing to analyze, no output (perfect in a way). Alternatively, lost experiments are overlooked results or wasted compute.&lt;/p&gt;&lt;p&gt;It is hard to give platform-independent tips, but while working with several different systems, some principles are transferable. &lt;/p&gt;&lt;h3 id=&quot;leverage-tags&quot;&gt;Leverage tags&lt;/h3&gt;&lt;p&gt;How to group related experiments? Luckily, humanity developed a way some hundreds of years ago. Use tags. Most platforms support tags. If not, prepend your experiment’s name with one.&lt;/p&gt;&lt;p&gt;One important rule I keep is having a so-called main tag that defines a minimal group of experiments, where all experiments within the group are &lt;strong&gt;comparable&lt;/strong&gt; and vary only by some parameters. This saves a lot of time and reduces the possibility of human error by making sure you fetch all the experiments within some sweep suite.&lt;/p&gt;&lt;p&gt;In my logs, I have tags like &lt;code&gt;width-depth-1&lt;/code&gt;, &lt;code&gt;width-depth-2&lt;/code&gt; and so on. Those define versioning with all versions up to the maximum one considered failed. This way, you can keep track of your experiments’ improvements without mixing up failed attempts (bugs) with final experiments. If only part of the experiments failed, nothing stops you from giving an experiment several main tags.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Use tags and adopt a consistent strategy. This will help you retrieve and compare experiments easily. It is a good thing to also leave a short note for every new tag you make so that you do not forget what you were doing in a week’s time&lt;/div&gt;&lt;/div&gt;&lt;h3 id=&quot;streamline-exps-naming-strategy&quot;&gt;Streamline exps naming strategy&lt;/h3&gt;&lt;p&gt;A dead-simple approach to experiment names is uniqueness. One name = one experiment. Why wouldn’t it be? This makes matching between runs, configs, and names unambiguous and minimizes the risk of overwriting important experiments. It’s also good if all names are human-readable. You don’t need to dump the full config into the name, but the most important parameters are worth mentioning.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Two things a good name conforms to: uniqueness and readability&lt;/div&gt;&lt;/div&gt;
&lt;!--kg-card-begin: html--&gt;
&lt;section class=&quot;custom-replace&quot; data-replace=&quot;.subscription-article&quot;&gt;
    &lt;a class=&quot;gh-portal-triggerbtn-container&quot;&gt;Subscribe and don&apos;t miss posts!&lt;/a&gt;
&lt;/section&gt;
&lt;!--kg-card-end: html--&gt;
&lt;h3 id=&quot;your-experiments-will-fail&quot;&gt;Your Experiments Will Fail&lt;/h3&gt;&lt;p&gt;If they will not fail, you may cancel something by accident, or the whole cluster will go down. Something will happen, and your launch scripts must be able to handle that. All my launch scripts include something like this:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;status = get_run_status(run_name)
if status in {RUNNING, PENDING, FINISHED}:
  logger.info(f&quot;Experiment {run_name} is {status}, skipping re-creation&quot;)
  return
elif status == FAILED:
  logger.info(f&quot;Experiment {run_name} has failed! Check out the logs&quot;)
elif status is None:
  logger.info(f&quot;Launching experiment {run_name}&quot;)
else:
  raise RuntimeError(f&quot;Unknown {status = }&quot;)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It is pretty self-explanatory, simple, and surprisingly efficient. You place this code in the loop, and now you are able to launch parameter sweeps. It saves a lot of time not to hand-pick failed IDs, look up tested params, and hand-launch failed experiments. More importantly, it is not just saving your time but is also saving you from human error when re-launching something. &lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Your launch scripts must be able to skip already running exps, re-launch failed ones, and spawn new experiments.&lt;/div&gt;&lt;/div&gt;&lt;h3 id=&quot;ditch-ui&quot;&gt;Ditch UI&lt;/h3&gt;&lt;p&gt;I mean, it’s in the title. Imagine launching thousands of experiments through UI. It’s just inefficient if not impossible. Leverage CLI for whatever platform you use and ensure that you are able to launch experiments with just scripts. You can use bash, &lt;strong&gt;but I would encourage using python straight away&lt;/strong&gt;, as bash starts to feel clunky as soon as you start doing complicated experiments, planning, calculations, etc. &lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Using UI to launch experiments is just not scalable. I am not even talking about copy-pasting parameters and configs&lt;/div&gt;&lt;/div&gt;&lt;h2 id=&quot;reproducibility&quot;&gt;Reproducibility&lt;/h2&gt;&lt;p&gt;Launch your code twice. Do you get the same results?&lt;/p&gt;&lt;p&gt;Reproducibility of your runs is a good principle for a reason. Mainly, I view it as a sanity check. If your experiments depend on the moon phase, something is off. It can be model init, data sampling, reproducible torch flags—anything. &lt;/p&gt;&lt;p&gt;Additionally, most systems have some kind of &lt;strong&gt;preemption or eviction&lt;/strong&gt;— aka a situation when you use too many resources and your jobs are killed to give room to other people. If your experiments are not reproducible, you will get different results depending on when preemption happened (the closest to the moon phase impact thing), which is bad. &lt;/p&gt;&lt;p&gt;Another good thing to check is whether training a model for 100 steps has the same result as training for 50 steps, stopping, restoring the checkpoint, and resuming until 100 steps (Optimus-DL has such tests!). If results vary, you are missing some information in your checkpoint.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Reproducibility is not just a nice thing to have. It is also a sanity check.&lt;/div&gt;&lt;/div&gt;&lt;h3 id=&quot;docker&quot;&gt;Docker &lt;/h3&gt;&lt;p&gt;An important thing about reproducibility is your environment. Luckily, most job-scheduling systems use some type of containers with fixed environments, so this is true for most setups. &lt;/p&gt;&lt;p&gt;If you do not want or cannot use Docker, at least use &lt;code&gt;pyproject.toml&lt;/code&gt; with a lock file or &lt;code&gt;requirements.txt&lt;/code&gt; dependencies. You do not want your results to differ on a random day just because PyTorch introduced a new bug.&lt;/p&gt;&lt;h3 id=&quot;git-all-the-way&quot;&gt;Git All the Way&lt;/h3&gt;&lt;p&gt;We all use &lt;code&gt;git&lt;/code&gt; for code. I do not know why more people do not extend it to experiments. Store your experiments’ launch scripts in a git repository too. You can develop some structure that works for you: branch per experiment, tag per experiment, directory per experiment, etc. The exact way you do it may vary, but organize your experiments’ management with git too.&lt;/p&gt;&lt;p&gt;Neat trick: if you depend on some external repositories, do not just do &lt;code&gt;git clone &amp;lt;repo&amp;gt;&lt;/code&gt; in your launch script. First of all, this clones the most recent version, making your results reproducible. Second, it may pull different code versions in between job preemptions, which just breaks all hell loose. So, the trick is to use git submodules for all your variable dependencies directly in your experiments management git repository. &lt;/p&gt;&lt;p&gt;You can add a submodule via:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git submodule add &amp;lt;dependency&amp;gt; ./&amp;lt;dependency path&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And in your job script, you clone the experiments management repo and check out to the exact commit that was used when launching the job: &lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language- bash&quot;&gt;git clone &amp;lt;repo&amp;gt; launch &amp;amp;&amp;amp; cd launch
git checkout &amp;lt;commit when launched&amp;gt;
git submodule init
git submodule update

pip install &amp;lt;dependency path&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This way, all the repos you depend on are fixed, and each experiment always runs with the same dependency. If you bump up the version of the dependency, you will need to commit a submodule change so that it is used in the following launches. &lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Git can be used not just to store your main codebase. Use it to store your experiments’ management scripts too! If you depend on external repositories, use gut sub modules to synchronize your launch scripts with external dependencies.&lt;/div&gt;&lt;/div&gt;&lt;h2 id=&quot;tracking&quot;&gt;Tracking&lt;/h2&gt;&lt;p&gt;If a tree falls in a forest and no one is around to hear it, does it make a sound? Well, the same applies to experiments. If they did not log the data, was it even worth it?&lt;/p&gt;&lt;p&gt;One thing that I’m constantly finding myself in is that it doesn’t matter what metrics tracking platform you use. All of them have very limited visualization capabilities to do anything serious. So, what actually matters is how resilient the logging is so that you do not lose any data. &lt;/p&gt;&lt;p&gt;And the truth is that everything fails; there’s not a single tracking service out there with 100.0% uptime. So, what I do is use several tracking services: mlflow, plain JSONL, wandb. All of those have their pros and cons. Wandb changes their API silently so that my old download scripts do not work anymore and produce wrong results. Mlflow does not easily support bulk export. JSONL may be lost if the service you store it in fails. So, the best option is to have them all in your framework!&lt;/p&gt;&lt;p&gt;Next, I just have scripts that download all data from all runs and use Python to do comparisons and plots.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Let’s face it: all tracking services just cannot do complex comparison plots. So focus on making sure your data is preserved, and use Python to do advanced analysis.&lt;/div&gt;&lt;/div&gt;&lt;h2 id=&quot;presenting-and-analyzing-data&quot;&gt;Presenting and Analyzing Data&lt;/h2&gt;&lt;p&gt;I will not say much about it, but it is just so painful when someone has interesting experiments implemented but then the data is so badly presented that you cannot understand anything. Even if it is an internal preliminary result that you decide to share, it takes exactly two more minutes to label axes, make a legend, and give it a title. &lt;/p&gt;&lt;p&gt;Seems minor, but the presentation of the data is what matters in the end, right? &lt;/p&gt;&lt;h2 id=&quot;all-in-all&quot;&gt;All in All&lt;/h2&gt;&lt;p&gt;Those tips were gradually developed from doing a large-scale ASR at Yandex, then throughout my master’s degree, and research papers. Perhaps there’s more to it, and not a single setup is perfect. Still, I wanted to share this to save time figuring out those basic truths for other researchers 😁.&lt;/p&gt;</content:encoded></item><item><title>Rethinking Quantization-Aware Training: Why Your QAT Length is Probably Wrong</title><link>https://alexdremov.me/rethinking-quantization-aware-training-why-your-qat-length-is-probably-wrong/</link><guid isPermaLink="true">https://alexdremov.me/rethinking-quantization-aware-training-why-your-qat-length-is-probably-wrong/</guid><description>The optimal QAT fraction increases with scale. We research how long to train QAT to get the best results, and what bit size is the best
</description><pubDate>Thu, 30 Oct 2025 20:37:42 GMT</pubDate><content:encoded>&lt;p&gt;Training quantized neural networks typically involves two phases: full-precision (FP) pretraining followed by quantization-aware training (QAT). The conventional approach allocates about 10% of the training budget to QAT. But recent research at Apple shows this ratio is far from optimal, especially at scale.&lt;/p&gt;&lt;p&gt;In extreme cases, using the wrong QAT fraction can waste up to 50% of your compute budget. Moreover, what QAT bit-width should you pick given a fixed memory budget? Here&apos;s what we found after running ~800 experiments across different model sizes and training lengths.&lt;/p&gt;&lt;h2 id=&quot;the-resource-allocation-problem&quot;&gt;The Resource Allocation Problem&lt;/h2&gt;&lt;p&gt;When training QAT models, you face a fundamental trade-off: given a fixed compute budget, how should you divide training time between full-precision pre-training and quantization-aware training?&lt;/p&gt;&lt;p&gt;More FP training gives you a better starting checkpoint. More QAT training gives the model more time to adapt to quantization. Previous work &lt;a href=&quot;https://arxiv.org/abs/2502.02631?ref=alexdremov.me&quot; rel=&quot;noreferrer&quot;&gt;(Liu et al., 2025)&lt;/a&gt; suggested 10% QAT was optimal but didn&apos;t explore how this changes with scale.&lt;/p&gt;&lt;h2 id=&quot;key-observations&quot;&gt;Key Observations&lt;/h2&gt;&lt;p&gt;We trained models from 86M to 2.2B parameters across token counts ranging from billions to trillions, testing 1-bit through 6-bit QAT to see how performance changes.&lt;/p&gt;&lt;h3 id=&quot;the-optimal-qat-fraction-increases-with-scale&quot;&gt;The Optimal QAT Fraction Increases With Scale&lt;/h3&gt;&lt;p&gt;What we discover is that the optimal QAT fraction isn&apos;t fixed at 10%. It grows with your total compute budget, ranging from 10-15% for small-scale training to 55% or even more for large-scale training.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;The intuition.&lt;/strong&gt; Longer full-precision training packs more and more information in high-precision bits, making subsequent quantization harder. Therefore, the model needs more QAT steps to adapt to the precision loss. In fact, not just proportionally more steps but the portion itself starts to grow.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Another intuition idea is from the &lt;b&gt;&lt;strong style=&quot;white-space: pre-wrap;&quot;&gt;optimization perspective&lt;/strong&gt;&lt;/b&gt;: QAT training uses gradient approximations, which negatively impact convergence. Therefore, we want to have as few QAT steps as possible to not waste compute on a sub-optimal optimization process.&lt;/div&gt;&lt;/div&gt;&lt;h2 id=&quot;predicting-optimal-fractions-from-tokens-per-parameter-byte&quot;&gt;Predicting Optimal Fractions From Tokens-Per-Parameter-Byte&lt;/h2&gt;&lt;p&gt;The optimal QAT fraction can be predicted using the tokens-per-parameter-byte statistic.&lt;/p&gt;&lt;p&gt;$$S_{\text{total}} = \frac{D_{\text{total}}}{N \cdot \frac{B}{8}},$$&lt;/p&gt;&lt;p&gt;where \(D_{\text{total}}\) is the total number of tokens, \(N\) is the parameter count, and \(B\) is the QAT bit-width. This metric captures several key insights:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Larger models are easier to quantize (higher \(N\) → lower \(S_{\text{total}}\)&lt;/li&gt;&lt;li&gt;Models trained longer are harder to quantize (higher \(D_{\text{total}}\) → higher \(S_{\text{total}}\))&lt;/li&gt;&lt;li&gt;Lower bit-widths are harder to quantize (lower \(B\) → higher \(S_{\text{total}}\))&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;We achieve a low mean absolute error in predicting optimal QAT fractions across all experiments by using such a simple predictor:&lt;/p&gt;
&lt;!--kg-card-begin: html--&gt;
$$\widehat{f}(D_\text{total}, N, B) = \frac{\exp\left(\log{S_\text{total}} - \frac{6.7297}{\log{S_\text{total}}}\right)}{S_\text{total}}.$$
&lt;!--kg-card-end: html--&gt;
&lt;figure class=&quot;kg-card kg-image-card kg-card-hascaption&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2025/10/Screenshot-2025-10-30-at-17.49.53.png&quot; class=&quot;kg-image&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;1708&quot; height=&quot;1176&quot; srcset=&quot;https://alexdremov.me/content/images/size/w600/2025/10/Screenshot-2025-10-30-at-17.49.53.png 600w, https://alexdremov.me/content/images/size/w1000/2025/10/Screenshot-2025-10-30-at-17.49.53.png 1000w, https://alexdremov.me/content/images/size/w1600/2025/10/Screenshot-2025-10-30-at-17.49.53.png 1600w, https://alexdremov.me/content/images/2025/10/Screenshot-2025-10-30-at-17.49.53.png 1708w&quot; sizes=&quot;(min-width: 720px) 720px&quot;&gt;&lt;figcaption&gt;&lt;span style=&quot;white-space: pre-wrap;&quot;&gt;QAT optima for 396M model plotted in tokens-per-parameter-byte coordinates for different bit-widths&lt;/span&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-purple&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💥&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;While this formula performs well, it is fitted only on &lt;b&gt;&lt;strong style=&quot;white-space: pre-wrap;&quot;&gt;optimal QAT data points.&lt;/strong&gt;&lt;/b&gt; This ignores many non-optimal data points, which also contain useful information about loss behavior.&lt;br&gt;&lt;br&gt;To capture full information, we can try predicting loss directly.&lt;/div&gt;&lt;/div&gt;&lt;h2 id=&quot;loss-scaling-law&quot;&gt;Loss Scaling Law&lt;/h2&gt;&lt;p&gt;As noted, we moved to deriving a comprehensive loss scaling law that models final loss as a function of parameter count (\(N\)), full-precision tokens (\(D_{\text{fp}}\)), QAT tokens (\(D_{\text{qat}}\)), and bit-width (\(B\)). It not only predicts the final model&apos;s performance but also captures the observed phenomena of optimal QAT fraction:&lt;/p&gt;
&lt;!--kg-card-begin: html--&gt;
$$L(N, D_\text{qat}, D_\text{fp}, B) = \underbrace{
  \alpha + \frac{\beta}{D_{\text{total}}^{\gamma}} + \frac{\zeta}{N^{\eta}}
}_{
  \text{Chinchilla-like loss}
}
+
\underbrace{
    \delta(N, D_\text{qat}, D_\text{fp}, B)
}_{
  \text{QAT fraction-aware penalty}
},$$
&lt;!--kg-card-end: html--&gt;

&lt;!--kg-card-begin: html--&gt;
$$\delta(N, D_\text{qat}, D_\text{fp}, B) =
\underbrace{
  \theta \cdot 2^{- \kappa \cdot B}}_{
    \text{Irreducible QAT error}
} +
\underbrace{
  \frac{\phi \cdot 2^{- \chi \cdot B}}{N^{\psi} \cdot S_{\text{qat}}^{\omega}}}_{
    \text{Pure QAT penalty}
}
+ \underbrace{
  \frac{\lambda \cdot 2^{- \mu \cdot B}}{N^{\nu} \cdot S_{\text{fp}}^{\xi} \cdot S_{\text{qat}}^{\rho}}
}_{
  \text{FP / QAT interaction}
}.$$
&lt;!--kg-card-end: html--&gt;
&lt;p&gt;The QAT penalty term includes:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Irreducible QAT error&lt;/strong&gt;: Baseline penalty dependent on bit-width&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Pure QAT penalty&lt;/strong&gt;: Loss that decreases with more QAT training&lt;/li&gt;&lt;li&gt;&lt;strong&gt;FP/QAT interaction&lt;/strong&gt;: Captures how FP training length affects QAT difficulty&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The scaling law achieves $R^2 = 0.982-0.991$ across different bit-widths. Moreover, we can infer the optimal QAT fraction for a given compute by finding a minimum point with $D_\text{qat} + D_\text{fp} = const$. That&apos;s how the loss plot looks:&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;figure class=&quot;kg-card kg-image-card kg-card-hascaption&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2025/10/Screenshot-2025-10-30-at-18.49.34.png&quot; class=&quot;kg-image&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;1418&quot; height=&quot;1210&quot; srcset=&quot;https://alexdremov.me/content/images/size/w600/2025/10/Screenshot-2025-10-30-at-18.49.34.png 600w, https://alexdremov.me/content/images/size/w1000/2025/10/Screenshot-2025-10-30-at-18.49.34.png 1000w, https://alexdremov.me/content/images/2025/10/Screenshot-2025-10-30-at-18.49.34.png 1418w&quot; sizes=&quot;(min-width: 720px) 720px&quot;&gt;&lt;figcaption&gt;&lt;span style=&quot;white-space: pre-wrap;&quot;&gt;Visualization of fitted loss scaling law for 759M model, 1-bit QAT, and different \(D_\text{qat}\), \(D_\text{fp}\). Orange lines represent constant \(D_\text{total} = D_\text{qat} + D_\text{fp}\) levels, and stars represent loss minima for each such level. It is clearly seen that the loss structure yields an optimal QAT fraction for a specific \(D_\text{total}\).&lt;/span&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;You can try exploring the scaling law through the following interactive plot:&lt;/p&gt;
&lt;!--kg-card-begin: html--&gt;
&lt;section class=&quot;loss-plot toc-ignore&quot;&gt;
&lt;div class=&quot;container&quot;&gt;
  &lt;div id=&quot;canvas-container&quot;&gt;&lt;/div&gt;
  
  &lt;div class=&quot;controls&quot;&gt;
        
      &lt;div class=&quot;control-group&quot;&gt;
          &lt;h3 style=&quot;margin-bottom: 15px; color: var(--color-accent); font-size: 16px;&quot;&gt;Dfp Range&lt;/h3&gt;
          &lt;div class=&quot;control-label&quot;&gt;
              &lt;span class=&quot;control-name&quot;&gt;Min - Max&lt;/span&gt;
              &lt;span class=&quot;control-value&quot; id=&quot;dfp-range-value&quot;&gt;100B - 10T&lt;/span&gt;
          &lt;/div&gt;
          &lt;div style=&quot;margin-bottom: 8px;&quot;&gt;
              &lt;label style=&quot;font-size: 12px; color: var(--color-text-secondary);&quot;&gt;Min:&lt;/label&gt;
              &lt;input type=&quot;range&quot; id=&quot;dfp-min-slider&quot; min=&quot;0&quot; max=&quot;100&quot; value=&quot;20&quot; step=&quot;1&quot;&gt;
          &lt;/div&gt;
          &lt;div&gt;
              &lt;label style=&quot;font-size: 12px; color: var(--color-text-secondary);&quot;&gt;Max:&lt;/label&gt;
              &lt;input type=&quot;range&quot; id=&quot;dfp-max-slider&quot; min=&quot;0&quot; max=&quot;100&quot; value=&quot;90&quot; step=&quot;1&quot;&gt;
          &lt;/div&gt;
      &lt;/div&gt;
      
      &lt;div class=&quot;control-group&quot;&gt;
          &lt;h3 style=&quot;margin-bottom: 15px; color: var(--color-accent); font-size: 16px;&quot;&gt;Dqat Range&lt;/h3&gt;
          &lt;div class=&quot;control-label&quot;&gt;
              &lt;span class=&quot;control-name&quot;&gt;Min - Max&lt;/span&gt;
              &lt;span class=&quot;control-value&quot; id=&quot;dqat-range-value&quot;&gt;100B - 10T&lt;/span&gt;
          &lt;/div&gt;
          &lt;div style=&quot;margin-bottom: 8px;&quot;&gt;
              &lt;label style=&quot;font-size: 12px; color: var(--color-text-secondary);&quot;&gt;Min:&lt;/label&gt;
              &lt;input type=&quot;range&quot; id=&quot;dqat-min-slider&quot; min=&quot;0&quot; max=&quot;100&quot; value=&quot;20&quot; step=&quot;1&quot;&gt;
          &lt;/div&gt;
          &lt;div&gt;
              &lt;label style=&quot;font-size: 12px; color: var(--color-text-secondary);&quot;&gt;Max:&lt;/label&gt;
              &lt;input type=&quot;range&quot; id=&quot;dqat-max-slider&quot; min=&quot;0&quot; max=&quot;100&quot; value=&quot;90&quot; step=&quot;1&quot;&gt;
          &lt;/div&gt;
      &lt;/div&gt;
      &lt;div class=&quot;control-group&quot; style=&quot;grid-column: 1 / -1;&quot;&gt;
          &lt;h3 style=&quot;margin-bottom: 15px; color: var(--color-accent); font-size: 16px;&quot;&gt;Model Parameters&lt;/h3&gt;
          &lt;div class=&quot;control-label&quot;&gt;
              &lt;span class=&quot;control-name&quot;&gt;N (Parameters)&lt;/span&gt;
              &lt;span class=&quot;control-value&quot; id=&quot;n-value&quot;&gt;1.00B&lt;/span&gt;
          &lt;/div&gt;
          &lt;input type=&quot;range&quot; id=&quot;n-slider&quot; min=&quot;0&quot; max=&quot;100&quot; value=&quot;25&quot; step=&quot;1&quot;&gt;
          
          &lt;div class=&quot;control-label&quot; style=&quot;margin-top: 15px;&quot;&gt;
              &lt;span class=&quot;control-name&quot;&gt;B (Bit-width)&lt;/span&gt;
              &lt;span class=&quot;control-value&quot; id=&quot;b-value&quot;&gt;4&lt;/span&gt;
          &lt;/div&gt;
          &lt;input type=&quot;range&quot; id=&quot;b-slider&quot; min=&quot;1&quot; max=&quot;8&quot; value=&quot;2&quot; step=&quot;1&quot;&gt;
      &lt;/div&gt;
  &lt;/div&gt;
  
  &lt;p class=&quot;info&quot;&gt;Drag to rotate • Scroll to zoom • Adjust sliders to explore the scaling law&lt;/p&gt;
&lt;/div&gt;
&lt;/section&gt;

&lt;script src=&quot;https://cdn.jsdelivr.net/npm/three@0.128.0/build/three.min.js&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/OrbitControls.js&quot;&gt;&lt;/script&gt;
&lt;script&gt;
  // Scaling law formula coefficients
  const CONSTANTS = {
      constant: 1.598,
      dtotal_coeff: 2477.0,
      dtotal_power: 0.4089,
      n_coeff: 57.64,
      n_power: 0.2148,
      b_const_coeff: 0.4297,
      b_const_power: -1.41,
      b_sqat_coeff: 1091.0,
      b_sqat_power: -1.212,
      n_sqat_power: 0.4004,
      sqat_power1: 0.076,
      b_final_coeff: 138.8,
      b_final_power: -0.0833,
      n_final_power: 0.2135,
      sfp_power: 0.4819,
      sqat_power2: 0.1903
  };

  // Parameter ranges
  const N_MIN = 100e6;
  const N_MAX = 1e12;
  const B_MIN = 1;
  const B_MAX = 8;
  
  // Compute token ranges
  const COMPUTE_MIN = 1e9;  // 1N
  const COMPUTE_MAX = 20e12;  // 20T
  const DFP_DEFAULT_MIN = 100e9;  // 100B
  const DFP_DEFAULT_MAX = 10e12;  // 10T
  const DQAT_DEFAULT_MIN = 100e9;  // 100B
  const DQAT_DEFAULT_MAX = 10e12;  // 10T
  
  // Surface resolution
  const GRID_POINTS = 50;

  // Three.js setup
  let scene, camera, renderer, controls;
  let surfaceMesh, gridHelper;
  let minLoss = Infinity;
  let maxLoss = -Infinity;
  let axisTicksGroup = new THREE.Group();
  let isoTokenLinesGroup = new THREE.Group();
  let isoTokenOptimalPointsGroup = new THREE.Group();
  let optimalPointLabelsGroup = new THREE.Group();

  function init() {
      const container = document.getElementById(&apos;canvas-container&apos;);
      
      // Scene
      scene = new THREE.Scene();
      // scene.background = new THREE.Color(0x0a0e14);
      
      // Camera
      camera = new THREE.PerspectiveCamera(
          45,
          container.clientWidth / container.clientHeight,
          0.1,
          1000
      );
      camera.position.set(40, 35, 40);
      
      // Renderer
      renderer = new THREE.WebGLRenderer(
        { antialias: true, alpha: true}
      );
      renderer.setSize(container.clientWidth, container.clientHeight);
      renderer.setPixelRatio(window.devicePixelRatio);
      renderer.setClearColor(0x000000, 0);
      container.appendChild(renderer.domElement);
      
      // Controls
      controls = new THREE.OrbitControls(camera, renderer.domElement);
      controls.enableDamping = true;
      controls.dampingFactor = 0.05;
      
      // Lights
      const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
      scene.add(ambientLight);
      
      const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
      directionalLight.position.set(10, 10, 10);
      scene.add(directionalLight);
      
      // Grid helper
      gridHelper = new THREE.GridHelper(40, 20, 0x32b8c6, 0x1a1a1a);
      gridHelper.position.y = 0;
      scene.add(gridHelper);
      
      // Add axes
      addAxes();
      
      // Add ticks group to scene
      scene.add(axisTicksGroup);
      
      // Add iso-token groups to scene
      scene.add(isoTokenLinesGroup);
      scene.add(isoTokenOptimalPointsGroup);
      scene.add(optimalPointLabelsGroup);
      
      // Create initial surface
      updateSurface();
      
      // Handle window resize
      window.addEventListener(&apos;resize&apos;, onWindowResize);
      
      // Animation loop
      animate();
  }

  function addAxes() {
      const axisLength = 25;
      const origin = new THREE.Vector3(-20, 0, -20);
      
      // X axis (Dfp) - RED
      const xAxis = new THREE.ArrowHelper(
          new THREE.Vector3(1, 0, 0),
          origin,
          axisLength,
          0xffffff,
          2,
          1
      );
      scene.add(xAxis);
      
      // Y axis (Loss) - BLUE
      const yAxis = new THREE.ArrowHelper(
          new THREE.Vector3(0, 1, 0),
          origin,
          axisLength,
          0xffffff,
          2,
          1
      );
      scene.add(yAxis);
      
      // Z axis (Dqat) - GREEN
      const zAxis = new THREE.ArrowHelper(
          new THREE.Vector3(0, 0, 1),
          origin,
          axisLength,
          0xffffff,
          2,
          1
      );
      scene.add(zAxis);
      
      // Add axis labels
      addAxisLabel(&apos;Dfp&apos;, new THREE.Vector3(-20 + axisLength + 3, 0, -20), 0xffffff);
      addAxisLabel(&apos;Perplexity&apos;, new THREE.Vector3(-20, axisLength + 3, -20), 0xffffff);
      addAxisLabel(&apos;Dqat&apos;, new THREE.Vector3(-20, 0, -20 + axisLength + 3), 0xffffff);
  }
  
  function addAxisLabel(text, position, color) {
      // Create canvas for text
      const canvas = document.createElement(&apos;canvas&apos;);
      const context = canvas.getContext(&apos;2d&apos;);
      canvas.width = 256;
      canvas.height = 128;
      
      // Draw text
      context.fillStyle = &apos;#&apos; + color.toString(16).padStart(6, &apos;0&apos;);
      context.font = &apos;Bold 48px Arial&apos;;
      context.textAlign = &apos;center&apos;;
      context.textBaseline = &apos;middle&apos;;
      context.fillText(text, 128, 64);
      
      // Create texture
      const texture = new THREE.CanvasTexture(canvas);
      
      // Create sprite material
      const material = new THREE.SpriteMaterial({ 
          map: texture,
          transparent: true
      });
      
      // Create sprite
      const sprite = new THREE.Sprite(material);
      sprite.position.copy(position);
      sprite.scale.set(6, 3, 1);
      
      scene.add(sprite);
  }

  function removeAxisTicks() {
      // Remove all existing ticks and labels
      while(axisTicksGroup.children.length &gt; 0) {
          const child = axisTicksGroup.children[0];
          axisTicksGroup.remove(child);
          if (child.geometry) child.geometry.dispose();
          if (child.material) {
              if (child.material.map) child.material.map.dispose();
              child.material.dispose();
          }
      }
  }

  function createAxisTicks(axisDirection, values, labels, axisColor, origin, perpDir1, perpDir2) {
      const tickLength = 0.8;
      
      for (let i = 0; i &lt; values.length; i++) {
          const t = values[i];
          const label = labels[i];
          
          // Calculate position along axis
          let tickPos = origin.clone();
          if (axisDirection === &apos;x&apos;) {
              tickPos.x += t;
          } else if (axisDirection === &apos;y&apos;) {
              tickPos.y += t;
          } else if (axisDirection === &apos;z&apos;) {
              tickPos.z += t;
          }
          
          // Create tick mark (small line perpendicular to axis)
          const tickGeometry = new THREE.BufferGeometry();
          const tickStart = tickPos.clone().add(perpDir1.clone().multiplyScalar(-tickLength/2));
          const tickEnd = tickPos.clone().add(perpDir1.clone().multiplyScalar(tickLength/2));
          tickGeometry.setAttribute(&apos;position&apos;, new THREE.Float32BufferAttribute([
              tickStart.x, tickStart.y, tickStart.z,
              tickEnd.x, tickEnd.y, tickEnd.z
          ], 3));
          
          const tickMaterial = new THREE.LineBasicMaterial({ color: axisColor });
          const tickLine = new THREE.Line(tickGeometry, tickMaterial);
          axisTicksGroup.add(tickLine);
          
          // Create label
          const canvas = document.createElement(&apos;canvas&apos;);
          const context = canvas.getContext(&apos;2d&apos;);
          canvas.width = 256;
          canvas.height = 128;
          
          context.fillStyle = &apos;#&apos; + axisColor.toString(16).padStart(6, &apos;0&apos;);
          context.font = &apos;Bold 40px Arial&apos;;
          context.textAlign = &apos;center&apos;;
          context.textBaseline = &apos;middle&apos;;
          context.fillText(label, 128, 64);
          
          const texture = new THREE.CanvasTexture(canvas);
          const spriteMaterial = new THREE.SpriteMaterial({ 
              map: texture,
              transparent: true
          });
          
          const sprite = new THREE.Sprite(spriteMaterial);
          const labelOffset = perpDir1.clone().multiplyScalar(2.5);
          sprite.position.copy(tickPos).add(labelOffset);
          sprite.scale.set(4, 2, 1);
          
          axisTicksGroup.add(sprite);
      }
  }

  function addAxisTicksForCurrentSurface(N, minPerplexity, maxPerplexity, dfpMin, dfpMax, dqatMin, dqatMax) {
      // Remove old ticks
      removeAxisTicks();
      
      const origin = new THREE.Vector3(-20, 0, -20);
      const numTicks = 5;
      
      // X-axis (Dfp) ticks - log spaced from N*5 to N*50
      const dfpValues = logSpace(dfpMin, dfpMax, numTicks);
      const dfpPositions = [];
      const dfpLabels = [];
      
      for (let i = 0; i &lt; numTicks; i++) {
          // Map from data space to visual space (0 to 40)
          const logPos = (Math.log10(dfpValues[i]) - Math.log10(dfpMin)) / 
                         (Math.log10(dfpMax) - Math.log10(dfpMin));
          dfpPositions.push(logPos * 40);
          dfpLabels.push(formatNumber(dfpValues[i]));
      }
      
      createAxisTicks(&apos;x&apos;, dfpPositions, dfpLabels, 0xff0000, origin, 
                     new THREE.Vector3(0, -1, 0), new THREE.Vector3(0, 0, 1));
      
      // Z-axis (Dqat) ticks - log spaced from N*5 to N*50
      const dqatValues = logSpace(dqatMin, dqatMax, numTicks);
      const dqatPositions = [];
      const dqatLabels = [];
      
      for (let i = 0; i &lt; numTicks; i++) {
          const logPos = (Math.log10(dqatValues[i]) - Math.log10(dqatMin)) / 
                         (Math.log10(dqatMax) - Math.log10(dqatMin));
          dqatPositions.push(logPos * 40);
          dqatLabels.push(formatNumber(dqatValues[i]));
      }
      
      createAxisTicks(&apos;z&apos;, dqatPositions, dqatLabels, 0x00ff00, origin,
                     new THREE.Vector3(-1, 0, 0), new THREE.Vector3(0, 1, 0));
      
      // Y-axis (Perplexity) ticks - linear spaced from minPerplexity to maxPerplexity
      const perplexityRange = maxPerplexity - minPerplexity;
      const perplexityPositions = [];
      const perplexityLabels = [];
      
      if (perplexityRange &gt; 0) {
          const scaleY = 20 / perplexityRange;
          for (let i = 0; i &lt; numTicks; i++) {
              const t = i / (numTicks - 1);
              const perplexityValue = minPerplexity + t * perplexityRange;
              perplexityPositions.push(t * 20);
              perplexityLabels.push(perplexityValue.toFixed(2));
          }
      } else {
          // Edge case: all perplexities are the same
          perplexityPositions.push(0);
          perplexityLabels.push(minPerplexity.toFixed(2));
      }
      
      createAxisTicks(&apos;y&apos;, perplexityPositions, perplexityLabels, 0x0000ff, origin,
                     new THREE.Vector3(-1, 0, 0), new THREE.Vector3(0, 0, 1));
  }

  function computeLoss(Dqat, Dfp, N, B) {
      // Avoid division by zero
      if (N &lt;= 0 || B &lt;= 0 || Dqat &lt;= 0 || Dfp &lt;= 0) {
          return Infinity;
      }
      
      const Dtotal = Dfp + Dqat;
      const Sqat = Dqat / (N * B / 8);
      const Sfp = Dfp / (N * B / 8);
      
      // Handle edge cases
      if (Sqat &lt;= 0 || Sfp &lt;= 0) {
          return Infinity;
      }
      
      // L(Dqat, Dfp, N, B) formula
      const term1 = CONSTANTS.constant;
      const term2 = CONSTANTS.dtotal_coeff / Math.pow(Dtotal, CONSTANTS.dtotal_power);
      const term3 = CONSTANTS.n_coeff / Math.pow(N, CONSTANTS.n_power);
      const term4 = CONSTANTS.b_const_coeff * Math.pow(2, CONSTANTS.b_const_power * B);
      const term5 = CONSTANTS.b_sqat_coeff * Math.pow(2, CONSTANTS.b_sqat_power * B) / 
                   (Math.pow(N, CONSTANTS.n_sqat_power) * Math.pow(Sqat, CONSTANTS.sqat_power1));
      const term6 = CONSTANTS.b_final_coeff * Math.pow(2, CONSTANTS.b_final_power * B) / 
                   (Math.pow(N, CONSTANTS.n_final_power) * Math.pow(Sfp, CONSTANTS.sfp_power) * 
                    Math.pow(Sqat, CONSTANTS.sqat_power2));
      
      return term1 + term2 + term3 + term4 + term5 + term6;
  }

  function logSpace(min, max, count) {
      const logMin = Math.log10(min);
      const logMax = Math.log10(max);
      const step = (logMax - logMin) / (count - 1);
      const result = [];
      
      for (let i = 0; i &lt; count; i++) {
          result.push(Math.pow(10, logMin + step * i));
      }
      
      return result;
  }

  function linSpace(min, max, count) {
      const step = (max - min) / (count - 1);
      const result = [];
      
      for (let i = 0; i &lt; count; i++) {
          result.push(min + step * i);
      }
      
      return result;
  }

  function getColorForLoss(loss, minLoss, maxLoss) {
      const normalized = (loss - minLoss) / (maxLoss - minLoss);
      const color = new THREE.Color();
      
      // Blue -&gt; Cyan -&gt; Green -&gt; Yellow -&gt; Red
      if (normalized &lt; 0.25) {
          const t = normalized / 0.25;
          color.setRGB(0, t, 1);
      } else if (normalized &lt; 0.5) {
          const t = (normalized - 0.25) / 0.25;
          color.setRGB(0, 1, 1 - t);
      } else if (normalized &lt; 0.75) {
          const t = (normalized - 0.5) / 0.25;
          color.setRGB(t, 1, 0);
      } else {
          const t = (normalized - 0.75) / 0.25;
          color.setRGB(1, 1 - t, 0);
      }
      
      return color;
  }

  function updateSurface() {
      // Get current N and B values
      const nSlider = document.getElementById(&apos;n-slider&apos;);
      const bSlider = document.getElementById(&apos;b-slider&apos;);
      
      const N = logValue(parseFloat(nSlider.value) / 100, N_MIN, N_MAX);
      const B = parseInt(bSlider.value);
      
      // Get compute ranges from sliders (logarithmic scale)
      const dfpMinSliderVal = parseFloat(document.getElementById(&apos;dfp-min-slider&apos;).value);
      const dfpMaxSliderVal = parseFloat(document.getElementById(&apos;dfp-max-slider&apos;).value);
      const dqatMinSliderVal = parseFloat(document.getElementById(&apos;dqat-min-slider&apos;).value);
      const dqatMaxSliderVal = parseFloat(document.getElementById(&apos;dqat-max-slider&apos;).value);
      
      // Convert from slider position (0-100) to actual compute values (logarithmic)
      const dfpMin = logValue(dfpMinSliderVal / 100, COMPUTE_MIN, COMPUTE_MAX);
      const dfpMax = logValue(dfpMaxSliderVal / 100, COMPUTE_MIN, COMPUTE_MAX);
      const dqatMin = logValue(dqatMinSliderVal / 100, COMPUTE_MIN, COMPUTE_MAX);
      const dqatMax = logValue(dqatMaxSliderVal / 100, COMPUTE_MIN, COMPUTE_MAX);
      
      // Generate grid (25x25 points)
      const dfpValues = logSpace(dfpMin, dfpMax, GRID_POINTS);
      const dqatValues = logSpace(dqatMin, dqatMax, GRID_POINTS);
      
      // Compute losses and find min/max for color mapping
      const losses = [];
      minLoss = Infinity;
      maxLoss = -Infinity;
      let minPerplexity = Infinity;
      let maxPerplexity = -Infinity;
      
      for (let i = 0; i &lt; GRID_POINTS; i++) {
          losses[i] = [];
          for (let j = 0; j &lt; GRID_POINTS; j++) {
              const loss = computeLoss(dqatValues[j], dfpValues[i], N, B);
              if (isFinite(loss)) {
                  losses[i][j] = loss;
                  minLoss = Math.min(minLoss, loss);
                  maxLoss = Math.max(maxLoss, loss);
                  const perplexity = Math.exp(loss);
                  minPerplexity = Math.min(minPerplexity, perplexity);
                  maxPerplexity = Math.max(maxPerplexity, perplexity);
              } else {
                  losses[i][j] = maxLoss;
              }
          }
      }
      
      // Create geometry
      const geometry = new THREE.BufferGeometry();
      const vertices = [];
      const colors = [];
      const indices = [];
      
      const scaleX = 40 / (GRID_POINTS - 1);
      const scaleZ = 40 / (GRID_POINTS - 1);
      const scaleY = maxPerplexity &gt; minPerplexity ? 20 / (maxPerplexity - minPerplexity) : 1;
      
      // Create vertices with colors
      for (let i = 0; i &lt; GRID_POINTS; i++) {
          for (let j = 0; j &lt; GRID_POINTS; j++) {
              const x = i * scaleX - 20;
              const z = j * scaleZ - 20;
              const loss = losses[i][j];
              const perplexity = Math.exp(loss);
              const y = (perplexity - minPerplexity) * scaleY;
              
              vertices.push(x, y, z);
              
              const color = getColorForLoss(loss, minLoss, maxLoss);
              colors.push(color.r, color.g, color.b);
          }
      }
      
      // Create faces
      for (let i = 0; i &lt; GRID_POINTS - 1; i++) {
          for (let j = 0; j &lt; GRID_POINTS - 1; j++) {
              const a = i * GRID_POINTS + j;
              const b = i * GRID_POINTS + (j + 1);
              const c = (i + 1) * GRID_POINTS + (j + 1);
              const d = (i + 1) * GRID_POINTS + j;
              
              indices.push(a, b, d);
              indices.push(b, c, d);
          }
      }
      
      geometry.setAttribute(&apos;position&apos;, new THREE.Float32BufferAttribute(vertices, 3));
      geometry.setAttribute(&apos;color&apos;, new THREE.Float32BufferAttribute(colors, 3));
      geometry.setIndex(indices);
      geometry.computeVertexNormals();
      
      const material = new THREE.MeshPhongMaterial({
          vertexColors: true,
          side: THREE.DoubleSide,
          shininess: 30,
          flatShading: false
      });
      
      // Remove old mesh
      if (surfaceMesh) {
          scene.remove(surfaceMesh);
          surfaceMesh.geometry.dispose();
          surfaceMesh.material.dispose();
      }
      
      // Add new mesh
      surfaceMesh = new THREE.Mesh(geometry, material);
      scene.add(surfaceMesh);
      
      // Add wireframe
      const wireframe = new THREE.WireframeGeometry(geometry);
      const line = new THREE.LineSegments(wireframe);
      line.material.color.setHex(0x222222);
      line.material.opacity = 0.3;
      line.material.transparent = true;
      surfaceMesh.add(line);
      
      // Add axis ticks with labels
      addAxisTicksForCurrentSurface(N, minPerplexity, maxPerplexity, dfpMin, dfpMax, dqatMin, dqatMax);
      
      // Add iso-token lines and optimal points
      addIsoTokenLines(N, B, dfpMin, dfpMax, dqatMin, dqatMax, minLoss, maxLoss, minPerplexity, maxPerplexity, scaleX, scaleZ, scaleY);
  }

  function addIsoTokenLines(N, B, dfpMin, dfpMax, dqatMin, dqatMax, minLoss, maxLoss, minPerplexity, maxPerplexity, scaleX, scaleZ, scaleY) {
      // Clear previous iso-token lines and points
      while(isoTokenLinesGroup.children.length &gt; 0) {
          const child = isoTokenLinesGroup.children[0];
          isoTokenLinesGroup.remove(child);
          if (child.geometry) child.geometry.dispose();
          if (child.material) child.material.dispose();
      }
      
      while(isoTokenOptimalPointsGroup.children.length &gt; 0) {
          const child = isoTokenOptimalPointsGroup.children[0];
          isoTokenOptimalPointsGroup.remove(child);
          if (child.geometry) child.geometry.dispose();
          if (child.material) child.material.dispose();
      }
      
      // Clear previous optimal point labels
      while(optimalPointLabelsGroup.children.length &gt; 0) {
          const child = optimalPointLabelsGroup.children[0];
          optimalPointLabelsGroup.remove(child);
          if (child.material &amp;&amp; child.material.map) child.material.map.dispose();
          if (child.material) child.material.dispose();
      }
      
      // Define iso-total-token levels (10 levels)
      const numLevels = 10;
      const minTotalTokens = dfpMin + dqatMin;
      const maxTotalTokens = dfpMax + dqatMax;
      
      // Logarithmic spacing for iso-token levels
      const logMinTotal = Math.log(minTotalTokens);
      const logMaxTotal = Math.log(maxTotalTokens);
      const logStep = (logMaxTotal - logMinTotal) / (numLevels - 1);
      
      const isoTokenLevels = [];
      for (let i = 0; i &lt; numLevels; i++) {
          isoTokenLevels.push(Math.exp(logMinTotal + i * logStep));
      }
      
      // For each iso-token level, find optimal point and draw line
      for (let levelIdx = 0; levelIdx &lt; isoTokenLevels.length; levelIdx++) {
          const totalTokens = isoTokenLevels[levelIdx];
          
          // Sample along the line Dfp + Dqat = totalTokens
          const numSamples = 1000;
          const dfpSamples = [];
          const dqatSamples = [];
          const lossSamples = [];
          
          // Sample Dfp from dfpMin to totalTokens, compute Dqat = totalTokens - Dfp
          for (let i = 0; i &lt; numSamples; i++) {
              const t = i / (numSamples - 1);
              const dfp = dfpMin + t * (totalTokens - dfpMin);
              const dqat = totalTokens - dfp;
              
              // Check if point is valid (within bounds)
              if (dqat &gt;= dqatMin &amp;&amp; dqat &lt;= dqatMax &amp;&amp; dfp &gt;= dfpMin &amp;&amp; dfp &lt;= dfpMax) {
                  const loss = computeLoss(dqat, dfp, N, B);
                  
                  if (isFinite(loss)) {
                      dfpSamples.push(dfp);
                      dqatSamples.push(dqat);
                      lossSamples.push(loss);
                  }
              }
          }
          
          if (dfpSamples.length === 0) continue;
          
          // Find optimal point (minimum loss)
          let minLossIdx = 0;
          let minLossValue = lossSamples[0];
          for (let i = 1; i &lt; lossSamples.length; i++) {
              if (lossSamples[i] &lt; minLossValue) {
                  minLossValue = lossSamples[i];
                  minLossIdx = i;
              }
          }
          
          const optimalDfp = dfpSamples[minLossIdx];
          const optimalDqat = dqatSamples[minLossIdx];
          const optimalLoss = lossSamples[minLossIdx];
          
          // Convert to 3D coordinates for visualization
          const lineVertices = [];
          for (let i = 0; i &lt; dfpSamples.length; i++) {
              // Map from data space to visual space
              const logDfp = Math.log10(dfpSamples[i]);
              const logDqat = Math.log10(dqatSamples[i]);
              const logDfpMin = Math.log10(dfpMin);
              const logDfpMax = Math.log10(dfpMax);
              const logDqatMin = Math.log10(dqatMin);
              const logDqatMax = Math.log10(dqatMax);
              
              const xNorm = (logDfp - logDfpMin) / (logDfpMax - logDfpMin);
              const zNorm = (logDqat - logDqatMin) / (logDqatMax - logDqatMin);
              
              const x = xNorm * 40 - 20;
              const z = zNorm * 40 - 20;
              const perplexity = Math.exp(lossSamples[i]);
              const y = (perplexity - minPerplexity) * scaleY;
              
              lineVertices.push(x, y, z);
          }
          
          // Create line geometry
          const lineGeometry = new THREE.BufferGeometry();
          lineGeometry.setAttribute(&apos;position&apos;, new THREE.Float32BufferAttribute(lineVertices, 3));
          
          const lineMaterial = new THREE.LineBasicMaterial({ 
              color: 0xffa500, // Orange
              linewidth: 5,
              transparent: true,
              opacity: 0.9,
              depthTest: true
          });
          
          const line = new THREE.Line(lineGeometry, lineMaterial);
          isoTokenLinesGroup.add(line);
          
          // Add optimal point as a star (sphere for now)
          const logOptDfp = Math.log10(optimalDfp);
          const logOptDqat = Math.log10(optimalDqat);
          const logDfpMin = Math.log10(dfpMin);
          const logDfpMax = Math.log10(dfpMax);
          const logDqatMin = Math.log10(dqatMin);
          const logDqatMax = Math.log10(dqatMax);
          
          const xNorm = (logOptDfp - logDfpMin) / (logDfpMax - logDfpMin);
          const zNorm = (logOptDqat - logDqatMin) / (logDqatMax - logDqatMin);
          
          const optX = xNorm * 40 - 20;
          const optZ = zNorm * 40 - 20;
          const optimalPerplexity = Math.exp(optimalLoss);
          const optY = (optimalPerplexity - minPerplexity) * scaleY;
          
          // Create star shape using icosahedron
          const starGeometry = new THREE.IcosahedronGeometry(0.5, 0);
          const starMaterial = new THREE.MeshPhongMaterial({ 
              color: 0x800080, // Purple
              emissive: 0x400040,
              shininess: 100,
              transparent: true,
              opacity: 0.95
          });
          
          const starMesh = new THREE.Mesh(starGeometry, starMaterial);
          starMesh.position.set(optX, optY, optZ);
          
          // Add a small glow around the star
          const glowGeometry = new THREE.IcosahedronGeometry(0.7, 0);
          const glowMaterial = new THREE.MeshBasicMaterial({
              color: 0xff00ff,
              transparent: true,
              opacity: 0.3
          });
          const glowMesh = new THREE.Mesh(glowGeometry, glowMaterial);
          glowMesh.position.set(optX, optY, optZ);
          
          isoTokenOptimalPointsGroup.add(glowMesh);
          isoTokenOptimalPointsGroup.add(starMesh);
          
          // Add percentage label for this optimal point
          const qatPercentage = (optimalDqat / (optimalDqat + optimalDfp)) * 100;
          addOptimalPointLabel(qatPercentage.toFixed(1) + &apos;%&apos;, optX, optY + 1.5, optZ);
      }
  }
  
  function addOptimalPointLabel(text, x, y, z) {
      // Create canvas for text
      const canvas = document.createElement(&apos;canvas&apos;);
      const context = canvas.getContext(&apos;2d&apos;);
      canvas.width = 256;
      canvas.height = 128;
      
      // Draw semi-transparent background
      context.fillStyle = &apos;rgba(128, 0, 128, 0.0)&apos;;
      context.roundRect = function(x, y, w, h, r) {
          if (w &lt; 2 * r) r = w / 2;
          if (h &lt; 2 * r) r = h / 2;
          this.beginPath();
          this.moveTo(x+r, y);
          this.arcTo(x+w, y, x+w, y+h, r);
          this.arcTo(x+w, y+h, x, y+h, r);
          this.arcTo(x, y+h, x, y, r);
          this.arcTo(x, y, x+w, y, r);
          this.closePath();
          return this;
      };
      context.roundRect(40, 30, 176, 68, 10).fill();
      
      // Draw text
      context.fillStyle = &apos;#ffffff&apos;;
      context.font = &apos;Bold 80px Arial&apos;;
      context.textAlign = &apos;center&apos;;
      context.textBaseline = &apos;middle&apos;;
      context.fillText(text, 128, 64);
      
      // Create texture
      const texture = new THREE.CanvasTexture(canvas);
      
      // Create sprite material
      const material = new THREE.SpriteMaterial({ 
          map: texture,
          transparent: true,
          depthTest: false
      });
      
      // Create sprite
      const sprite = new THREE.Sprite(material);
      sprite.position.set(x, y, z);
      sprite.scale.set(3, 1.5, 1);
      
      optimalPointLabelsGroup.add(sprite);
  }

  function animate() {
      requestAnimationFrame(animate);
      controls.update();
      renderer.render(scene, camera);
  }

  function onWindowResize() {
      const container = document.getElementById(&apos;canvas-container&apos;);
      camera.aspect = container.clientWidth / container.clientHeight;
      camera.updateProjectionMatrix();
      renderer.setSize(container.clientWidth, container.clientHeight);
  }

  // Slider utilities
  function logValue(normalized, min, max) {
      const logMin = Math.log10(min);
      const logMax = Math.log10(max);
      return Math.pow(10, logMin + normalized * (logMax - logMin));
  }

  function formatNumber(value) {
      if (value &gt;= 1e12) {
          return (value / 1e12).toFixed(2) + &apos;T&apos;;
      } else if (value &gt;= 1e9) {
          return (value / 1e9).toFixed(2) + &apos;B&apos;;
      } else if (value &gt;= 1e6) {
          return (value / 1e6).toFixed(2) + &apos;M&apos;;
      } else {
          return value.toFixed(2);
      }
  }

  function formatScientific(value) {
      return value.toExponential(2);
  }

  // Slider event listeners
  const nSlider = document.getElementById(&apos;n-slider&apos;);
  const bSlider = document.getElementById(&apos;b-slider&apos;);
  const dfpMinSlider = document.getElementById(&apos;dfp-min-slider&apos;);
  const dfpMaxSlider = document.getElementById(&apos;dfp-max-slider&apos;);
  const dqatMinSlider = document.getElementById(&apos;dqat-min-slider&apos;);
  const dqatMaxSlider = document.getElementById(&apos;dqat-max-slider&apos;);
  
  const nValue = document.getElementById(&apos;n-value&apos;);
  const bValue = document.getElementById(&apos;b-value&apos;);
  const dfpRangeValue = document.getElementById(&apos;dfp-range-value&apos;);
  const dqatRangeValue = document.getElementById(&apos;dqat-range-value&apos;);

  function updateNValue() {
      const n = logValue(parseFloat(nSlider.value) / 100, N_MIN, N_MAX);
      nValue.textContent = formatNumber(n);
      updateSurface();
  }

  function updateBValue() {
      bValue.textContent = bSlider.value;
      updateSurface();
  }
  
  function updateDfpRange() {
      const minSliderVal = parseFloat(dfpMinSlider.value);
      const maxSliderVal = parseFloat(dfpMaxSlider.value);
      
      // Ensure min &lt; max
      if (minSliderVal &gt;= maxSliderVal) {
          dfpMaxSlider.value = minSliderVal + 1;
      }
      
      // Convert to actual values
      const minVal = logValue(parseFloat(dfpMinSlider.value) / 100, COMPUTE_MIN, COMPUTE_MAX);
      const maxVal = logValue(parseFloat(dfpMaxSlider.value) / 100, COMPUTE_MIN, COMPUTE_MAX);
      
      dfpRangeValue.textContent = formatNumber(minVal) + &apos; - &apos; + formatNumber(maxVal);
      updateSurface();
  }
  
  function updateDqatRange() {
      const minSliderVal = parseFloat(dqatMinSlider.value);
      const maxSliderVal = parseFloat(dqatMaxSlider.value);
      
      // Ensure min &lt; max
      if (minSliderVal &gt;= maxSliderVal) {
          dqatMaxSlider.value = minSliderVal + 1;
      }
      
      // Convert to actual values
      const minVal = logValue(parseFloat(dqatMinSlider.value) / 100, COMPUTE_MIN, COMPUTE_MAX);
      const maxVal = logValue(parseFloat(dqatMaxSlider.value) / 100, COMPUTE_MIN, COMPUTE_MAX);
      
      dqatRangeValue.textContent = formatNumber(minVal) + &apos; - &apos; + formatNumber(maxVal);
      updateSurface();
  }

  nSlider.addEventListener(&apos;input&apos;, updateNValue);
  bSlider.addEventListener(&apos;input&apos;, updateBValue);
  dfpMinSlider.addEventListener(&apos;input&apos;, updateDfpRange);
  dfpMaxSlider.addEventListener(&apos;input&apos;, updateDfpRange);
  dqatMinSlider.addEventListener(&apos;input&apos;, updateDqatRange);
  dqatMaxSlider.addEventListener(&apos;input&apos;, updateDqatRange);

  // Initialize after window loads to ensure Three.js is ready
  window.addEventListener(&apos;load&apos;, function() {
      // Double check that THREE is defined
      if (typeof THREE !== &apos;undefined&apos;) {
          init();
          updateNValue();
          updateBValue();
          updateDfpRange();
          updateDqatRange();
      } else {
          console.error(&apos;THREE.js failed to load&apos;);
          document.getElementById(&apos;canvas-container&apos;).innerHTML = &apos;&lt;p style=&quot;color: red; padding: 20px;&quot;&gt;Error: Three.js library failed to load. Please refresh the page.&lt;/p&gt;&apos;;
      }
  });
&lt;/script&gt;
&lt;!--kg-card-end: html--&gt;
&lt;h2 id=&quot;practical-predictions&quot;&gt;Practical Predictions&lt;/h2&gt;&lt;p&gt;Ok, we know that there&apos;s an optimal QAT fraction, but how bad is a sub-optimal fraction? We can compare optimal and sub-optimal setups from the perspective of &quot;wasted tokens&quot; — how many more tokens you need to spend with a sub-optimal setup to match an optimal one.&lt;/p&gt;&lt;h3 id=&quot;quantifying-wasted-compute&quot;&gt;Quantifying wasted compute&lt;/h3&gt;&lt;p&gt;Using the fitted scaling law, we can quantify how bad a sub-optimal setup is. Comparing 10% QAT to optimal fractions reveals significant inefficiencies:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;1-bit QAT&lt;/strong&gt;: Up to 50% wasted tokens&lt;/li&gt;&lt;li&gt;&lt;strong&gt;2-4-bit QAT&lt;/strong&gt;: 5-30% wasted tokens&lt;/li&gt;&lt;li&gt;&lt;strong&gt;6-bit QAT&lt;/strong&gt;: 5-10% wasted tokens&lt;/li&gt;&lt;/ul&gt;&lt;figure class=&quot;kg-card kg-image-card kg-card-hascaption&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2025/10/Screenshot-2025-10-30-at-19.01.25.png&quot; class=&quot;kg-image&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;1398&quot; height=&quot;976&quot; srcset=&quot;https://alexdremov.me/content/images/size/w600/2025/10/Screenshot-2025-10-30-at-19.01.25.png 600w, https://alexdremov.me/content/images/size/w1000/2025/10/Screenshot-2025-10-30-at-19.01.25.png 1000w, https://alexdremov.me/content/images/2025/10/Screenshot-2025-10-30-at-19.01.25.png 1398w&quot; sizes=&quot;(min-width: 720px) 720px&quot;&gt;&lt;figcaption&gt;&lt;span style=&quot;white-space: pre-wrap;&quot;&gt;Comparison of sub-optimal QAT setup with fixed 10% QAT fraction and optimal QAT setup for 1B parameter model. Wasted token count is the number of tokens effectively wasted by not utilizing an optimal QAT fraction setup. That is, if the wasted token count is n%, then the same loss can be achieved with (100− n)% tokens and optimal QAT fraction. While results vary for different bit widths, the general relationship is similar, revealing high potential savings.&lt;/span&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;h3 id=&quot;optimal-bit-width-under-memory-constraints&quot;&gt;Optimal bit-width under memory constraints&lt;/h3&gt;&lt;p&gt;Another useful use-case is inferring optimal QAT bit-width. Given a fixed memory budget, the scaling law determines whether you should use a larger model with lower bit-width or a smaller model with higher precision. The &quot;fixed memory budget&quot; is practically important as LLMs decoding is commonly bottlenecked by memory transfers. We found that optimal bit-width decreases as training compute increases.&lt;/p&gt;&lt;figure class=&quot;kg-card kg-image-card kg-card-hascaption&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2025/10/Screenshot-2025-10-30-at-19.31.32.png&quot; class=&quot;kg-image&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;2000&quot; height=&quot;1175&quot; srcset=&quot;https://alexdremov.me/content/images/size/w600/2025/10/Screenshot-2025-10-30-at-19.31.32.png 600w, https://alexdremov.me/content/images/size/w1000/2025/10/Screenshot-2025-10-30-at-19.31.32.png 1000w, https://alexdremov.me/content/images/size/w1600/2025/10/Screenshot-2025-10-30-at-19.31.32.png 1600w, https://alexdremov.me/content/images/2025/10/Screenshot-2025-10-30-at-19.31.32.png 2166w&quot; sizes=&quot;(min-width: 720px) 720px&quot;&gt;&lt;figcaption&gt;&lt;span style=&quot;white-space: pre-wrap;&quot;&gt;Optimal QAT bit width for different memory budgets and total training budgets. We use the loss corresponding to the optimal QAT fraction. For training FLOPs, we use the estimation \(C \sim 6ND\). The white area corresponds to \(D &amp;lt; N\), which is not practically important&lt;/span&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;h3 id=&quot;qat-accuracy-vs-full-precision&quot;&gt;&lt;strong&gt;QAT accuracy vs full-precision&lt;/strong&gt;&lt;/h3&gt;&lt;p&gt;One perspective to plan QAT from is from the idea &quot;when can we match full-precision performance?&quot; The loss scaling law can help with that! We can compare each specific QAT bit-width for different token counts to full-precision performance. As expected, larger models tolerate lower bit-widths better, which has implications for choosing which bit-width to train.&lt;/p&gt;&lt;figure class=&quot;kg-card kg-image-card kg-width-wide kg-card-hascaption&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2025/10/Screenshot-2025-10-30-at-19.49.21.png&quot; class=&quot;kg-image&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;2000&quot; height=&quot;707&quot; srcset=&quot;https://alexdremov.me/content/images/size/w600/2025/10/Screenshot-2025-10-30-at-19.49.21.png 600w, https://alexdremov.me/content/images/size/w1000/2025/10/Screenshot-2025-10-30-at-19.49.21.png 1000w, https://alexdremov.me/content/images/size/w1600/2025/10/Screenshot-2025-10-30-at-19.49.21.png 1600w, https://alexdremov.me/content/images/size/w2400/2025/10/Screenshot-2025-10-30-at-19.49.21.png 2400w&quot; sizes=&quot;(min-width: 1200px) 1200px&quot;&gt;&lt;figcaption&gt;&lt;span style=&quot;white-space: pre-wrap;&quot;&gt;Difference in perplexity between FP loss scaling law and QAT loss scaling law for two model sizes. For QAT, the loss corresponding to the optimal QAT fraction is used. Values below 0 correspond to QAT performing better than the FP model. It is clearly observed that the ability of QAT to match FP loss is greatly influenced by model size and token count. In particular, larger models are able to tolerate lower QAT precision for higher total token count budgets.&lt;/span&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;h2 id=&quot;cooldown-qat-fusion&quot;&gt;Cooldown &amp;amp; QAT Fusion&lt;/h2&gt;&lt;p&gt;Standard training performs learning rate cooldown on the full-precision model, then re-warms the learning rate for QAT. We speculate that those carefully adjusted weights during FP cooldown are almost discarded when quantization is initialized.&lt;/p&gt;&lt;p&gt;We propose &lt;strong&gt;cooldown &amp;amp; QAT fusion&lt;/strong&gt;: skip the FP cooldown phase and perform learning rate decay jointly with QAT instead.&lt;/p&gt;&lt;figure class=&quot;kg-card kg-image-card kg-width-wide kg-card-hascaption&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2025/10/Screenshot-2025-10-30-at-19.37.40.png&quot; class=&quot;kg-image&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;2000&quot; height=&quot;643&quot; srcset=&quot;https://alexdremov.me/content/images/size/w600/2025/10/Screenshot-2025-10-30-at-19.37.40.png 600w, https://alexdremov.me/content/images/size/w1000/2025/10/Screenshot-2025-10-30-at-19.37.40.png 1000w, https://alexdremov.me/content/images/size/w1600/2025/10/Screenshot-2025-10-30-at-19.37.40.png 1600w, https://alexdremov.me/content/images/2025/10/Screenshot-2025-10-30-at-19.37.40.png 2134w&quot; sizes=&quot;(min-width: 1200px) 1200px&quot;&gt;&lt;figcaption&gt;&lt;span style=&quot;white-space: pre-wrap;&quot;&gt;Comparison between two different QAT schemes. In both setups, the QAT fraction is 40%. Red-shaded areas indicate zones with lowered learning rate, which we expect to correspond to minor weight updates that get effectively ignored by QAT initialization. &lt;/span&gt;&lt;b&gt;&lt;strong style=&quot;white-space: pre-wrap;&quot;&gt;On the left,&lt;/strong&gt;&lt;/b&gt;&lt;span style=&quot;white-space: pre-wrap;&quot;&gt; classic QAT scheme visualization: QAT follows fully completed FP training that ends with 20% (of FP training length) learning rate decay. For QAT, the learning rate follows a cosine shape with 5% re-warmup phase. &lt;/span&gt;&lt;b&gt;&lt;strong style=&quot;white-space: pre-wrap;&quot;&gt;On the right, &lt;/strong&gt;&lt;/b&gt;&lt;span style=&quot;white-space: pre-wrap;&quot;&gt;the cooldown &amp;amp; QAT fusion scheme is displayed. QAT starts directly from the constant learning rate stage with small re-warmup, effectively resuming the FP learning rate scheduler as if QAT was not present at all. QAT ends with 20% cooldown (of total training length). As QAT follows the classic FP learning rate recipe with usual cooldown, we call this approach cooldown &amp;amp; QAT fusion&lt;/span&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;h3 id=&quot;results&quot;&gt;Results&lt;/h3&gt;&lt;p&gt;QAT fusion shows good results on 4-bit and 6-bit QAT across different model sizes. We also experimented with lower bits, but gains there were not as evident. We believe this is because for lower bits, the optimal QAT fraction is quite high, which makes the effect from QAT fusion less noticeable.&lt;/p&gt;&lt;figure class=&quot;kg-card kg-image-card kg-card-hascaption&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2025/10/Screenshot-2025-10-30-at-19.15.47.png&quot; class=&quot;kg-image&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;2000&quot; height=&quot;602&quot; srcset=&quot;https://alexdremov.me/content/images/size/w600/2025/10/Screenshot-2025-10-30-at-19.15.47.png 600w, https://alexdremov.me/content/images/size/w1000/2025/10/Screenshot-2025-10-30-at-19.15.47.png 1000w, https://alexdremov.me/content/images/size/w1600/2025/10/Screenshot-2025-10-30-at-19.15.47.png 1600w, https://alexdremov.me/content/images/size/w2400/2025/10/Screenshot-2025-10-30-at-19.15.47.png 2400w&quot; sizes=&quot;(min-width: 720px) 720px&quot;&gt;&lt;figcaption&gt;&lt;span style=&quot;white-space: pre-wrap;&quot;&gt;Accuracy comparison between the classic QAT scheme and the cooldown &amp;amp; QAT fusion training scheme. The loss difference is reported in “wasted tokens”—the difference in total token count between optimal QAT fraction loss points in the loss scaling law. Substantial improvements are noticeable across different model sizes and token counts.&lt;/span&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;The perplexity improvements translate to billions of tokens&apos; worth of compute saved.&lt;/p&gt;&lt;h2 id=&quot;implementation-guidelines&quot;&gt;Implementation Guidelines&lt;/h2&gt;&lt;p&gt;If you&apos;re planning QAT, consider the following steps:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Calculate tokens-per-parameter-byte&lt;/strong&gt; and use it to predict optimal QAT fraction instead of assuming 10%.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Budget compute appropriately&lt;/strong&gt; — optimal fractions can exceed 50% for large-scale training.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Implement cooldown &amp;amp; QAT fusion&lt;/strong&gt; — it&apos;s a simple scheduler change with noticeable compute savings.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Choose bit-width based on constraints&lt;/strong&gt; — use the scaling law to optimize for your memory and compute budget.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Pay extra attention to low-bit QAT&lt;/strong&gt; — suboptimal fractions are much more costly for 1-2 bit quantization than 6-bit.&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;conclusions&quot;&gt;Conclusions&lt;/h2&gt;&lt;p&gt;Efficient quantized model training requires careful compute allocation between full-precision and quantization-aware phases. The optimal QAT fraction isn&apos;t fixed—it increases with scale, from 10% to 50% or higher depending on tokens per parameter byte.&lt;/p&gt;&lt;p&gt;The loss scaling law enables us to:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Predict optimal QAT fractions in advance&lt;/li&gt;&lt;li&gt;Avoid significant compute waste (up to 50% for extreme cases)&lt;/li&gt;&lt;li&gt;Select optimal bit-widths under memory constraints&lt;/li&gt;&lt;li&gt;Achieve higher-quality quantized models for the same cost&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Combined with cooldown &amp;amp; QAT fusion, these techniques provide substantial efficiency gains for training quantized models at scale. Full details and additional experiments are available in the original paper:&lt;/p&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://arxiv.org/abs/2509.22935v1?ref=alexdremov.me&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;Compute-Optimal Quantization-Aware Training&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;Quantization-aware training (QAT) is a leading technique for improving the accuracy of quantized neural networks. Previous work has shown that decomposing training into a full-precision (FP) phase followed by a QAT phase yields superior accuracy compared to QAT alone. However, the optimal allocation of compute between the FP and QAT phases remains unclear. We conduct extensive experiments with various compute budgets, QAT bit widths, and model sizes from 86.0M to 2.2B to investigate how different QAT durations impact final performance. We demonstrate that, contrary to previous findings, the loss-optimal ratio of QAT to FP training increases with the total amount of compute. Moreover, the optimal fraction can be accurately predicted for a wide range of model sizes and quantization widths using the tokens-per-parameter-byte statistic. From experimental data, we derive a loss scaling law that predicts both optimal QAT ratios and final model performance across different QAT/FP compute allocation strategies and QAT bit widths. We use the scaling law to make further predictions, which we verify experimentally, including which QAT bit width is optimal under a given memory constraint and how QAT accuracy with different bit widths compares to full-precision model accuracy. Additionally, we propose a novel cooldown and QAT fusion approach that performs learning rate decay jointly with quantization-aware training, eliminating redundant full-precision model updates and achieving significant compute savings. These findings provide practical insights into efficient QAT planning and enable the training of higher-quality quantized models with the same compute budget.&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://alexdremov.me/content/images/icon/apple-touch-icon-5.png&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;arXiv.org&lt;/span&gt;&lt;span class=&quot;kg-bookmark-publisher&quot;&gt;Aleksandr Dremov&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/thumbnail/arxiv-logo-fb-1.png&quot; alt=&quot;&quot; onerror=&quot;this.style.display = &apos;none&apos;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;blockquote&gt;&lt;em&gt;Work conducted at Apple with David Grangier, Angelos Katharopoulos, and Awni Hannun. All information is from the public paper preprint.&lt;/em&gt;&lt;br&gt;&lt;br&gt;Apple and the Apple logo are trademarks of Apple Inc., registered in the U.S. and other countries and regions.&lt;/blockquote&gt;</content:encoded></item><item><title>Understanding Flash Attention: Writing the Algorithm from Scratch in Triton</title><link>https://alexdremov.me/understanding-flash-attention-writing-the-algorithm-from-scratch-in-triton/</link><guid isPermaLink="true">https://alexdremov.me/understanding-flash-attention-writing-the-algorithm-from-scratch-in-triton/</guid><description>Find out how Flash Attention works. Afterward, we&apos;ll refine our understanding by writing a GPU kernel of the algorithm in Triton.</description><pubDate>Sun, 12 Jan 2025 18:36:55 GMT</pubDate><content:encoded>&lt;p&gt;Flash Attention is a revolutionary technique that dramatically accelerates the attention mechanism in transformer-based models, delivering processing speeds many times faster than naive methods. By cleverly tiling data and minimizing memory transfers, it tackles the notorious GPU memory bottleneck that large language models often struggle with.&lt;/p&gt;&lt;p&gt;In this post, we’ll dive into how Flash Attention leverages efficient &lt;em&gt;I/O-awareness&lt;/em&gt; to reduce overhead, then take it a step further by crafting a &lt;strong&gt;block-sparse attention kernel&lt;/strong&gt; in Triton.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-purple&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💥&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;I will provide a simple explanation of how Flash Attention works. We will then implement the explained algorithm in Triton!&lt;/div&gt;&lt;/div&gt;&lt;h2 id=&quot;what-is-attention&quot;&gt;What is Attention?&lt;/h2&gt;&lt;p&gt;The attention mechanism (or scaled dot-product attention) is a core element of transformer models, which is a leading architecture for solving the problem of language modeling. All popular models, like GPT, LLaMA, and BERT, rely on attention.&lt;/p&gt;&lt;p&gt;The formula is pretty simple:&lt;/p&gt;&lt;p&gt;$$\text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V,\\Q, K, V\;—\; \text{query, key, value tensors}$$&lt;/p&gt;&lt;p&gt;The rest is history.&lt;/p&gt;&lt;p&gt;Even though the formula looks simple, its computation involves multiplications of large tensors and a lot of data movement. Considering that this is a core part of the transformer architecture, optimizing the algorithm greatly improves the performance of the model in general.&lt;/p&gt;&lt;p&gt;In the naive implementation, attention requires \(O(n^2)\) additional memory and \(O(n^2)\) compute time complexity, where \(n\) is the sequence length. &lt;strong&gt;That&apos;s a lot!&lt;/strong&gt;&lt;/p&gt;&lt;h2 id=&quot;flash-attention&quot;&gt;&lt;strong&gt;Flash Attention&lt;/strong&gt;&lt;/h2&gt;&lt;h3 id=&quot;core-idea&quot;&gt;&lt;strong&gt;Core Idea&lt;/strong&gt;&lt;/h3&gt;&lt;p&gt;The main idea of Flash attention can be summarized in a simple quote from &lt;a href=&quot;https://arxiv.org/pdf/2205.14135?ref=alexdremov.me&quot;&gt;the original paper&lt;/a&gt;:&lt;/p&gt;&lt;blockquote&gt;We argue that a missing principle is making attention algorithms IO-aware — accounting for reads and writes between levels of GPU memory.&lt;/blockquote&gt;&lt;p&gt;That is, modern GPUs have several types of memory:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;SRAM&lt;/strong&gt; — fast, on-chip, small&lt;/li&gt;&lt;li&gt;&lt;strong&gt;HBM — &lt;/strong&gt;slower than SRAM, large size. That&apos;s what we usually address as GPU memory.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Check out the memory hierarchy in the image below to see the differences in bandwidth and sizes of different memory types.&lt;/p&gt;&lt;figure class=&quot;kg-card kg-image-card kg-card-hascaption&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2025/01/Screenshot-2025-01-11-at-16.15.50.png&quot; class=&quot;kg-image&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;2000&quot; height=&quot;783&quot; srcset=&quot;https://alexdremov.me/content/images/size/w600/2025/01/Screenshot-2025-01-11-at-16.15.50.png 600w, https://alexdremov.me/content/images/size/w1000/2025/01/Screenshot-2025-01-11-at-16.15.50.png 1000w, https://alexdremov.me/content/images/size/w1600/2025/01/Screenshot-2025-01-11-at-16.15.50.png 1600w, https://alexdremov.me/content/images/2025/01/Screenshot-2025-01-11-at-16.15.50.png 2000w&quot; sizes=&quot;(min-width: 720px) 720px&quot;&gt;&lt;figcaption&gt;&lt;span style=&quot;white-space: pre-wrap;&quot;&gt;Image from FlashAttention: Fast and Memory-Efficient Exact Attention with IO-Awareness by Tri Dao et al.&lt;/span&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;To conduct computation, data must be transferred from HBM to SRAM, and this transfer is not overhead-free!&lt;/div&gt;&lt;/div&gt;&lt;p&gt;The Flash Attention algorithm proposes a method of &lt;strong&gt;computing attention in tiles&lt;/strong&gt;, without explicitly materializing the attention scores tensor:&lt;/p&gt;&lt;p&gt;$$\text{AttentionScores}(Q, K) = \text{Softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)$$&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-purple&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💥&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;&lt;b&gt;&lt;strong style=&quot;white-space: pre-wrap;&quot;&gt;Not materializing a matrix&lt;/strong&gt;&lt;/b&gt; means that at any given time, the matrix does not exist in its full shape in memory.&lt;/div&gt;&lt;/div&gt;&lt;p&gt;It&apos;s easy to see that this matrix requires \(O(n^2)\) of memory to store. For large sequence lengths, &lt;strong&gt;that&apos;s a lot of data!&lt;/strong&gt; So, if we manage to avoid explicitly materializing this matrix, we can save lots of memory.&lt;/p&gt;&lt;p&gt;However, this matrix is necessary for transformer training as it is a part of backpropagation and gradient calculation. The authors propose that it&apos;s better to recalculate this matrix during the backward pass (again without explicit materialization). Not only does this saves lots of memory, but it also provides huge speedups as we don&apos;t need to transfer this enormous matrix between different GPU memory types.&lt;/p&gt;&lt;p&gt;Overall, such an approach did not only speed up calculations by taking GPU I/O specifics into account, but also allowed processing huge sequence lengths as memory complexity drops to \(O(n)\).&lt;/p&gt;&lt;h3 id=&quot;tiled-attention-calculation&quot;&gt;Tiled Attention Calculation&lt;/h3&gt;&lt;p&gt;The last thing to understand is how to compute attention &lt;strong&gt;in tiles&lt;/strong&gt;. Basically, this means that we will calculate attention over the full sequence by processing incoming tokens in small portions.&lt;/p&gt;&lt;p&gt;Well, it&apos;s easy to calculate \(QK^T\) in tiles. Considering that attention dimension is not high, we can load full matrix rows and columns and conduct multiplication in tiles.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-red&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;😡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Yes, if we want to have an enormous attention dimension, Flash Attention will not work without algorithm modifications. &lt;br&gt;&lt;br&gt;As dimensions are usually quite small even for enormous models, this limitation is fair.&lt;/div&gt;&lt;/div&gt;&lt;figure class=&quot;kg-card kg-image-card kg-card-hascaption&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2025/01/Screenshot-2025-01-11-at-17.18.40.png&quot; class=&quot;kg-image&quot; alt=&quot;Tiled QK^T | Image by the author&quot; loading=&quot;lazy&quot; width=&quot;2000&quot; height=&quot;982&quot; srcset=&quot;https://alexdremov.me/content/images/size/w600/2025/01/Screenshot-2025-01-11-at-17.18.40.png 600w, https://alexdremov.me/content/images/size/w1000/2025/01/Screenshot-2025-01-11-at-17.18.40.png 1000w, https://alexdremov.me/content/images/size/w1600/2025/01/Screenshot-2025-01-11-at-17.18.40.png 1600w, https://alexdremov.me/content/images/2025/01/Screenshot-2025-01-11-at-17.18.40.png 2000w&quot; sizes=&quot;(min-width: 720px) 720px&quot;&gt;&lt;figcaption&gt;&lt;span style=&quot;white-space: pre-wrap;&quot;&gt;Tiled QK^T | Image by the author&lt;/span&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;So, we have \(QK^T\) calculated in SRAM. All that&apos;s left is to apply softmax, multiply by \(V\), and that&apos;s it!&lt;/p&gt;&lt;p&gt;$$\text{Softmax}(z_i) = \frac{e^{z_{i}}}{\sum_{j=1}^T e^{z_{j}}} \; \; \text{for}\; i = 1, 2,\ldots, T$$&lt;/p&gt;&lt;p&gt;That&apos;s where the trick is.&lt;/p&gt;&lt;p&gt;The problem is that the softmax denominator requires aggregation over the sequence length to normalize scores, and we do not have access to the whole length as we load data in tiles.&lt;/p&gt;&lt;p&gt;To address it, we can implement a concatenated softmax algorithm. Using it, we can calculate softmax &quot;in batch&quot; mode: by adjusting computed values with the new incoming data.&lt;/p&gt;&lt;p&gt;Taking the algorithm from the original article, we can define rules to compute the softmax over data concatenation. Having two vectors \(x^{(1)}\) and \(x^{(2)}\), we need to calculate the softmax denominator \(l(x)\) over those vectors&apos; concatenation: \(x = \left[x^{(1)}, x^{(2)}\right]\). If the vector&apos;s maximum is \(m(x)\), we can easily derive the softmax denominator of the concatenation:&lt;/p&gt;&lt;p&gt;$$m(x) = m\left(\left[x^{(1)}, x^{(2)}\right]\right) = m(m(x^{(1)}), m(x^{(2)})),$$&lt;/p&gt;&lt;p&gt;$$l(x) = l\left(\left[x^{(1)}, x^{(2)}\right]\right) = e^{m(x^{(1)}) - m(x)}l(x^{(1)}) + e^{m(x^{(2)}) - m(x)}l(x^{(2)}).$$&lt;/p&gt;&lt;p&gt;The last equivalence can be easily verified as \(l(x)=\sum_{j=1}^{T} e^{x_{j}}.\)&lt;/p&gt;&lt;p&gt;So, now we have what we want — we can calculate softmax per-tile and then, by doing re-normalization from the formula above, compute the global softmax. The last thing to do is to incorporate the tile of the \(V\) tensor and keep doing the same re-normalization (as matrix multiplication is a linear operation).&lt;/p&gt;&lt;p&gt;And all of this without loading the full sequence into memory or materializing \(QK^T\)!&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-purple&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💥&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Notice that we calculate \(\text{Softmax}\left(QK^T\right)\) in tiles only, without needing to have the whole matrix at any moment.&lt;/div&gt;&lt;/div&gt;&lt;p&gt;Also, in the actual algorithm for numerical stability, we will compute not \(\text{Softmax}(x)\) but \(\text{Softmax}(x - \max(x))\). We can do that as softmax is invariant to constant shifts.&lt;/p&gt;&lt;h2 id=&quot;triton-implementation&quot;&gt;Triton Implementation&lt;/h2&gt;&lt;p&gt;Now, we can easily implement the outlined algorithm in Triton, which is a tool that allows us to write efficient GPU kernels with the ease of Python.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;To learn more about Triton, check out their official guides.&lt;/div&gt;&lt;/div&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://triton-lang.org/main/getting-started/tutorials/index.html?ref=alexdremov.me&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;Tutorials — Triton documentation&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://static.ghost.org/v5.0.0/images/link-icon.svg&quot; alt=&quot;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/thumbnail/triton-logo.png&quot; alt=&quot;&quot; onerror=&quot;this.style.display = &apos;none&apos;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;!--kg-card-begin: html--&gt;
&lt;section class=&quot;custom-replace&quot; data-replace=&quot;.subscription-article&quot;&gt;
    &lt;a class=&quot;gh-portal-triggerbtn-container&quot;&gt;Subscribe and don&apos;t miss posts!&lt;/a&gt;
&lt;/section&gt;
&lt;!--kg-card-end: html--&gt;
&lt;h3 id=&quot;outlining-the-algorithm&quot;&gt;Outlining the Algorithm&lt;/h3&gt;&lt;p&gt;The first step is to decide how we will assign jobs and what data each job will load. By the algorithm of tiled softmax, each job must have access to \(K, V\) over the whole sequence length. So, each job will iterate over \(K, V\) in tiles. We don&apos;t have any algorithmic restriction on the number of \(Q\) tiles processed. Therefore, each job will load just one \(Q\) tile and work with it only — this way we will maximize job parallelism.&lt;/p&gt;&lt;figure class=&quot;kg-card kg-image-card kg-card-hascaption&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2025/01/Screenshot-2025-01-11-at-18.35.58.png&quot; class=&quot;kg-image&quot; alt=&quot;Jobs data management | Image by the author&quot; loading=&quot;lazy&quot; width=&quot;2000&quot; height=&quot;1034&quot; srcset=&quot;https://alexdremov.me/content/images/size/w600/2025/01/Screenshot-2025-01-11-at-18.35.58.png 600w, https://alexdremov.me/content/images/size/w1000/2025/01/Screenshot-2025-01-11-at-18.35.58.png 1000w, https://alexdremov.me/content/images/size/w1600/2025/01/Screenshot-2025-01-11-at-18.35.58.png 1600w, https://alexdremov.me/content/images/2025/01/Screenshot-2025-01-11-at-18.35.58.png 2000w&quot; sizes=&quot;(min-width: 720px) 720px&quot;&gt;&lt;figcaption&gt;&lt;span style=&quot;white-space: pre-wrap;&quot;&gt;Kernel jobs data management | Image by the author&lt;/span&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;In summary, each job will load a single \(Q\) tile, iterate over all tiles in \(K\) and \(V\), and store one tile of result corresponding to the \(Q\) tile.&lt;/p&gt;&lt;h3 id=&quot;the-kernel&quot;&gt;The Kernel&lt;/h3&gt;&lt;p&gt;What&apos;s left is to write the actual code. Let&apos;s focus on the core part first, and only then we&apos;ll add Triton-specific boilerplates.&lt;/p&gt;&lt;p&gt;Below is a Triton pseudocode with every line explained.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;def self_attn_fwd(...):
    # loading sample len
    seq_len = ...

    # running qk^T max (initialized by -inf)
    m_i = tl.zeros([TILE_Q_SIZE], dtype=tl.float32) - float(&quot;inf&quot;)

    # current softmax denominator
    l_i = tl.zeros([TILE_Q_SIZE], dtype=tl.float32)

    # result tile 
    # we will accumulate here (softmax numerator) @ V
    # then, we will divide it by softmax denominator in the very end
    acc = tl.zeros([TILE_Q_SIZE, HEAD_DIM], dtype=tl.float32)

    # notice: we accumulate all values above
    # in fp32 for higher precision

    # account for variable length of samples in batch
    q_tile_indices = q_token_idx + tl.arange(0, TILE_Q_SIZE)
    q_lens_mask = (
        q_tile_indices[:, None] &amp;lt; seq_len
    )

    # loading q tile into SRAM, shape (TILE_Q_SIZE, HEAD_DIM)
    q_tile = ... 

    # softmax scale, multiplying by log_2(e) 
    # to use faster exp2(...) instead of exp(...)
    softmax_scale: tl.constexpr = tl.cast(SM_SCALE * log_2(e), q_tile.dtype)

    # indices of tokens inside kv tile 
    tile_k_arange = tl.arange(0, TILE_K_SIZE)

    # iterate over all tiles in k, v
    for kv_tile_idx in tl.range(
        0, tl.cdiv(seq_len, TILE_K_SIZE), num_stages=PIPELINING
    ):
        # index of the first token in the kv tile
        kv_token_idx = kv_tile_idx * TILE_K_SIZE

        kt_tile = ... # load into SRAM K^T tile no. kv_tile_idx
        v_tile = ... # load into SRAM V tile no. kv_tile_idx

        # compute tile of QK^T
        qk = tl.dot(
            q_tile * softmax_scale,
            kt_tile,
            input_precision=INPUT_PRECISION,
            out_dtype=tl.float32
        )

        # masking out kv tokens after the sequence length
        kv_indices = kv_token_idx + tile_k_arange
        mask = q_lens_mask &amp;amp; (
            kv_indices[None, :] &amp;lt; seq_len
        )

        # set masked out values to -inf
        # for softmax to ignore them
        qk = tl.where(mask, qk, tl.cast(-float(&quot;inf&quot;), qk.dtype))

        # calculating new maximum over seq len
        # m(x) = m(m(x1), m(x2))
        m_ij = tl.maximum(m_i, tl.max(qk, 1))

        # e^(x2 - m(x))
        p = tl.math.exp2(qk - m_ij[:, None])

        # current tile softmax denominator
        l_ij = tl.sum(p, 1)

        # from softmax formula: e^(m(x1) - m(x))
        alpha = tl.math.exp2(m_i - m_ij)

        # updating denominator using the formula
        # l(x) = e^(m(x1) - m(x)) * l(x1) + e^(0)l(x2)
        # notice: e^(0) as we subtract m(x) from x2 above
        l_i = l_i * alpha + l_ij
        
        # update previous acc to address maximum change
        # as e^(xi - m(x1)) * alpha = e^(xi - m(x))
        acc = acc * alpha[:, None]

        # multiply p by v and adding to acc
        acc += tl.dot(
            p.to(v_tile.dtype),
            v_tile,
            input_precision=INPUT_PRECISION,
            out_dtype=tl.float32,
        )

        # storing new maximum
        m_i = m_ij

    # finally incorporate softmax denominator
    acc = acc / l_i[:, None]

    # set fully masked token values to 0 to avoid garbage values
    # in the result
    acc = tl.where(q_lens_mask, acc, 0.0)

    # save the result
    tl.save(acc, ...) &lt;/code&gt;&lt;/pre&gt;&lt;p&gt;See? Easy!&lt;/p&gt;&lt;p&gt;What&apos;s important is that you can see how simple it is to write such a thing as soon as we understand the idea of tiled softmax. Apart from that, there&apos;s nothing complicated from the algorithm perspective.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-purple&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💥&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;This kernel can be made even faster by implementing triton optimizations. However, this is out of the scope of this article.&lt;/div&gt;&lt;/div&gt;&lt;p&gt;This pseudocode is pretty close to the actual code. You may find it in my GitHub by following the link. All that I added is just data management and PyTorch wrappers.&lt;/p&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://github.com/alexdremov/kernels/blob/main/src/self_attention/kernel.py?ref=alexdremov.me&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;kernels/src/self_attention/kernel.py at main · alexdremov/kernels&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;Collection of useful kernels. Contribute to alexdremov/kernels development by creating an account on GitHub.&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://alexdremov.me/content/images/icon/pinned-octocat-093da3e6fa40-2.svg&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;GitHub&lt;/span&gt;&lt;span class=&quot;kg-bookmark-publisher&quot;&gt;alexdremov&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://opengraph.githubassets.com/fbb2c3e5de3a0a6dbb858f209284255e632e255c911b7421f730fc1a653a3b9d/alexdremov/kernels&quot; alt=&quot;&quot; onerror=&quot;this.style.display = &apos;none&apos;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;❗&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Don&apos;t hesitate to ask if something isn&apos;t clear. I&apos;m here in the comments 😁.&lt;/div&gt;&lt;/div&gt;&lt;p&gt;The code above &lt;a href=&quot;https://github.com/alexdremov/kernels/blob/main/tests/test_self_attention.py?ref=alexdremov.me&quot;&gt;was extensively tested&lt;/a&gt; to match PyTorch&apos;s &lt;code&gt;scaled_dot_product_attention&lt;/code&gt;. You can also check out the tests to see how to use the written kernel.&lt;/p&gt;&lt;h3 id=&quot;benchmarking&quot;&gt;Benchmarking&lt;/h3&gt;&lt;p&gt;While we wrote the kernel in Triton to improve the algorithm understanding, it&apos;s interesting to compare the performance with a naive implementation and PyTorch&apos;s &lt;code&gt;scaled_dot_product_attention&lt;/code&gt;.&lt;/p&gt;&lt;figure class=&quot;kg-card kg-image-card kg-width-wide kg-card-hascaption&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2025/01/plot.png&quot; class=&quot;kg-image&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;1100&quot; height=&quot;400&quot; srcset=&quot;https://alexdremov.me/content/images/size/w600/2025/01/plot.png 600w, https://alexdremov.me/content/images/size/w1000/2025/01/plot.png 1000w, https://alexdremov.me/content/images/2025/01/plot.png 1100w&quot;&gt;&lt;figcaption&gt;&lt;span style=&quot;white-space: pre-wrap;&quot;&gt;Benchmarking implementations for different sequence lengths | Image by the author&lt;/span&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;As expected, the Flash Attention algorithm completely outperforms the naive implementation performance-wise. Also, I&apos;ve marked with a dashed line the range of lengths for which the naive implementation causes a CUDA out-of-memory error.&lt;/p&gt;&lt;p&gt;We see that our Triton implementation is slightly worse than PyTorch SDPA. But the difference is not too large Considering the fact that PyTorch SDPA is a well-optimized CUDA kernel, that&apos;s a nice result.&lt;/p&gt;&lt;p&gt;Benchmarking code is also available in the repository.&lt;/p&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://github.com/alexdremov/kernels/blob/main/benchmark/benchmark_self_attention.py?ref=alexdremov.me&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;kernels/benchmark/benchmark_self_attention.py at main · alexdremov/kernels&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;Collection of useful kernels. Contribute to alexdremov/kernels development by creating an account on GitHub.&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://alexdremov.me/content/images/icon/pinned-octocat-093da3e6fa40-3.svg&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;GitHub&lt;/span&gt;&lt;span class=&quot;kg-bookmark-publisher&quot;&gt;alexdremov&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/thumbnail/kernels&quot; alt=&quot;&quot; onerror=&quot;this.style.display = &apos;none&apos;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;h2 id=&quot;conclusions&quot;&gt;Conclusions&lt;/h2&gt;&lt;p&gt;In the post, I covered the motivation of the Flash Attention algorithm as well as its algorithm details. Finally, we were able to implement it from scratch in Triton, reproducing the speedups from the paper.&lt;/p&gt;&lt;p&gt;I hope this post improved your understanding of Flash Attention. Feel free to leave a comment below if you have any questions.&lt;/p&gt;&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://arxiv.org/abs/2205.14135?ref=alexdremov.me&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;FlashAttention: Fast and Memory-Efficient Exact Attention with IO-Awareness&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;Transformers are slow and memory-hungry on long sequences, since the time and memory complexity of self-attention are quadratic in sequence length. Approximate attention methods have attempted to address this problem by trading off model quality to reduce the compute complexity, but often do not achieve wall-clock speedup. We argue that a missing principle is making attention algorithms IO-aware -- accounting for reads and writes between levels of GPU memory. We propose FlashAttention, an IO-aware exact attention algorithm that uses tiling to reduce the number of memory reads/writes between GPU high bandwidth memory (HBM) and GPU on-chip SRAM. We analyze the IO complexity of FlashAttention, showing that it requires fewer HBM accesses than standard attention, and is optimal for a range of SRAM sizes. We also extend FlashAttention to block-sparse attention, yielding an approximate attention algorithm that is faster than any existing approximate attention method. FlashAttention trains Transformers faster than existing baselines: 15% end-to-end wall-clock speedup on BERT-large (seq. length 512) compared to the MLPerf 1.1 training speed record, 3$\times$ speedup on GPT-2 (seq. length 1K), and 2.4$\times$ speedup on long-range arena (seq. length 1K-4K). FlashAttention and block-sparse FlashAttention enable longer context in Transformers, yielding higher quality models (0.7 better perplexity on GPT-2 and 6.4 points of lift on long-document classification) and entirely new capabilities: the first Transformers to achieve better-than-chance performance on the Path-X challenge (seq. length 16K, 61.4% accuracy) and Path-256 (seq. length 64K, 63.1% accuracy).&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://alexdremov.me/content/images/icon/apple-touch-icon.png&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;arXiv.org&lt;/span&gt;&lt;span class=&quot;kg-bookmark-publisher&quot;&gt;Tri Dao&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/thumbnail/arxiv-logo-fb.png&quot; alt=&quot;&quot; onerror=&quot;this.style.display = &apos;none&apos;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://triton-lang.org/main/getting-started/tutorials/index.html?ref=alexdremov.me&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;Tutorials — Triton documentation&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://static.ghost.org/v5.0.0/images/link-icon.svg&quot; alt=&quot;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/thumbnail/triton-logo-1.png&quot; alt=&quot;&quot; onerror=&quot;this.style.display = &apos;none&apos;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://github.com/alexdremov/kernels/tree/main?ref=alexdremov.me&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;GitHub - alexdremov/kernels: Collection of useful kernels&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;Collection of useful kernels. Contribute to alexdremov/kernels development by creating an account on GitHub.&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://alexdremov.me/content/images/icon/pinned-octocat-093da3e6fa40-4.svg&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;GitHub&lt;/span&gt;&lt;span class=&quot;kg-bookmark-publisher&quot;&gt;alexdremov&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/thumbnail/kernels-1&quot; alt=&quot;&quot; onerror=&quot;this.style.display = &apos;none&apos;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;</content:encoded></item><item><title>Speed Up PyTorch With Custom Kernels. But It Gets Progressively Darker</title><link>https://alexdremov.me/speed-up-pytorch-with-custom-kernels-but-it-gets-progressively-darker/</link><guid isPermaLink="true">https://alexdremov.me/speed-up-pytorch-with-custom-kernels-but-it-gets-progressively-darker/</guid><description>It&apos;s all about making your models run faster. In each step, we’ll implement a fast softmax function, but things are about to get dark by the end</description><pubDate>Sat, 04 Jan 2025 00:34:24 GMT</pubDate><content:encoded>&lt;p&gt;PyTorch offers remarkable flexibility, allowing you to code complex GPU-accelerated operations in a matter of seconds. However, this convenience comes at a cost. PyTorch executes your code sequentially, resulting in suboptimal performance. This translates into slower model training, which impacts the iteration cycle of your experiments, the robustness of your team, the financial implications, and so on.&lt;/p&gt;&lt;p&gt;In this post, I’ll explore three strategies for accelerating your PyTorch operations. Each method uses &lt;strong&gt;&lt;code&gt;softmax&lt;/code&gt;&lt;/strong&gt; as our “Hello World” demonstration, but you can swap it with any function you like, and the discussed methods would still apply.&lt;/p&gt;&lt;p&gt;We’ll begin with &lt;strong&gt;&lt;code&gt;torch.compile&lt;/code&gt;&lt;/strong&gt;, move on to writing a custom Triton kernel, and finally dive into designing a CUDA kernel.&lt;/p&gt;&lt;p&gt;So, this post may get complicated, but bear with me.&lt;/p&gt;&lt;h2 id=&quot;torchcompile-%E2%80%94-a-quick-way-to-boost-performance&quot;&gt;&lt;code&gt;torch.compile&lt;/code&gt;&amp;nbsp;— A Quick Way to Boost Performance&lt;/h2&gt;&lt;figure class=&quot;kg-card kg-image-card&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2025/01/Phase_1-2.jpeg&quot; class=&quot;kg-image&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;254&quot; height=&quot;254&quot;&gt;&lt;/figure&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-purple&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💥&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;&lt;i&gt;&lt;em class=&quot;italic&quot; style=&quot;white-space: pre-wrap;&quot;&gt;“Wait, you just turn on a single function call and it speeds up your code? That’s it? Sounds too good to be true.”&lt;/em&gt;&lt;/i&gt;&lt;br&gt;&lt;br&gt;— Yes.&lt;/div&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;torch.compile&lt;/code&gt;&amp;nbsp;is a relatively new API in PyTorch that uses runtime graph capture and kernel fusion under the hood . With one decorator, you can often see speed improvements without significant changes to your code.&lt;/p&gt;&lt;p&gt;Speaking simply, for example, we can speed up calculations by merging operations into one GPU function, which removes overheads of separate GPU calls. Or even better, optimize a chain of operations by replacing them with one equivalent! &lt;/p&gt;&lt;p&gt;Such optimizations are not possible in the regular PyTorch execution mode (eager) as it is eager and executes operations just as they are called in the code.&lt;/p&gt;&lt;h3 id=&quot;softmax-implementation-with-torchcompile&quot;&gt;Softmax Implementation with&amp;nbsp;&lt;code&gt;torch.compile&lt;/code&gt;&lt;/h3&gt;&lt;p&gt;Below is a&amp;nbsp;simple example showing how to implement and compile a softmax function using&amp;nbsp;&lt;code&gt;torch.compile&lt;/code&gt;. Replace it in your model’s forward pass, and your code (hopefully) runs faster.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;import torch

# Our softmax function in PyTorch land
def softmax_pytorch(x):
    # Avoid numerical instability by subtracting max
    x_max = torch.max(x, dim=-1, keepdim=True).values
    x_exp = torch.exp(x - x_max)
    return x_exp / torch.sum(x_exp, dim=-1, keepdim=True)

# Let&apos;s compile it with torch.compile
@torch.compile
def compiled_softmax(x):
    return softmax_pytorch(x)

if __name__ == &quot;__main__&quot;:
    # Example usage:
    input_tensor = torch.randn((2, 4), device=&quot;cuda&quot;)
    output = compiled_softmax(input_tensor)
    print(&quot;Input:&quot;, input_tensor)
    print(&quot;Compiled Softmax Output:&quot;, output)
&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;❗&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Note that you&apos;ll have bigger speedups if you compile the whole model pass and not just one operation&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Pros&lt;/strong&gt;:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;One line to enable the compiler.&lt;/li&gt;&lt;li&gt;No black magic rituals needed (except for the dynamic shapes maybe).&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;Cons&lt;/strong&gt;:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;The first pass can be slower while it compiles; afterwards, it picks up speed.&lt;/li&gt;&lt;li&gt;Doesn’t always produce dramatic speed-ups for&amp;nbsp;&lt;em&gt;all&lt;/em&gt;&amp;nbsp;models and can occasionally break if your code is too creative.&lt;/li&gt;&lt;li&gt;Still has problems with handling dynamic shapes.&lt;/li&gt;&lt;/ul&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-red&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;😡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Dynamic shapes compilation mode is needed when input shapes change and we don&apos;t want to recompile the code for each specific size.&lt;br&gt;&lt;br&gt;The ways to debug this is a whole new article.&lt;/div&gt;&lt;/div&gt;&lt;h2 id=&quot;triton-code-%E2%80%94-write-gpu-kernels-with-python-breeze&quot;&gt;Triton Code — Write GPU Kernels With Python Breeze&lt;/h2&gt;&lt;figure class=&quot;kg-card kg-image-card&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2025/01/images.jpeg&quot; class=&quot;kg-image&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;225&quot; height=&quot;224&quot;&gt;&lt;/figure&gt;&lt;h3 id=&quot;why-use-triton&quot;&gt;Why Use Triton?&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Triton&lt;/strong&gt;&amp;nbsp;is a language that compiles to efficient GPU kernels while letting you write Pythonic code. It’s used under the hood of PyTorch’s dynamo/inductor stack, but you can also write your own custom ops! For many matrix/tensor operations — like softmax — you can get huge speed-ups. Because&amp;nbsp;&lt;strong&gt;why&lt;/strong&gt;&amp;nbsp;wait for official PyTorch kernels when you can write your own?&lt;/p&gt;&lt;h3 id=&quot;softmax-in-triton&quot;&gt;Softmax in Triton&lt;/h3&gt;&lt;p&gt;Here’s a minimal snippet that shows how we might do a naive softmax forward in Triton. I&apos;ll keep it short and sweet for demonstration. In a real project, you’d likely do more advanced tiling and block management.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-purple&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💥&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;This may look complicated, but you just need to get familiar with Triton, and it will start making sense.&lt;br&gt;&lt;br&gt;Check out &lt;a href=&quot;https://triton-lang.org/main/index.html?ref=alexdremov.me&quot;&gt;their guides&lt;/a&gt;!&lt;/div&gt;&lt;/div&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;import torch
import triton
import triton.language as tl


@triton.autotune(
    configs=[
        triton.Config(
            kwargs=dict(
                BLOCK_SIZE_ROWS=BLOCK_SIZE_ROWS,
                num_stages=num_stages,
            ),
            num_warps=num_warps,
            num_stages=num_stages,
        )
        for BLOCK_SIZE_ROWS in (16, 32, 64, 128)
        for num_stages in (2, 3, 4)
        for num_warps in (2, 4, 8)
    ],
    key=[&apos;N_COLS&apos;],
)
@triton.heuristics(
    values=dict(
        BLOCK_SIZE_COLS=lambda args: triton.next_power_of_2(args[&apos;N_COLS&apos;])
    )
)
@triton.jit
def softmax_kernel(
    input_ptr: tl.tensor,
    output_ptr: tl.tensor,
    input_row_stride: int,
    output_row_stride: int,
    n_rows: int,
    N_COLS: tl.constexpr,
    BLOCK_SIZE_ROWS: tl.constexpr,
    BLOCK_SIZE_COLS: tl.constexpr,
    num_stages: tl.constexpr
):
    input_ptr = tl.make_block_ptr(
        base=input_ptr,
        shape=(n_rows, N_COLS),
        strides=(input_row_stride, 1),
        offsets=(0, 0),
        block_shape=(BLOCK_SIZE_ROWS, BLOCK_SIZE_COLS),
        order=(1, 0),
    )

    output_ptr = tl.make_block_ptr(
        base=output_ptr,
        shape=(n_rows, N_COLS),
        strides=(output_row_stride, 1),
        offsets=(0, 0),
        block_shape=(BLOCK_SIZE_ROWS, BLOCK_SIZE_COLS),
        order=(1, 0),
    )

    cols_mask = tl.arange(0, BLOCK_SIZE_COLS) &amp;lt; N_COLS

    row_idx = tl.program_id(0) * BLOCK_SIZE_ROWS
    in_tile_ptr = tl.advance(input_ptr, (row_idx, 0))
    row = tl.load(pointer=in_tile_ptr, boundary_check=(0, 1))

    # Subtract maximum for numerical stability
    row_minus_max = row - tl.max(row, axis=1, keep_dims=True)
    row_minus_max = tl.where(cols_mask, row_minus_max, -float(&apos;inf&apos;))

    numerator = tl.exp(row_minus_max)
    denominator = tl.sum(numerator, axis=1, keep_dims=True)
    softmax_output = numerator / denominator

    out_tile_ptr = tl.advance(output_ptr, (row_idx, 0))
    tl.store(out_tile_ptr, softmax_output, boundary_check=(0, 1))


def softmax(x: torch.Tensor):
    x_orig_shape = x.shape
    x = x.view(-1, x_orig_shape[-1])
    n_rows, n_cols = x.shape

    y = torch.empty_like(x, memory_format=torch.contiguous_format)

    grid = lambda args: (
        triton.cdiv(n_rows, args[&apos;BLOCK_SIZE_ROWS&apos;]),
        1,
        1
    )

    softmax_kernel[grid](
        input_ptr=x,
        output_ptr=y,
        input_row_stride=x.stride(0),
        output_row_stride=y.stride(0),
        n_rows=n_rows,
        N_COLS=n_cols,
    )
    return y.view(*x_orig_shape)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Indeed, it looks complicated. But the core of the algorithm is summarized in a few lines.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;    row_minus_max = row - tl.max(row, axis=1, keep_dims=True)
    row_minus_max = tl.where(cols_mask, row_minus_max, -float(&apos;inf&apos;))

    numerator = tl.exp(row_minus_max)
    denominator = tl.sum(numerator, axis=1, keep_dims=True)
    
    softmax_output = numerator / denominator&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Everything else is just data management and side-hustle.&lt;/p&gt;&lt;p&gt;If we&apos;ll conduct benchmarking for different data length, we&apos;ll see that we match &lt;code&gt;torch.nn.functional.softmax&lt;/code&gt; performance &lt;strong&gt;(which is highly optimized kernel!)&lt;/strong&gt; and dramatically outperform naive torch implementation. &lt;/p&gt;&lt;figure class=&quot;kg-card kg-image-card&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2025/01/softmax-performance.png&quot; class=&quot;kg-image&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;640&quot; height=&quot;480&quot; srcset=&quot;https://alexdremov.me/content/images/size/w600/2025/01/softmax-performance.png 600w, https://alexdremov.me/content/images/2025/01/softmax-performance.png 640w&quot;&gt;&lt;/figure&gt;&lt;p&gt;You may find the full code for the kernel and benchmark in the following github file.&lt;/p&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://github.com/alexdremov/kernels/blob/main/src/softmax/kernel.py?ref=alexdremov.me&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;kernels/src/softmax/kernel.py at main · alexdremov/kernels&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;Collection of useful kernels. Contribute to alexdremov/kernels development by creating an account on GitHub.&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://alexdremov.me/content/images/icon/pinned-octocat-093da3e6fa40.svg&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;GitHub&lt;/span&gt;&lt;span class=&quot;kg-bookmark-publisher&quot;&gt;alexdremov&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://opengraph.githubassets.com/c1d5fd6dabdbfb9b86b5e7053edb027477fb87351bd31f21a099a829b761e09e/alexdremov/kernels&quot; alt=&quot;&quot; onerror=&quot;this.style.display = &apos;none&apos;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;p&gt;&lt;strong&gt;Pros&lt;/strong&gt;:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Potentially huge speed-ups by fusing ops and optimizing memory access patterns.&lt;/li&gt;&lt;li&gt;More control than&amp;nbsp;&lt;code&gt;torch.compile&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;Easy to write efficient code (we matched torch implementation!)&lt;/li&gt;&lt;li&gt;Easy to write inefficient code (if you don&apos;t know what you&apos;re doing).&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;Cons&lt;/strong&gt;:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;You’re now the&amp;nbsp;&lt;em&gt;kernel developer&lt;/em&gt;, which means debugging if something goes sideways. Which is tough. Really.&lt;/li&gt;&lt;li&gt;If you go further with custom backward passes, you might need a second coffee… or more. That&apos;s because torch cannot use autograd for triton. So you will need to define backward yourself.&lt;/li&gt;&lt;li&gt;Subscribe so you don&apos;t miss a post about usage of triton kernels + autograd + torch.compile tandem.&lt;/li&gt;&lt;/ul&gt;
&lt;!--kg-card-begin: html--&gt;
&lt;section class=&quot;custom-replace&quot; data-replace=&quot;.subscription-article&quot;&gt;
    &lt;a class=&quot;gh-portal-triggerbtn-container&quot;&gt;Subscribe and don&apos;t miss posts!&lt;/a&gt;
&lt;/section&gt;
&lt;!--kg-card-end: html--&gt;
&lt;h2 id=&quot;pure-cuda-aka-going-hardcore&quot;&gt;Pure CUDA (a.k.a. Going Hardcore)&lt;/h2&gt;&lt;figure class=&quot;kg-card kg-image-card&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2025/01/Uncanny_Phase_3-2.jpeg&quot; class=&quot;kg-image&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;254&quot; height=&quot;259&quot;&gt;&lt;/figure&gt;&lt;p&gt;Sometimes even Triton won’t cut it, or you just enjoy living on the edge. In that case, you can write a custom CUDA kernel in C++, compile it, and tie it into PyTorch via a custom extension. Projects like &lt;a href=&quot;https://github.com/fattorib/CudaSoftmax?ref=alexdremov.me&quot;&gt;[this fused CUDA softmax reference]&lt;/a&gt;&amp;nbsp;show how people build specialized kernels for maximum speed.&lt;/p&gt;&lt;h3 id=&quot;softmax-in-custom-cuda&quot;&gt;Softmax in Custom CUDA&lt;/h3&gt;&lt;p&gt;You’ll typically have a&amp;nbsp;&lt;code&gt;setup.py&lt;/code&gt;&amp;nbsp;that compiles a&amp;nbsp;&lt;code&gt;.cu&lt;/code&gt;&amp;nbsp;or&amp;nbsp;&lt;code&gt;.cpp&lt;/code&gt;&amp;nbsp;file and exposes a Python function as an extension. &lt;/p&gt;&lt;p&gt;Checkout &lt;a href=&quot;https://github.com/fattorib/CudaSoftmax?ref=alexdremov.me&quot;&gt;CudaSoftmax&lt;/a&gt; for self-explanatory example.&lt;/p&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://github.com/fattorib/CudaSoftmax?ref=alexdremov.me&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;GitHub - fattorib/CudaSoftmax: Softmax CUDA kernel :)&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;Softmax CUDA kernel :). Contribute to fattorib/CudaSoftmax development by creating an account on GitHub.&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://alexdremov.me/content/images/icon/pinned-octocat-093da3e6fa40-1.svg&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;GitHub&lt;/span&gt;&lt;span class=&quot;kg-bookmark-publisher&quot;&gt;fattorib&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/thumbnail/CudaSoftmax&quot; alt=&quot;&quot; onerror=&quot;this.style.display = &apos;none&apos;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;p&gt;I will not provide the code for this method in this post, so this fact speaks for itself. This approach is quite complicated, requires good justification, and usually the last thing you should try doing.&lt;/p&gt;&lt;p&gt;It&apos;s very easy to write inefficient, buggy, unsafe code.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Pros&lt;/strong&gt;:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Maximum control. “If you want something done right, do it yourself.”&lt;/li&gt;&lt;li&gt;Potential for the fastest possible kernel if well-optimized.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;Cons&lt;/strong&gt;:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Requires deep CUDA understanding.&lt;/li&gt;&lt;li&gt;Memory management, block sizes, shared memory—those are hard!&lt;/li&gt;&lt;li&gt;Maintenance overhead can be &lt;strong&gt;extremely&lt;/strong&gt; high.&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;&lt;p&gt;When it comes to speeding up PyTorch operations, you can choose from progressively more intricate methods:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;strong&gt;&lt;code&gt;torch.compile&lt;/code&gt;&lt;/strong&gt;: Minimal code changes needed.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Triton Kernel&lt;/strong&gt;: More control over kernel behaviour, still quite easy coding.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Pure CUDA&lt;/strong&gt;: Maximum optimisation potential, but &lt;strong&gt;a lot higher&lt;/strong&gt; complexity.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;If you’re looking for the simplest improvement, start with&amp;nbsp;&lt;code&gt;torch.compile&lt;/code&gt;. If that’s insufficient, explore Triton. For advanced users, writing a custom CUDA kernel can yield further gains, though it demands deep GPU programming skills.&lt;/p&gt;&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;&lt;ol&gt;&lt;li&gt;&lt;a href=&quot;https://pytorch.org/tutorials/recipes/compiling_optimizer.html?ref=alexdremov.me&quot; rel=&quot;nofollow noopener&quot;&gt;Compiling the optimizer with torch.compile (PyTorch Docs)&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://discuss.pytorch.org/t/how-should-i-use-torch-compile-properly/144598?ref=alexdremov.me&quot; rel=&quot;nofollow noopener&quot;&gt;How should I use torch.compile properly? (PyTorch discussion)&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://pytorch.org/tutorials/recipes/torch_compile_user_defined_triton_kernel_tutorial.html?ref=alexdremov.me&quot; rel=&quot;nofollow noopener&quot;&gt;Using User-Defined Triton Kernels with torch.compile (PyTorch Docs)&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://discuss.pytorch.org/t/torch-compile-with-custom-triton-kernel/192876?ref=alexdremov.me&quot; rel=&quot;nofollow noopener&quot;&gt;Torch.compile with custom Triton kernel (PyTorch discussion)&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/fattorib/CudaSoftmax?ref=alexdremov.me&quot; rel=&quot;nofollow noopener&quot;&gt;GitHub: fattorib/CudaSoftmax&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Choose the path that fits your project’s needs and your comfort level. Good luck optimizing!&lt;/p&gt;</content:encoded></item><item><title>Simple Ways to Speed Up Your PyTorch Model Training</title><link>https://alexdremov.me/simple-ways-to-speedup-your-pytorch-model-training/</link><guid isPermaLink="true">https://alexdremov.me/simple-ways-to-speedup-your-pytorch-model-training/</guid><description>If all machine learning engineers want one thing, it&apos;s faster model training — maybe after good test metrics.</description><pubDate>Tue, 28 May 2024 20:16:11 GMT</pubDate><content:encoded>&lt;p&gt;Does this topic even need an introduction?&lt;/p&gt;&lt;p&gt;Speeding&amp;nbsp;up machine learning model training&amp;nbsp;is one thing that all machine learning engineers want.&amp;nbsp;Faster training equals faster experiments equals faster iterations for your product. Also, it means that one model training will require fewer resources. So, straight to the point&lt;/p&gt;&lt;h2 id=&quot;containerization&quot;&gt;Containerization&lt;/h2&gt;&lt;p&gt;Yes, this will not speed up your training on its own. But this targets another&amp;nbsp;important&amp;nbsp;aspect — reproducibility. Sometimes virtualenv with fixed library versions is enough, but I encourage you to take one step further and build an all-in-one docker container for your model training.&amp;nbsp;&lt;/p&gt;&lt;p&gt;This&amp;nbsp;ensures&amp;nbsp;that the&amp;nbsp;environment is&amp;nbsp;fully&amp;nbsp;consistent during debugging, profiling, and final training. The last thing you want is to optimize a part of code that is no longer a bottleneck due to python12 speed up, for example. Or even a bug that is not reproducible on different CUDA versions.&lt;/p&gt;&lt;p&gt;As a starting point, you can use pre-built images from NVIDIA. They already have CUDA, PyTorch, and other popular libs installed:&lt;/p&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://catalog.ngc.nvidia.com/orgs/nvidia/containers/pytorch?ref=alexdremov.me&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;PyTorch | NVIDIA NGC&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;PyTorch is a GPU accelerated tensor computational framework. Functionality can be extended with common Python libraries such as NumPy and SciPy. Automatic differentiation is done with a tape-based system at the functional and neural network layer levels.&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://catalog.ngc.nvidia.com/favicon.ico&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;NVIDIA NGC Catalog&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://assets.nvidiagrid.net/ngc/logos/OSS-Nvidia-Partnership-Pytorch.png&quot; alt=&quot;&quot; onerror=&quot;this.style.display = &apos;none&apos;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;A Docker container is the ultimate solution for problems like&lt;br&gt;&quot;Hey, it works on my machine. I have no idea why it doesn&apos;t on yours.&quot;&lt;/div&gt;&lt;/div&gt;&lt;h2 id=&quot;get-comfortable-with-pytorch-profiler&quot;&gt;Get comfortable with PyTorch profiler&lt;/h2&gt;&lt;p&gt;Before optimizing anything, you have to understand how long some parts of your code run. Pytorch profiler is &lt;em&gt;almost&lt;/em&gt; an all-in-one tool for profiling training. It&apos;s able to record:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;CPU operations timings&lt;/li&gt;&lt;li&gt;CUDA kernels timings&lt;/li&gt;&lt;li&gt;Memory consumption history&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;That&apos;s all you need. And it&apos;s easy to enable!&lt;/p&gt;&lt;p&gt;To record events, all you need is to embed training into a profiler context like this:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;import torch.autograd.profiler as profiler

with profiler.profile(
  activities=[ProfilerActivity.CPU, ProfilerActivity.CUDA],
  on_trace_ready=torch.profiler.tensorboard_trace_handler(&apos;./logs&apos;),
) as prof:
  train(args)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;After that, you can launch the tensorboard and view profiling traces. Do not forget to install &lt;a href=&quot;https://pypi.org/project/torch-tb-profiler/?ref=alexdremov.me&quot;&gt;torch-tb-profiler&lt;/a&gt;.&lt;/p&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://pytorch.org/tutorials/intermediate/tensorboard_profiler_tutorial.html?ref=alexdremov.me&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;PyTorch Profiler With TensorBoard — PyTorch Tutorials 2.3.0+cu121 documentation&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://pytorch.org/favicon.ico&quot; alt=&quot;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://pytorch.org/tutorials/_static/img/profiler_overview1.png&quot; alt=&quot;&quot; onerror=&quot;this.style.display = &apos;none&apos;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;p&gt;Profiler has a lot of different options, but the most important are &lt;code&gt;activities&lt;/code&gt; and &lt;code&gt;profile_memory&lt;/code&gt;. You can experiment with other options, but keep in mind a simple rule: &lt;strong&gt;the fewer options you&apos;ve enabled, the less overhead you have&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;So, if you want to profile CUDA kernel execution timings, it is a good idea to turn off CPU profiling and all other features. In this mode, profiling will be as close to the real execution as possible.&lt;/p&gt;&lt;p&gt;To make traces easier to understand, consider adding profiling contexts that describe core parts of your code. If profiling is not enabled, those are no-op.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;with profiler.record_function(&quot;forward_pass&quot;):
  result = model(**batch)

with profiler.record_function(&quot;train_step&quot;):
  step(**result)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This way, the labels that you use will be visible in traces. So, it will be easier to identify code blocks. Or even more granular inside mode&apos;s forward:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;with profiler.record_function(&quot;transformer_layer:self_attention&quot;):
  data = self.self_attention(**data)

...

with profiler.record_function(&quot;transformer_layer:encoder_attention&quot;):
  data = self.encoder_attention(**data, **encoder_data)&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;understanding-pytorch-traces&quot;&gt;Understanding PyTorch traces&lt;/h2&gt;&lt;p&gt;After you gather traces, open them in the tensorboard. That&apos;s what the CPU + CUDA profile looks like:&lt;/p&gt;&lt;figure class=&quot;kg-card kg-image-card kg-width-wide kg-card-hascaption&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2024/05/profiler_trace_view1.png&quot; class=&quot;kg-image&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;2000&quot; height=&quot;575&quot; srcset=&quot;https://alexdremov.me/content/images/size/w600/2024/05/profiler_trace_view1.png 600w, https://alexdremov.me/content/images/size/w1000/2024/05/profiler_trace_view1.png 1000w, https://alexdremov.me/content/images/size/w1600/2024/05/profiler_trace_view1.png 1600w, https://alexdremov.me/content/images/2024/05/profiler_trace_view1.png 2086w&quot; sizes=&quot;(min-width: 1200px) 1200px&quot;&gt;&lt;figcaption&gt;&lt;span style=&quot;white-space: pre-wrap;&quot;&gt;source: &lt;/span&gt;&lt;a href=&quot;https://pytorch.org/tutorials/intermediate/tensorboard_profiler_tutorial.html?ref=alexdremov.me&quot;&gt;&lt;span style=&quot;white-space: pre-wrap;&quot;&gt;https://pytorch.org/tutorials/intermediate/tensorboard_profiler_tutorial.html&lt;/span&gt;&lt;/a&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;Straight away, find the core parts of any training:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;data loading&lt;/li&gt;&lt;li&gt;forward pass&lt;/li&gt;&lt;li&gt;backward pass&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Backward pass is handled by PyTorch in a separate thread (thread 16893 on the image above), so it is easy to identify.&lt;/p&gt;&lt;h2 id=&quot;data-loading&quot;&gt;Data loading&lt;/h2&gt;&lt;p&gt;For data loading, we want near-zero timings.&lt;/p&gt;&lt;p&gt;No compromises.&lt;/p&gt;&lt;p&gt;That&apos;s because during data loading GPU does nothing, which under-utilizes available resources. However, data processing can be overlapped with GPU computing as those are independent parts.&lt;/p&gt;&lt;p&gt;You can easily identify areas where GPU is idle&amp;nbsp;— just look at &lt;em&gt;GPU Est. SM Efficiency&lt;/em&gt; and &lt;em&gt;GPU Utilization&lt;/em&gt; figures in the profiler&apos;s trace. Areas with zero activity are our patients. That&apos;s where GPU does nothing.&lt;/p&gt;&lt;p&gt;A simple solution for that is:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;process data in the background process (no GIL)&lt;/li&gt;&lt;li&gt;process data augmentations and transforms in parallel processes&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;If you use PyTorch DataLoader, then it can be easily achieved by specifying &lt;code&gt;num_workers&lt;/code&gt;. It&apos;s more complicated if you use &lt;code&gt;IterableDataset&lt;/code&gt;, as then data will be duplicated. However, this issue still can be solved by using &lt;a href=&quot;https://pytorch.org/docs/stable/data.html?ref=alexdremov.me#torch.utils.data.IterableDataset&quot;&gt;get_worker_info()&lt;/a&gt; — you need to adjust iteration in a way so that each worker receives different, non-intersecting rows.&lt;/p&gt;&lt;p&gt;For more configurable processing, you may consider implementing multi-process transforms yourself with &lt;code&gt;multiprocessing&lt;/code&gt;&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;If you never checked your code&apos;s data processing speed, then this slight modification can yield &lt;b&gt;&lt;strong style=&quot;white-space: pre-wrap;&quot;&gt;dramatic speedups&lt;/strong&gt;&lt;/b&gt;&lt;/div&gt;&lt;/div&gt;
&lt;!--kg-card-begin: html--&gt;
&lt;section class=&quot;custom-replace&quot; data-replace=&quot;.subscription-article&quot;&gt;
    &lt;a class=&quot;gh-portal-triggerbtn-container&quot;&gt;Subscribe and don&apos;t miss posts!&lt;/a&gt;
&lt;/section&gt;
&lt;!--kg-card-end: html--&gt;
&lt;h2 id=&quot;making-friends-with-memory-allocator&quot;&gt;Making friends with memory allocator&lt;/h2&gt;&lt;p&gt;You want to be friends with PyTorch&apos;s CUDA caching allocator.&lt;/p&gt;&lt;p&gt;When you allocate tensors with PyTorch on a CUDA device, PyTorch will use a caching allocator. That&apos;s because &lt;code&gt;cudaMalloc&lt;/code&gt;/&lt;code&gt;cudaFree&lt;/code&gt; are expensive operations that we want to avoid, so PyTorch has its allocator that will try to reuse previously allocated through &lt;code&gt;cudaMalloc&lt;/code&gt; blocks. That is, if PyTorch&apos;s allocator has an appropriate block available, it will give it straight away without calling &lt;code&gt;cudaMalloc&lt;/code&gt;. That way, &lt;code&gt;cudaMalloc&lt;/code&gt; is called only at the beginning.&lt;/p&gt;&lt;p&gt;However, if you&apos;re dealing with data of variable length, different forward passes will require intermediate tensors of different sizes. So, PyTorch&apos;s allocator may not have an appropriate block of data available. In this case, the allocator panics and releases allocated previously bocks by calling &lt;code&gt;cudaFree&lt;/code&gt; to free up space for new allocations.&lt;/p&gt;&lt;p&gt;After that, the allocator starts building its cache again, doing tons of &lt;code&gt;cudaMalloc&lt;/code&gt;, which is an expensive operation. You can spot this problem by looking at the memory profiler section of the tensorboard profiler viewer.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;You also can spot this problem in the traces. It will be visible as calls to &lt;code spellcheck=&quot;false&quot; style=&quot;white-space: pre-wrap;&quot;&gt;cudaMalloc&lt;/code&gt; and &lt;code spellcheck=&quot;false&quot; style=&quot;white-space: pre-wrap;&quot;&gt;cudaFree&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;figure class=&quot;kg-card kg-image-card kg-width-wide kg-card-hascaption&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2024/05/Screenshot-2024-05-26-at-18.17.44.png&quot; class=&quot;kg-image&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;2000&quot; height=&quot;460&quot; srcset=&quot;https://alexdremov.me/content/images/size/w600/2024/05/Screenshot-2024-05-26-at-18.17.44.png 600w, https://alexdremov.me/content/images/size/w1000/2024/05/Screenshot-2024-05-26-at-18.17.44.png 1000w, https://alexdremov.me/content/images/size/w1600/2024/05/Screenshot-2024-05-26-at-18.17.44.png 1600w, https://alexdremov.me/content/images/2024/05/Screenshot-2024-05-26-at-18.17.44.png 2000w&quot; sizes=&quot;(min-width: 1200px) 1200px&quot;&gt;&lt;figcaption&gt;&lt;span style=&quot;white-space: pre-wrap;&quot;&gt;PyTorch allocator freaks out &lt;/span&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;As you see, a red line that corresponds to the allocator&apos;s reserved memory constantly changes. That means that PyTorch allocator is not able to efficiently handle allocation requests.&lt;/p&gt;&lt;p&gt;When allocations are handled without the allocator panicking, the red line is completely straight&lt;/p&gt;&lt;figure class=&quot;kg-card kg-image-card kg-width-wide kg-card-hascaption&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2024/05/Screenshot-2024-05-26-at-18.36.36.png&quot; class=&quot;kg-image&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;2000&quot; height=&quot;714&quot; srcset=&quot;https://alexdremov.me/content/images/size/w600/2024/05/Screenshot-2024-05-26-at-18.36.36.png 600w, https://alexdremov.me/content/images/size/w1000/2024/05/Screenshot-2024-05-26-at-18.36.36.png 1000w, https://alexdremov.me/content/images/size/w1600/2024/05/Screenshot-2024-05-26-at-18.36.36.png 1600w, https://alexdremov.me/content/images/2024/05/Screenshot-2024-05-26-at-18.36.36.png 2000w&quot; sizes=&quot;(min-width: 1200px) 1200px&quot;&gt;&lt;figcaption&gt;&lt;span style=&quot;white-space: pre-wrap;&quot;&gt;PyTorch allocator works as expected&lt;/span&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;As I said, that is usually due to variable shapes of tensors. How to fix that?&lt;/p&gt;&lt;h3 id=&quot;1-expandable-segments&quot;&gt;&lt;strong&gt;1.&amp;nbsp;Expandable Segments&lt;/strong&gt;&lt;/h3&gt;&lt;p&gt;The first thing that is worth trying is to set PyTorch&apos;s relatively new allocator mode:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;PYTORCH_CUDA_ALLOC_CONF=&quot;expandable_segments:True&quot;&lt;/code&gt;&lt;/pre&gt;&lt;blockquote&gt;If set to&amp;nbsp;&lt;code&gt;True&lt;/code&gt;, this setting instructs the allocator to create CUDA allocations that can later be expanded to better handle cases where a job changes allocation sizes frequently, such as having a changing batch size.&lt;/blockquote&gt;&lt;p&gt;So, this tells PyTorch allocator to allocate blocks that could be expanded in the future, which is exactly our case. Though, if size variations are too big, it still may fail to solve the issue. In this case, move to the next option.&lt;/p&gt;&lt;h3 id=&quot;2-make-allocations-variate-less&quot;&gt;&lt;strong&gt;2.&amp;nbsp;Make allocations variate less&lt;/strong&gt;&lt;/h3&gt;&lt;p&gt;Another possible solution is to make data shapes consistent. That way it will be easier for the allocator to find an appropriate data block to reuse.&lt;/p&gt;&lt;p&gt;To accomplish that, you may pad data to the same sizes. Or you can preheat the allocator by running a model with maximum input sizes.&lt;/p&gt;&lt;p&gt;You can learn more about PyTorch allocator modification in the following article&lt;/p&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://docs.pytorch.org/docs/2.3/notes/cuda.html?ref=alexdremov.me&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;CUDA semantics — PyTorch 2.3 documentation&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;A guide to torch.cuda, a PyTorch module to run CUDA operations&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://alexdremov.me/content/images/icon/favicon-fa293a76-ec41-46f9-89ff-ec3f031e93c7.ico&quot; alt=&quot;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/thumbnail/view-page-source-icon-b85eba2c-358a-49f7-891c-058ec3d29084.svg&quot; alt=&quot;&quot; onerror=&quot;this.style.display = &apos;none&apos;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;h2 id=&quot;tidy-up-allocations-history&quot;&gt;Tidy up allocations history&lt;/h2&gt;&lt;p&gt;We want to use all available GPU memory — that allows us to run big batches and process data faster. However, at some point, you will encounter a &lt;em&gt;CUDA out-of-memory&lt;/em&gt; error when increasing batch size. What causes this error?&lt;/p&gt;&lt;p&gt;To debug this, we can view the allocator&apos;s memory history. It can be recorded through PyTorch and then visualized at &lt;a href=&quot;https://pytorch.org/memory_viz?ref=alexdremov.me&quot;&gt;https://pytorch.org/memory_viz&lt;/a&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Start:&lt;/strong&gt;&amp;nbsp;&lt;code&gt;torch.cuda.memory._record_memory_history(max_entries=100000)&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Save:&lt;/strong&gt;&amp;nbsp;&lt;code&gt;torch.cuda.memory._dump_snapshot(file_name)&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Stop:&lt;/strong&gt;&amp;nbsp;&lt;code&gt;torch.cuda.memory._record_memory_history(enabled=None)&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Visualization will draw something like this:&lt;/p&gt;&lt;figure class=&quot;kg-card kg-image-card kg-card-hascaption&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2024/05/fig1.png&quot; class=&quot;kg-image&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;1185&quot; height=&quot;656&quot; srcset=&quot;https://alexdremov.me/content/images/size/w600/2024/05/fig1.png 600w, https://alexdremov.me/content/images/size/w1000/2024/05/fig1.png 1000w, https://alexdremov.me/content/images/2024/05/fig1.png 1185w&quot; sizes=&quot;(min-width: 720px) 720px&quot;&gt;&lt;figcaption&gt;&lt;span style=&quot;white-space: pre-wrap;&quot;&gt;source: &lt;/span&gt;&lt;a href=&quot;https://pytorch.org/blog/understanding-gpu-memory-1/?ref=alexdremov.me&quot;&gt;&lt;span style=&quot;white-space: pre-wrap;&quot;&gt;https://pytorch.org/blog/understanding-gpu-memory-1/&lt;/span&gt;&lt;/a&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;The x-axis represents time, the y-axis represents total used memory, and colourful blocks represent tensors. So, it shows when tensors were allocated and when it was released.&lt;/p&gt;&lt;p&gt;You may notice narrow spikes — those are short-lasting tensors that take up a lot of space. By clicking on a tensor, you can get information on where this tensor was allocated. We want to minimize those spikes as they limit efficient memory usage. Check out what caused this spike and consider other ways of computing what you intended.&lt;/p&gt;&lt;p&gt;Apart from spikes, it&apos;s easy to detect memory leaks:&lt;/p&gt;&lt;figure class=&quot;kg-card kg-image-card kg-card-hascaption&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2024/05/fig3.png&quot; class=&quot;kg-image&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;1185&quot; height=&quot;729&quot; srcset=&quot;https://alexdremov.me/content/images/size/w600/2024/05/fig3.png 600w, https://alexdremov.me/content/images/size/w1000/2024/05/fig3.png 1000w, https://alexdremov.me/content/images/2024/05/fig3.png 1185w&quot; sizes=&quot;(min-width: 720px) 720px&quot;&gt;&lt;figcaption&gt;&lt;span style=&quot;white-space: pre-wrap;&quot;&gt;source: &lt;/span&gt;&lt;a href=&quot;https://pytorch.org/blog/understanding-gpu-memory-1/?ref=alexdremov.me&quot;&gt;&lt;span style=&quot;white-space: pre-wrap;&quot;&gt;https://pytorch.org/blog/understanding-gpu-memory-1/&lt;/span&gt;&lt;/a&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;As you see, some data after the first forward is not cleared. By clicking on blocks you can get the idea where these tensors come from. In the image is the case when gradients are not cleared after the training step, so they lay dead during the forward pass, limiting the ability to increase the batch size to fit more data.&lt;/p&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://pytorch.org/blog/understanding-gpu-memory-1/?ref=alexdremov.me&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;Understanding GPU Memory 1: Visualizing All Allocations over Time&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;During your time with PyTorch on GPUs, you may be familiar with this common error message:&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://pytorch.org/favicon.ico&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;PyTorch&lt;/span&gt;&lt;span class=&quot;kg-bookmark-publisher&quot;&gt;Aaron Shi, Zachary DeVito&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://pytorch.org/assets/images/social-share.jpg&quot; alt=&quot;&quot; onerror=&quot;this.style.display = &apos;none&apos;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;h2 id=&quot;speed-up-the-model-and-use-less-memory&quot;&gt;Speed up the model and use less memory&lt;/h2&gt;&lt;p&gt;What can be better than this? We can achieve so by using the &lt;strong&gt;FlashAttention&lt;/strong&gt; kernel for calculating dot-product attention. &lt;/p&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://github.com/Dao-AILab/flash-attention?ref=alexdremov.me&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;GitHub - Dao-AILab/flash-attention: Fast and memory-efficient exact attention&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;Fast and memory-efficient exact attention. Contribute to Dao-AILab/flash-attention development by creating an account on GitHub.&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://github.githubassets.com/assets/pinned-octocat-093da3e6fa40.svg&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;GitHub&lt;/span&gt;&lt;span class=&quot;kg-bookmark-publisher&quot;&gt;Dao-AILab&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://opengraph.githubassets.com/e1e6d6e0ffe03e775ffc9262d8022c4f844acd1c07d84105567bdd4412666a79/Dao-AILab/flash-attention&quot; alt=&quot;&quot; onerror=&quot;this.style.display = &apos;none&apos;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;p&gt;If you haven&apos;t heard about it, it is a way of calculating precise dot product attention without constructing the attention matrix explicitly. That optimizes GPU&apos;s io operations which improves speed and also &lt;strong&gt;dramatically&lt;/strong&gt; minimizes memory consumption. There&apos;s simply no reason not to use it.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-red&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;😡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Unfortunately, there&apos;s one reason not to use it — hardware.&lt;br&gt;&lt;br&gt;Flash attention only works with &lt;code spellcheck=&quot;false&quot; style=&quot;white-space: pre-wrap;&quot;&gt;fp16&lt;/code&gt; and &lt;code spellcheck=&quot;false&quot; style=&quot;white-space: pre-wrap;&quot;&gt;bf16&lt;/code&gt; precision on compatible hardware. That is NVIDIA Ampere, Hooper, etc&lt;/div&gt;&lt;/div&gt;&lt;p&gt;Other libraries use flash attention under the hood, so you may consider using other variants that better fit your codebase.&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;strong&gt;XFormers&lt;/strong&gt;&lt;/li&gt;&lt;/ol&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://github.com/facebookresearch/xformers?ref=alexdremov.me&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;GitHub - facebookresearch/xformers: Hackable and optimized Transformers building blocks, supporting a composable construction.&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;Hackable and optimized Transformers building blocks, supporting a composable construction. - facebookresearch/xformers&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://github.githubassets.com/assets/pinned-octocat-093da3e6fa40.svg&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;GitHub&lt;/span&gt;&lt;span class=&quot;kg-bookmark-publisher&quot;&gt;facebookresearch&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://repository-images.githubusercontent.com/416849738/22b08af9-fe74-4946-acda-52e73c72d99e&quot; alt=&quot;&quot; onerror=&quot;this.style.display = &apos;none&apos;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;ol start=&quot;2&quot;&gt;&lt;li&gt;&lt;strong&gt;Transformer Engine&lt;/strong&gt;&lt;/li&gt;&lt;/ol&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://github.com/NVIDIA/TransformerEngine?ref=alexdremov.me&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;GitHub - NVIDIA/TransformerEngine: A library for accelerating Transformer models on NVIDIA GPUs, including using 8-bit floating point (FP8) precision on Hopper and Ada GPUs, to provide better performance with lower memory utilization in both training and inference.&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;A library for accelerating Transformer models on NVIDIA GPUs, including using 8-bit floating point (FP8) precision on Hopper and Ada GPUs, to provide better performance with lower memory utilizatio…&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://github.githubassets.com/assets/pinned-octocat-093da3e6fa40.svg&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;GitHub&lt;/span&gt;&lt;span class=&quot;kg-bookmark-publisher&quot;&gt;NVIDIA&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://opengraph.githubassets.com/89a18f7d463b8ff56291d77cbd2f22f9cffd623496d6a0f1fb32e60bd4549ea1/NVIDIA/TransformerEngine&quot; alt=&quot;&quot; onerror=&quot;this.style.display = &apos;none&apos;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;ol start=&quot;3&quot;&gt;&lt;li&gt;&lt;strong&gt;PyTorch itself!&lt;/strong&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;That is true, new versions of PyTorch may use flash attention when applicable. To activate this mode, you need to execute attention blocks in the context manager that specify which attention strategy to use: &lt;/p&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://docs.pytorch.org/docs/2.3/generated/torch.nn.functional.scaled_dot_product_attention.html?ref=alexdremov.me#torch-nn-functional-scaled-dot-product-attention&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;torch.nn.functional.scaled_dot_product_attention — PyTorch 2.3 documentation&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://alexdremov.me/content/images/icon/favicon-cef8586b-2e21-4128-b419-d606d32e6ad0.ico&quot; alt=&quot;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/thumbnail/view-page-source-icon-ce84c398-5df0-4e97-84ba-0a46d92b6964.svg&quot; alt=&quot;&quot; onerror=&quot;this.style.display = &apos;none&apos;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;h2 id=&quot;optimize-multi-gpu-data-redundancy-%E2%80%94-fsdp&quot;&gt;Optimize multi-GPU data redundancy — FSDP&lt;/h2&gt;&lt;p&gt;If you use multiple GPUs to run your training, the basic solution is to use the &lt;code&gt;DistributedDataParallel&lt;/code&gt; class. This way, several identical processes are spawned, and gradients are aggregated during the backward step.&lt;/p&gt;&lt;p&gt;However, that is sub-optimal!&lt;/p&gt;&lt;p&gt;The problem is as we spawned identical processes, then we have identical models and optimiser states on each GPU, which is redundant. The solution is to shard data across. We can do so using the Fully Sharded Data Parallel PyTorch wrapper. &lt;/p&gt;&lt;figure class=&quot;kg-card kg-image-card kg-width-wide kg-card-hascaption&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2024/05/fsdp_workflow.png&quot; class=&quot;kg-image&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;2000&quot; height=&quot;903&quot; srcset=&quot;https://alexdremov.me/content/images/size/w600/2024/05/fsdp_workflow.png 600w, https://alexdremov.me/content/images/size/w1000/2024/05/fsdp_workflow.png 1000w, https://alexdremov.me/content/images/size/w1600/2024/05/fsdp_workflow.png 1600w, https://alexdremov.me/content/images/2024/05/fsdp_workflow.png 2000w&quot; sizes=&quot;(min-width: 1200px) 1200px&quot;&gt;&lt;figcaption&gt;&lt;span style=&quot;white-space: pre-wrap;&quot;&gt;source: &lt;/span&gt;&lt;a href=&quot;https://pytorch.org/tutorials/intermediate/FSDP_tutorial.html?ref=alexdremov.me&quot;&gt;&lt;span style=&quot;white-space: pre-wrap;&quot;&gt;https://pytorch.org/tutorials/intermediate/FSDP_tutorial.html&lt;/span&gt;&lt;/a&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;How does it work?&lt;/p&gt;&lt;p&gt;As I said, when training on several GPUs, each process has exact copies of the same data when training with DDP. We can optimize it, by implementing several enhancements:&lt;/p&gt;&lt;h3 id=&quot;shard-optimizer-state-zero-1&quot;&gt;&lt;strong&gt;Shard optimizer state (ZeRO 1)&lt;/strong&gt;&lt;/h3&gt;&lt;p&gt;When training with DDP, each process holds a complete copy of the optimizer states. With ZeRO1, we shard these optimizer states across all ranks such that each rank holds only a portion of the optimizer states. During the backward pass, each rank only needs to gather the optimizer states relevant to its parameters to make an optimization step. This reduction in redundancy helps conserve memory.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;&amp;nbsp;In case of the Adam, which holds parameters at roughly twice the model size, sharding the optimizer state among 8 ranks means each rank &lt;b&gt;&lt;strong style=&quot;white-space: pre-wrap;&quot;&gt;stores only one quarter (2/8) of the total state size.&lt;/strong&gt;&lt;/b&gt;&lt;/div&gt;&lt;/div&gt;&lt;h3 id=&quot;shard-gradients-zero-2&quot;&gt;&lt;strong&gt;Shard gradients (ZeRO 2)&lt;/strong&gt;&lt;/h3&gt;&lt;p&gt;We shard optimizer states. Now, we will modify the optimizer step to shard gradients too. If one rank has optimizer states for a portion of  parameters, then we will:&lt;/p&gt;&lt;ul&gt;&lt;ul&gt;&lt;li&gt;aggregate all gradients relevant to the states the rank holds&lt;/li&gt;&lt;li&gt;calculate optimization step&lt;/li&gt;&lt;li&gt;send optimization step for a portion of parameters to all other ranks&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;&lt;p&gt;As you noticed, now each rank does not need to hold a full replica of gradients. We can send gradients to a relevant rank as soon as they are available. So, we can reduce peak memory consumption even further.&lt;/p&gt;&lt;h3 id=&quot;shard-model-parameters-zero-3&quot;&gt;&lt;strong&gt;Shard model parameters (ZeRO 3)&lt;/strong&gt;&lt;/h3&gt;&lt;p&gt;This is about to be epic.&lt;/p&gt;&lt;p&gt;Why do we need to store a full copy of the model on each rank? Let&apos;s shard model parameters between all ranks. Then, we&apos;re going to fetch the required parameters just in time during forward and backward.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;In case of large models, these optimisations can drammaticaly decrease memory consumption&lt;/div&gt;&lt;/div&gt;&lt;h2 id=&quot;how-to-use-fsdp&quot;&gt;How to use FSDP?&lt;/h2&gt;&lt;p&gt;Quite simple actually. All we need is to wrap the model with FSDP:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;import torch
import torch.nn as nn
import torch.optim as optim
from torch.distributed.fsdp import FullyShardedDataParallel as FSDP


model = FSDP(model)

# it&apos;s critical to get parameters from the wrapped model
# as only a portion of them returned (sharded part)
optimizer = optim.Adam(model.parameters())

# consuct training as usual
train(model, optimizer)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You can also specify the sharding strategy of FSDP. For example, we can select the &lt;code&gt;SHARD_GRAD_OP&lt;/code&gt; strategy to achieve behaviour similar to that of ZeRO2. You can learn about other strategies here:&lt;/p&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://pytorch.org/docs/stable/fsdp.html?ref=alexdremov.me#torch.distributed.fsdp.ShardingStrategy&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;FullyShardedDataParallel — PyTorch 2.3 documentation&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://pytorch.org/favicon.ico&quot; alt=&quot;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://pytorch.org/docs/stable/_static/images/view-page-source-icon.svg&quot; alt=&quot;&quot; onerror=&quot;this.style.display = &apos;none&apos;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;p&gt;Also, you can wrap with FSDP submodules. In the example above, only one FSDP module is used, which will reduce computation efficiency and memory efficiency. The way it works is that, suppose your model contains 100 Linear layers. If you do FSDP(model), there will only be one FSDP unit which wraps the entire model. In that case, the allgather would collect the full parameters for all 100 linear layers, and hence won’t save CUDA memory for parameter sharding.&lt;/p&gt;&lt;p&gt;You can wrap submodules explicitly or define an auto-wrap policy. To learn more about FSDP, read the PyTorch guide:&lt;/p&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://docs.pytorch.org/docs/2.3/fsdp.html?ref=alexdremov.me#torch.distributed.fsdp.ShardingStrategy&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;FullyShardedDataParallel — PyTorch 2.3 documentation&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://alexdremov.me/content/images/icon/favicon-483e38c6-4935-43ca-b44e-1095584d2a04.ico&quot; alt=&quot;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/thumbnail/view-page-source-icon-db949506-bb0e-49dd-b160-1b8f61fddb12.svg&quot; alt=&quot;&quot; onerror=&quot;this.style.display = &apos;none&apos;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;h2 id=&quot;magic-speedup-with-torchcompile&quot;&gt;Magic speedup with &lt;code&gt;torch.compile&lt;/code&gt; &lt;/h2&gt;&lt;p&gt;That is, torch compile can speed up your code by several percent by just enabling it.&lt;/p&gt;&lt;p&gt;Torch traces your execution graph and tries to compile it into an efficient format so that the model can be executed almost without Python invocation. &lt;/p&gt;&lt;p&gt;Basic usage is to wrap the model with compile:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;import torch

model = torch.compile(model)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This will execute almost instantly. The actual tracing will happen only during the first forward.&lt;/p&gt;&lt;p&gt;It also has a lot of options that are worth to try:&lt;/p&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://pytorch.org/docs/stable/generated/torch.compile.html?ref=alexdremov.me#torch.compile&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;torch.compile — PyTorch 2.3 documentation&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://pytorch.org/favicon.ico&quot; alt=&quot;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://pytorch.org/docs/stable/_static/images/view-page-source-icon.svg&quot; alt=&quot;&quot; onerror=&quot;this.style.display = &apos;none&apos;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Torch compiler is a big feature that will be covered in the next posts! &lt;br&gt;Stay tuned&lt;/div&gt;&lt;/div&gt;&lt;p&gt;Learn more about torch compile here:&lt;/p&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://pytorch.org/tutorials/intermediate/torch_compile_tutorial.html?ref=alexdremov.me&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;Introduction to torch.compile — PyTorch Tutorials 2.3.0+cu121 documentation&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://pytorch.org/favicon.ico&quot; alt=&quot;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://pytorch.org/tutorials/_static/images/view-page-source-icon.svg&quot; alt=&quot;&quot; onerror=&quot;this.style.display = &apos;none&apos;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;&lt;p&gt;This post is in no way complete with explanations. Rather, that is a list of speed-ups that are worth trying straight away. Hope that it was helpful. Feel free to leave a comment!&lt;/p&gt;&lt;p&gt;Consider subscribing &lt;/p&gt;</content:encoded></item><item><title>Swift Actors — Common Problems and Tips</title><link>https://alexdremov.me/swift-actors-common-problems-and-tips/</link><guid isPermaLink="true">https://alexdremov.me/swift-actors-common-problems-and-tips/</guid><description>Swift actors are a powerful tool. However, it is also quite a sophisticated concept that requires deep understanding to write bug-free code</description><pubDate>Tue, 13 Jun 2023 12:32:57 GMT</pubDate><content:encoded>&lt;p&gt;Swift actors are a powerful tool to address data races and make your code thread-safe. However, it is also quite a sophisticated concept that requires deep understanding.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Check out my introduction to Swift Actors or quick guide to Swift async/await&lt;/div&gt;&lt;/div&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://alexdremov.me/conquer-data-races-with-swift-actors/&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;Conquer Data Races with Swift Actors | Alex Dremov&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;Unleash the power of Swift concurrency with Actors! Get all the information you need in this comprehensive article&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://alexdremov.me/assets/icons/apple-touch-icon.png?v=012b35a5f7&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;Alex Dremov&lt;/span&gt;&lt;span class=&quot;kg-bookmark-publisher&quot;&gt;Alex Dremov&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://images.unsplash.com/photo-1532800783378-1bed60adaf58?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=max&amp;amp;fm=jpg&amp;amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDJ8fGFjdG9yfGVufDB8fHx8MTY3NTUxNTM3OQ&amp;amp;ixlib=rb-4.0.3&amp;amp;q=80&amp;amp;w=2000&quot; alt=&quot;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://alexdremov.me/quick-guide-to-async-await-in-swift/&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;Quick Guide to Async Await in Swift | Alex Dremov&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;Everything you need to know about new Swift asynchronous features. Async await, main actor, task, async get, and possible use cases — all covered.&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://alexdremov.me/assets/icons/apple-touch-icon.png?v=012b35a5f7&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;Alex Dremov&lt;/span&gt;&lt;span class=&quot;kg-bookmark-publisher&quot;&gt;Alex Dremov&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2022/04/slide_17.jpg&quot; alt=&quot;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;h2 id=&quot;reentrancy-invalid-state-expectations&quot;&gt;Reentrancy: Invalid State Expectations&lt;/h2&gt;&lt;p&gt;One of the core actor&apos;s features is reentrancy. By allowing calls to the actor&apos;s isolated methods while another method awaits for something, actors reduce the time your code spends on waiting for actor availability.&lt;/p&gt;&lt;p&gt;Though, it requires additional considerations about the actor&apos;s state. Classic example:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;actor Door {
    private var isOpen = false
    
    func open() async {
        isOpen = true
        
        await notifyDoorOpened() // Suspension point
        
        // Mistake! Door could have been closed
        // while notifyDoorOpened was executing
        print(&quot;Door is open: \(isOpen)&quot;)
    }
    
    func close() {
        isOpen = false
    }
    
    func notifyDoorOpened() async {
        try! await Task.sleep(for: .seconds(1))
    }
}

let door = Door()
Task {
    await door.open()
}
Task {
    await door.close()
}&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;Door is open: false&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;So, the first tip is to drop any expectations about the actor&apos;s state after an asynchronous call inside it. Explicitly check for conditions you believe to be true.&lt;/p&gt;&lt;h2 id=&quot;reentrancy-double-computations&quot;&gt;Reentrancy: Double Computations&lt;/h2&gt;&lt;p&gt;An even more common case is when execution enters &lt;strong&gt;the same method&lt;/strong&gt; with the same arguments several times.&lt;/p&gt;&lt;p&gt;For example, let&apos;s suppose that actor performs heavy data loading inside one of its methods. But we don&apos;t want heavy data to be loaded each call, so we implement simple caching:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;import Foundation

actor ActivitiesStorage {
    var cache = [UUID: Data?]()
    
    func retrieveHeavyData(for id: UUID) async -&amp;gt; Data? {
        if let data = cache[id] {
            return data
        }
        
        // ...
        
        let data = await requestDataFromDatabase(for: id) // suspension
        cache[id] = data
        
        return data
    }
    
    private func requestDataFromDatabase(for id: UUID) async -&amp;gt; Data? {
        print(&quot;Performing heavy data loading!&quot;)
        try! await Task.sleep(for: .seconds(1))
        // ...
        return nil
    }
    
}

let id = UUID()
let storage = ActivitiesStorage()

Task {
    let data = await storage.retrieveHeavyData(for: id)
}

Task {
    let data = await storage.retrieveHeavyData(for: id)
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;But our caching is useless as data is loaded twice anyways. &lt;strong&gt;We deal with data race&lt;/strong&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;Performing heavy data loading!
Performing heavy data loading!&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;At this point, you already see that this is due to the actor&apos;s reentrancy. The cache is not set until data is loaded, allowing the following heavy loadings.&lt;/p&gt;&lt;p&gt;Let&apos;s use mutexes! (no, please don&apos;t)&lt;/p&gt;&lt;p&gt;To fix this problem we can explicitly &quot;subscribe&quot; to &lt;strong&gt;single&lt;/strong&gt; heavy data loading and return it when it is available:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;import Foundation

actor ActivitiesStorage {
    var cache = [UUID: Task&amp;lt;Data?, Never&amp;gt;]()
    
    func retrieveHeavyData(for id: UUID) async -&amp;gt; Data? {
        if let task = cache[id] {
            return await task.value
        }
        
        // ...
        
        let task = Task {
            await requestDataFromDatabase(for: id)
        }
        
        // Notice that it is set before `await`
        // So, the following calls will have this task available
        cache[id] = task
        return await task.value // suspension
    }
    
    private func requestDataFromDatabase(for id: UUID) async -&amp;gt; Data? {
        print(&quot;Performing heavy data loading!&quot;)
        try! await Task.sleep(for: .seconds(1))
        // ...
        return nil
    }
    
}

let id = UUID()
let storage = ActivitiesStorage()

Task {
    let data = await storage.retrieveHeavyData(for: id)
}

Task {
    let data = await storage.retrieveHeavyData(for: id)
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;As you see, we use a task to delay await inside an actor, allowing us to set the cache before the suspension. Now, only one call to heavy data is performed.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-purple&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💥&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Using tasks inside actors to delay await is a powerful feature!&lt;/div&gt;&lt;/div&gt;&lt;h2 id=&quot;mainactor-overuse&quot;&gt;@MainActor Overuse&lt;/h2&gt;&lt;p&gt;Marking your methods or classes with &lt;code&gt;@MainActor&lt;/code&gt; results in the code inside them running on the main thread. It is useful for UI-related code as UI updates must happen on the main thread.&lt;/p&gt;&lt;p&gt;However, overusing &lt;code&gt;@MainActor&lt;/code&gt; slows down your concurrent code a lot as it will be running only in one thread, freezing your UI frequently. &lt;/p&gt;&lt;p&gt;To not fall into this trap, do not use &lt;code&gt;@MainActor&lt;/code&gt; for the whole class:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;@MainActor
class OnboardingViewModel: ViewModel {
	// ...
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Such use restricts all methods to the main thread, which may be overlooked when adding new methods or functionality. &lt;/p&gt;&lt;p&gt;Use it for specific methods only.&lt;/p&gt;&lt;p&gt;And decompose your methods so that &lt;code&gt;@MainActor&lt;/code&gt; methods have as little code as possible, resulting in a low chance of main thread block.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;class OnboardingViewModel {
    func performLogIn() async {
        // loading, processing and stuff
        // can be executed on any thread
        
        await updateLogInInformation()
    }
    
    @MainActor func updateLogInInformation() {
        // fast ui updates only
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;!--kg-card-begin: html--&gt;
&lt;section class=&quot;custom-replace&quot; data-replace=&quot;.subscription-article&quot;&gt;
    &lt;a class=&quot;gh-portal-triggerbtn-container&quot;&gt;Subscribe and don&apos;t miss posts!&lt;/a&gt;
&lt;/section&gt;
&lt;!--kg-card-end: html--&gt;
&lt;h2 id=&quot;use-sendable-do-not-keep-this-information-in-mind&quot;&gt;Use Sendable. Do Not Keep This Information In Mind&lt;/h2&gt;&lt;p&gt;The Sendable protocol is a feature added in Swift 5.5 that is used to mark code as safe to be passed across concurrency domains by copying. This means that it is safe to execute Sendable code concurrently.&lt;/p&gt;&lt;p&gt;Before that, &lt;strong&gt;you had to keep in mind which classes and closures are thread-safe and which are not&lt;/strong&gt;. Now, you can explicitly state this by conforming to the Sendable protocol&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;final class FoodData: Sendable {
    // ...
    
    func addFood(foodFactory: @Sendable () -&amp;gt; Food) {
        // ...
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In the code above, we say that &lt;code&gt;FoodData&lt;/code&gt; methods are safe to be called without synchronization. Also, &lt;code&gt;foodFactory&lt;/code&gt; closure is marked with &lt;code&gt;@Sendable&lt;/code&gt; which also means that it can be safely called from different concurrent contexts.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-purple&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💥&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Moreover, if you use &lt;code spellcheck=&quot;false&quot; style=&quot;white-space: pre-wrap;&quot;&gt;Sendable&lt;/code&gt;, Swift automatically checks that your code is actually thread-safe. That&apos;s cool as you cannot introduce unsafe code by accident as your code will not compile.&lt;/div&gt;&lt;/div&gt;&lt;p&gt;You can take one step further and set &lt;code&gt;SWIFT_STRICT_CONCURRENCY&lt;/code&gt; build setting to &lt;code&gt;complete&lt;/code&gt;. In this mode, the swift compiler will not tolerate any thread-unsafe code it detects.&lt;/p&gt;&lt;h2 id=&quot;do-not-ignore-nonisolated-keyword&quot;&gt;Do Not Ignore Nonisolated Keyword&lt;/h2&gt;&lt;p&gt;Nonisolated methods do not mutate or access the actor&apos;s isolated state, therefore they do not require the actor&apos;s isolated execution. Use them to decompose actors&apos; isolated methods into smaller methods. Actors&apos; code must be readable too&lt;/p&gt;&lt;h2 id=&quot;continue-reading-about-swift-ios&quot;&gt;Continue Reading About Swift &amp;amp; iOS&lt;/h2&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://alexdremov.me/tag/ios/&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;Alex Dremov | iOS&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;One of my favourites. Here I write about Swift and iOS development. It is noticeable that I mainly focus on iOS development right now.&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://alexdremov.me/assets/icons/apple-touch-icon.png?v=012b35a5f7&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;Alex Dremov&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://images.unsplash.com/photo-1558126372-76b529458592?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=max&amp;amp;fm=jpg&amp;amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDExfHxpb3N8ZW58MHx8fHwxNjQ5NTA0MTQ5&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;w=2000&quot; alt=&quot;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;</content:encoded></item><item><title>I Contributed to PyTorch. Here&apos;s What I Learned</title><link>https://alexdremov.me/i-contributed-to-pytorch-heres-what-i-learned/</link><guid isPermaLink="true">https://alexdremov.me/i-contributed-to-pytorch-heres-what-i-learned/</guid><description>When you see something that does not work in an omnipresent framework, you believe it can&apos;t be completely broken, right?</description><pubDate>Mon, 20 Mar 2023 16:23:35 GMT</pubDate><content:encoded>&lt;h2 id=&quot;the-issue-must-not-be-that-bad&quot;&gt;The Issue Must Not Be That Bad&lt;/h2&gt;&lt;p&gt;That&apos;s what I thought when I encountered a PyTorch problem during one of my college assignments. Jupyter kernel was dying because of some bug in the LSTM implementation for MPS.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;MPS (Metal Performance Shaders) is an acceleration backend for MacOS that utilizes GPU for computations&lt;/div&gt;&lt;/div&gt;&lt;p&gt;After a quick investigation, I discovered that this happens because of the &lt;code&gt;batch_first&lt;/code&gt; flag. MPS&apos;s backend did not work correctly with it and crushed the entire kernel.&lt;/p&gt;&lt;blockquote&gt;&quot;Easy fix&quot;&lt;br&gt;P.S. After that phrase, Alex spend the next two days fixing what looked like an &quot;easy fix&quot;&lt;/blockquote&gt;&lt;p&gt;PR was merged pretty quickly. Thanks, PyTorch team, for that! And the story could&apos;ve ended here, but I discovered a funny detail in MPS tests.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;@unittest.skipIf(True, &quot;Backward of lstm returns wrong result&quot;)
def test_lstm_2(self, device=&quot;mps&quot;, dtype=torch.float32)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And LSTM was really bad. It got a whole lot worse score than when trained on CUDA or CPU.&lt;/p&gt;&lt;h2 id=&quot;it-was-bad-really-bad&quot;&gt;It Was Bad. Really Bad&lt;/h2&gt;&lt;p&gt;It turned out that LSTM on MPS was &lt;strong&gt;completely&lt;/strong&gt; broken. The forward pass had a bug with the &lt;code&gt;batch_first&lt;/code&gt; flag and hidden cell initialization.&lt;/p&gt;&lt;p&gt;Backward pass used first layers weights for the last layers, mixing up all gradients. It did not calculate gradients for hidden states. And my favourite: the backward function returned initialized with garbage tensors, screwing up all subsequent training. It was a mess that I kept investigating for several days.&lt;/p&gt;&lt;p&gt;Eventually, I fixed LSTM and its tests in a massive PR, ensuring that it is consistent with the CPU.&lt;/p&gt;&lt;figure class=&quot;kg-card kg-image-card&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2023/03/Screenshot-2023-03-20-at-18.59.25.png&quot; class=&quot;kg-image&quot; alt loading=&quot;lazy&quot; width=&quot;1626&quot; height=&quot;692&quot; srcset=&quot;https://alexdremov.me/content/images/size/w600/2023/03/Screenshot-2023-03-20-at-18.59.25.png 600w, https://alexdremov.me/content/images/size/w1000/2023/03/Screenshot-2023-03-20-at-18.59.25.png 1000w, https://alexdremov.me/content/images/size/w1600/2023/03/Screenshot-2023-03-20-at-18.59.25.png 1600w, https://alexdremov.me/content/images/2023/03/Screenshot-2023-03-20-at-18.59.25.png 1626w&quot; sizes=&quot;(min-width: 720px) 720px&quot;&gt;&lt;/figure&gt;&lt;h2 id=&quot;what-i-learned&quot;&gt;What I Learned&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;em&gt;Big projects also have garbage code.&lt;/em&gt; Broken implementation lived in stable releases for &lt;strong&gt;almost a year, &lt;/strong&gt;generating several related GitHub issues.&lt;/li&gt;&lt;li&gt;&lt;em&gt;Contributing to a big project is fun and challenging.&lt;/em&gt; And it eventually helps a lot of developers, which keeps me warm during cold winter nights. Specifically, contributing to PyTorch is extremely simple. Thanks, PyTorch team, for arranging that!&lt;/li&gt;&lt;li&gt;&lt;em&gt;Deploying untested code that looks right is extremely dangerous.&lt;/em&gt; I listed pretty severe mistakes that I found scrutinizing LSTM sources for several days. There&apos;s no way they could have been discovered without extensive testing. Even though the issues were severe, they were also subtle. The code looked right.&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;finally&quot;&gt;Finally&lt;/h2&gt;&lt;p&gt;I was able to complete the college PyTorch assignment even though it required rewriting PyTorch&apos;s LSTM MPS implementation. Consider also solving open issues of your favourite framework or project. At the end of the day, it is a lot more fun than Leetcode problems.&lt;/p&gt;&lt;!--kg-card-begin: html--&gt;&lt;section class=&quot;custom-replace&quot; data-replace=&quot;.subscription-article&quot;&gt;
    &lt;a class=&quot;gh-portal-triggerbtn-container&quot;&gt;Subscribe and don&apos;t miss posts!&lt;/a&gt;
&lt;/section&gt;&lt;!--kg-card-end: html--&gt;&lt;h2 id=&quot;see-my-work&quot;&gt;See My Work&lt;/h2&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://github.com/pytorch/pytorch/pull/95137?ref=alexdremov.me&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;[MPS] Fix LSTM backward and forward pass by AlexRoar · Pull Request #95137 · pytorch/pytorch&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;Fixes #91694Fixes #92615Several transpositions were missing for backward graph in case of batch_first&amp;#x3D;True. The #91694 is not reproduced with batch_first&amp;#x3D;False.After fixing transpose issue, I fi...&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://github.com/fluidicon.png&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;GitHub&lt;/span&gt;&lt;span class=&quot;kg-bookmark-publisher&quot;&gt;pytorch&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://opengraph.githubassets.com/eb01c9acbbb7a453257ef8f56fef240c67c593b66d377dab4b5a74a57868f4c6/pytorch/pytorch/pull/95137&quot; alt=&quot;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://github.com/pytorch/pytorch/pull/95563?ref=alexdremov.me&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;[MPS] Fix bidirectional LSTM &amp;amp; small one-direction LSTM fix by AlexRoar · Pull Request #95563 · pytorch/pytorch&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;Fixes #94754With this PR I hope to finish my breathtaking journey of fixing MPS LSTM.Here, I enable bidirectional on MPS. Also, I’ve noticed that cache key did not account for all parameters, so ...&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://github.com/fluidicon.png&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;GitHub&lt;/span&gt;&lt;span class=&quot;kg-bookmark-publisher&quot;&gt;pytorch&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://opengraph.githubassets.com/36976c41bcb749892d6c73f144887a50c1fc93d5eace798a4202392f5862eeee/pytorch/pytorch/pull/95563&quot; alt=&quot;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://github.com/pytorch/pytorch/pull/96601?ref=alexdremov.me&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;[MPS] LSTM grad_y missing fix by AlexRoar · Pull Request #96601 · pytorch/pytorch&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;Fixes #96416Added tests that do not use LSTM output simalarly to the issueSeems like this fix once again introduces backward incompatibility.&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://github.com/fluidicon.png&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;GitHub&lt;/span&gt;&lt;span class=&quot;kg-bookmark-publisher&quot;&gt;pytorch&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://opengraph.githubassets.com/fdca786439d2b99be79b4955fe26f15414c11266c4ba3a9f0f30d96dc7630dfa/pytorch/pytorch/pull/96601&quot; alt=&quot;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://github.com/pytorch/pytorch/pull/95091?ref=alexdremov.me&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;[MPS] LogSoftmax numerical stability by AlexRoar · Pull Request #95091 · pytorch/pytorch&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;Fixes #94043Calculations are now consistent with numericaly stable formula and CPU:$LogSoftmax(X, \dim) &amp;#x3D; X - \max(X, \dim) - \log(sum(X - \max(X, \dim), \dim))$@malfet&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://github.com/fluidicon.png&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;GitHub&lt;/span&gt;&lt;span class=&quot;kg-bookmark-publisher&quot;&gt;pytorch&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://opengraph.githubassets.com/ef21590314dfd0a1ec52bfdf6f74cf293d08fcd4206bd2433d5faf9a4e42278a/pytorch/pytorch/pull/95091&quot; alt=&quot;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;</content:encoded></item><item><title>Conquer Data Races with Swift Actors</title><link>https://alexdremov.me/conquer-data-races-with-swift-actors/</link><guid isPermaLink="true">https://alexdremov.me/conquer-data-races-with-swift-actors/</guid><description>Unleash the power of Swift concurrency with Actors! Get all the information you need in this comprehensive article</description><pubDate>Tue, 07 Feb 2023 19:08:18 GMT</pubDate><content:encoded>&lt;p&gt;Mobile development is close to impossible without concurrent code. While executing tasks concurrently generally speeds up your app, it also introduces a lot of challenges to overcome. And one of them is a data race.&lt;/p&gt;&lt;h2 id=&quot;data-races-and-when-they-happen&quot;&gt;Data Races And When They Happen&lt;/h2&gt;&lt;p&gt;Try to find a problem in the code below&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;import Foundation

var counter = 0
let queue = DispatchQueue.global()

for _ in 1...100500 {
    queue.async {
        counter += 1
    }
}

queue.sync(flags: .barrier) {
    // Synchronous barrier to wait untill all
    // async tasks are finished
    print(&quot;Final value: \(counter)&quot;)
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This does not output &lt;code&gt;100500&lt;/code&gt; as desired&lt;/p&gt;&lt;p&gt;&lt;code&gt;Final value: 100490&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Let me run the same code one more time.&lt;/p&gt;&lt;p&gt; &lt;code&gt;Final value: 100486&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Voilà&lt;/p&gt;&lt;p&gt;As you see, the same code produces different results. In this case, we deal with a &lt;strong&gt;data race.&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Data races occur when multiple threads access a shared resource without protections, leading to undefined behaviour&lt;/div&gt;&lt;/div&gt;&lt;p&gt;In the code above, asynchronous tasks capture &lt;code&gt;counter&lt;/code&gt; and modify it simultaneously. This leads to undefined behaviour.&lt;/p&gt;&lt;div class=&quot;kg-card kg-toggle-card&quot; data-kg-toggle-state=&quot;close&quot;&gt;
            &lt;div class=&quot;kg-toggle-heading&quot;&gt;
                &lt;h4 class=&quot;kg-toggle-heading-text&quot;&gt;&lt;span style=&quot;white-space: pre-wrap;&quot;&gt;What&apos;s under the hood?&lt;/span&gt;&lt;/h4&gt;
                &lt;button class=&quot;kg-toggle-card-icon&quot; aria-label=&quot;Expand toggle to read content&quot;&gt;
                    &lt;svg id=&quot;Regular&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot;&gt;
                        &lt;path class=&quot;cls-1&quot; d=&quot;M23.25,7.311,12.53,18.03a.749.749,0,0,1-1.06,0L.75,7.311&quot;&gt;&lt;/path&gt;
                    &lt;/svg&gt;
                &lt;/button&gt;
            &lt;/div&gt;
            &lt;div class=&quot;kg-toggle-content&quot;&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre-wrap;&quot;&gt;The reasoning behind such behaviour is in assembly operations. Before incrementing the value, it is loaded from RAM into the processor&apos;s register. At the same time, other threads can increment the value and save it back to RAM. But the thread that saved value from memory to register will not know about it and will continue to work with the old value, eventually overwriting the updated value in RAM&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;
        &lt;/div&gt;&lt;h2 id=&quot;non-actor-solutions&quot;&gt;Non-Actor Solutions&lt;/h2&gt;&lt;p&gt;Before the introduction of actors, several solutions to the problem were used.&lt;/p&gt;&lt;h3 id=&quot;serial-queue&quot;&gt;Serial Queue&lt;/h3&gt;&lt;p&gt;We can create a dedicated queue that will be used during all accesses to the counter. Internally, tasks execute serially, so no data races occur.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;import Foundation

var counter = 0
let queue = DispatchQueue.global()

// Serial queue
let counterAccessQueue = DispatchQueue(label: &quot;CounterAccessQueue&quot;)

for _ in 1...100500 {
	queue.async {
		counterAccessQueue.sync { counter += 1 }
	}
}

queue.sync(flags: .barrier) {
	counterAccessQueue.sync { print(&quot;Final value: \(counter)&quot;) }
}&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;concurrent-queue-with-barrier&quot;&gt;Concurrent Queue With Barrier&lt;/h3&gt;&lt;p&gt;It&apos;s possible to use sync with barrier parameter to modify value even in concurrent queue. Basically, the barrier waits until all previous tasks are completed, then it executes code synchronously, and after that queue continues to operate as usual.&lt;/p&gt;&lt;p&gt;In the current example, it basically transforms concurrent queue to serial, but still, it&apos;s a different approach.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;import Foundation

var counter = 0
let queue = DispatchQueue.global()

for _ in 1...100500 {
	queue.sync(flags: .barrier) {
		counter += 1
	}
}

queue.sync {
	print(&quot;Final value: \(counter)&quot;)
}&lt;/code&gt;&lt;/pre&gt;
&lt;!--kg-card-begin: html--&gt;
&lt;section class=&quot;custom-replace&quot; data-replace=&quot;.subscription-article&quot;&gt;
    &lt;a class=&quot;gh-portal-triggerbtn-container&quot;&gt;Subscribe and don&apos;t miss posts!&lt;/a&gt;
&lt;/section&gt;
&lt;!--kg-card-end: html--&gt;
&lt;h2 id=&quot;actors-model&quot;&gt;Actors Model&lt;/h2&gt;&lt;p&gt;The actor model is an architecturally different approach. Consider actors as classes with additional restrictions. Ideologically, code inside actors &lt;strong&gt;cannot be executed concurrently&lt;/strong&gt;, therefore actors can safely modify their state.&lt;/p&gt;&lt;blockquote&gt;In the world of chaos (concurrent) consider actors as a safe space&lt;/blockquote&gt;&lt;p&gt;Also, other instances cannot modify the actor&apos;s state from the outside. Thus, ensuring the safety of accesses.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-purple&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💥&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;All in all, actors let you safely share information between concurrent contexts&lt;/div&gt;&lt;/div&gt;&lt;h2 id=&quot;using-actors-in-swift&quot;&gt;Using Actors in Swift&lt;/h2&gt;&lt;p&gt;Luckily, we do not need to implement the actor model ourselves. Starting from &lt;strong&gt;Swift 5.7&lt;/strong&gt;, actors are available as part of Swift concurrency.&lt;/p&gt;&lt;p&gt;Actors are defined with &lt;code&gt;actor&lt;/code&gt; keyword.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;actor Counter {
	private(set) var counter = 0
    
	func increment() {
		counter += 1
	}
}&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Like classes, actors are&lt;b&gt;&lt;strong style=&quot;white-space: pre-wrap;&quot;&gt; reference types&lt;/strong&gt;&lt;/b&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;Generally, all access to actors may be suspended and require &lt;code&gt;await&lt;/code&gt; keyword.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-purple&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💥&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;If you&apos;re unfamiliar with Swift concurrency, check out my quick guide!&lt;/div&gt;&lt;/div&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://alexdremov.me/quick-guide-to-async-await-in-swift/&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;Quick Guide to Async Await in Swift | Alex Dremov&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;Everything you need to know about new Swift asynchronous features. Async await, main actor, task, async get, and possible use cases — all covered.&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://alexdremov.me/assets/icons/apple-touch-icon.png?v=eef9b14b42&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;Alex Dremov&lt;/span&gt;&lt;span class=&quot;kg-bookmark-publisher&quot;&gt;Alex Dremov&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2022/04/slide_17.jpg&quot; alt=&quot;&quot; onerror=&quot;this.style.display = &apos;none&apos;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;p&gt;Now, according to the defined model, an actor represents an isolated state. Therefore, we cannot directly execute code inside the actor or change its state because some other task can already be changing the actor&apos;s state.&lt;/p&gt;&lt;p&gt;We want to mitigate data races!&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;let counter = Counter()
let queue = DispatchQueue.global()

// Used only to wait for all tasks to complete
let group = DispatchGroup()

for _ in 1...100500 {
    group.enter()
    
    queue.async {
    	// async calls can be executed only in
        // appropriate concurrent environment, so
        // we spawn a new task
        Task.detached {
            await counter.increment()
            group.leave()
        }
    }
}

group.wait()
Task {
    print(&quot;Final value: \(await counter.counter)&quot;)
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;As you see, all calls to methods of &lt;code&gt;Counter&lt;/code&gt; and even to its properties are asynchronous and marked with &lt;code&gt;await&lt;/code&gt; keyword.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Notice that &lt;code spellcheck=&quot;false&quot; style=&quot;white-space: pre-wrap;&quot;&gt;await&lt;/code&gt; is not needed inside the actor&apos;s method. That&apos;s because the actor&apos;s methods are already inside an isolated state&lt;/div&gt;&lt;/div&gt;&lt;h3 id=&quot;nonisolated-members&quot;&gt;Nonisolated Members&lt;/h3&gt;&lt;p&gt;All members of actors are by default isolated. Actors also can have non-isolated members. Access to them is the same as if actor was a regular class. Notice, though, that nonisolated methods cannot directly access isolated members.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-red&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;😡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Stored non-constant properties cannot be &lt;code spellcheck=&quot;false&quot; style=&quot;white-space: pre-wrap;&quot;&gt;nonisolated&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Constant properties ( &lt;code spellcheck=&quot;false&quot; style=&quot;white-space: pre-wrap;&quot;&gt;let&lt;/code&gt; ) are &lt;code spellcheck=&quot;false&quot; style=&quot;white-space: pre-wrap;&quot;&gt;nonisolated&lt;/code&gt; by default, as they cannot provoke a data race&lt;/div&gt;&lt;/div&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;actor Counter {
    let id = UUID()
    private(set) var counter: Int = 0
    
    private nonisolated var description: String {
        &quot;Counter&quot;
    }
    
    func increment() {
        counter += 1
    }
    
    nonisolated func getDescription() -&amp;gt; String {
        return description
    }
}

...

print(counter.getDescription()) // no await
print(counter.id) // no await&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;difference-to-locks&quot;&gt;Difference to Locks&lt;/h2&gt;&lt;p&gt;One may ask&lt;/p&gt;&lt;blockquote&gt;How&apos;s it different from taking a lock before executing code inside an actor and releasing a lock on an exit?&lt;/blockquote&gt;&lt;p&gt;The difference is noticeable if actor itself runs asynchronous operations inside it. For example, if it messages another actor.&lt;/p&gt;&lt;p&gt;Take a look&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;actor Ping {
    let pong = Pong()
    
    func run() async {
        print(&quot;ping!&quot;)
        await pong.run() // Suspension point
        
        // While pong.run() is waited, other tasks
        // can enter this actor
    }
}

actor Pong {
    func run() async {
        try! await Task.sleep(for: .seconds(1)) // sleeping a bit
        print(&quot;pong!&quot;)
    }
}

let ping = Ping()
Task {
    await ping.run()
}

Task{
    await ping.run()
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This code outputs&lt;/p&gt;&lt;pre&gt;&lt;code&gt;ping!
ping!
pong!
pong!&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Notice that another actor is also called using &lt;code&gt;await&lt;/code&gt; keyword. I marked this place as a suspension point. The current task is suspended while waiting for an asynchronous task, &lt;strong&gt;so the actor is free for entrance again.&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;That&apos;s the core difference to a simple mutex or lock, and it is called &lt;strong&gt;Actor Reentrancy&lt;/strong&gt;. Some consider this a problem. However, it is an awesome optimization at expense of complicating code a bit.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-purple&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💥&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Mind about actor reentrancy! It is incorrect to make assumptions about an actor&apos;s state after an &lt;code spellcheck=&quot;false&quot; style=&quot;white-space: pre-wrap;&quot;&gt;await&lt;/code&gt; call inside an actor&lt;/div&gt;&lt;/div&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;actor Door {
    private var _open = false
    
    func open() async {
        _open = true
        
        await someTask() // Suspension point
        
        // Mistake! Door could have been closed
        // while someTask was executing
        print(&quot;Door is open&quot;)
    }
    
    func close() {
        _open = false
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Luckily, suspension points are all marked with &lt;code&gt;await&lt;/code&gt; keyword, so it is easy to keep track of them&lt;/p&gt;&lt;h2 id=&quot;final-notes&quot;&gt;Final Notes&lt;/h2&gt;&lt;p&gt;Actors are a great solution to data races. They nicely integrate into Swift concurrency. Keep in mind, though, that actor reentrancy must be taken into account to avoid incorrect state assumptions.&lt;/p&gt;&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://developer.apple.com/documentation/swift/actor?ref=alexdremov.me&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;Actor | Apple Developer Documentation&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;Common protocol to which all actors conform.&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://alexdremov.me/content/images/icon/favicon-3c79dfa1-48ef-46a3-bbde-64d063cb3300.ico&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;Apple Developer Documentation&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/thumbnail/developer-og-30d7c182-5491-446c-9161-b8a09d50db54.jpg&quot; alt=&quot;&quot; onerror=&quot;this.style.display = &apos;none&apos;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://docs.swift.org/swift-book/documentation/the-swift-programming-language/concurrency/?ref=alexdremov.me&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;Documentation&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://alexdremov.me/content/images/icon/favicon-c4b4e562-5389-4d30-a24c-d2801072f84e.ico&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;Swift.org&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;</content:encoded></item><item><title>Dive into Swift&apos;s Memory Management</title><link>https://alexdremov.me/dive-into-swifts-memory-management/</link><guid isPermaLink="true">https://alexdremov.me/dive-into-swifts-memory-management/</guid><description>Swift uses ARC to track and deallocate unused objects. Learn about the three types of reference counts and how ARC works — in this detailed post.</description><pubDate>Sun, 08 Jan 2023 19:33:03 GMT</pubDate><content:encoded>&lt;p&gt;In this post, I&apos;ll explore how Swift&apos;s memory management works under the hood, and how the memory modifiers: &lt;code&gt;unowned&lt;/code&gt; and &lt;code&gt;weak&lt;/code&gt;, affect an object&apos;s lifetime. You&apos;ll get a deeper understanding of how Swift manages objects&apos; lifetime internally.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-purple&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💥&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Swift memory management is one of the basic interview questions. It was asked &lt;b&gt;&lt;strong style=&quot;white-space: pre-wrap;&quot;&gt;in every&lt;/strong&gt;&lt;/b&gt; iOS developer interview I&apos;ve ever been to&lt;/div&gt;&lt;/div&gt;&lt;h2 id=&quot;memory-management&quot;&gt;Memory Management&lt;/h2&gt;&lt;p&gt;For example, in C, only the developer is in charge of deallocating unused objects. This can lead to memory leaks, double deallocations, or the use of invalid memory areas.&lt;/p&gt;&lt;p&gt;We don&apos;t want this.&lt;/p&gt;&lt;p&gt;Swift uses automatic reference counting (ARC) under the hood to deduce objects&apos; lifetime and automatically deallocate unused objects. Swift has &lt;strong&gt;three different types of the reference count. &lt;/strong&gt;They count how many other instances use an object. And when it is not needed, it is deallocated.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;This guide will progress from a general overview to the internals of ARC. Even if you&apos;re familiar with Swift&apos;s memory management, there&apos;s a high chance that you will learn something new&lt;/div&gt;&lt;/div&gt;&lt;h2 id=&quot;strong-reference&quot;&gt;Strong Reference&lt;/h2&gt;&lt;p&gt;The counter that is responsible for deallocation is a &lt;strong&gt;strong reference counter (RC). &lt;/strong&gt;The strong RC counts strong references to the object. When the strong RC reaches zero the object is deinited.&lt;/p&gt;&lt;p&gt;A strong reference is just a regular object usage. Creating a variable, or a constant, or saving a reference to an object in another object&apos;s property — they all create a strong reference.&lt;/p&gt;&lt;p&gt;Why a developer should even care about reference counting? Seems like a low-level implementation detail that is not important. &lt;strong&gt;But actually, it&apos;s crucial.&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Take a look at this example&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;class Person {
    let name: String
    init(name: String) { self.name = name }
    var apartment: Apartment?
    deinit { print(&quot;\(name) is being deinitialized&quot;) }
}

class Apartment {
    let unit: String
    init(unit: String) { self.unit = unit }
    var tenant: Person?
    deinit { print(&quot;Apartment \(unit) is being deinitialized&quot;) }
}

var john: Person? = Person(name: &quot;John Appleseed&quot;)
var unit4A: Apartment? = Apartment(unit: &quot;4A&quot;)

john!.apartment = unit4A // Person -&amp;gt; Apartment: strong reference
unit4A!.tenant = john // Apartment -&amp;gt; Person: strong reference

john = nil // Person is no longer needed
unit4A = nil // Apartment is no longer needed&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In the above example, the &lt;code&gt;Person&lt;/code&gt; and &lt;code&gt;Apartment&lt;/code&gt; objects have a strong reference to each other, creating a &lt;strong&gt;retain cycle&lt;/strong&gt;. As a result, when you set both &lt;code&gt;john&lt;/code&gt; and &lt;code&gt;unit4A&lt;/code&gt; to &lt;code&gt;nil&lt;/code&gt;, the &lt;strong&gt;deinitializers are not called and the objects are not deallocated.&lt;/strong&gt;&lt;/p&gt;&lt;figure class=&quot;kg-card kg-image-card&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2023/01/strongRef.png&quot; class=&quot;kg-image&quot; alt=&quot;Retain cycle image&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;800&quot; srcset=&quot;https://alexdremov.me/content/images/size/w600/2023/01/strongRef.png 600w, https://alexdremov.me/content/images/size/w1000/2023/01/strongRef.png 1000w, https://alexdremov.me/content/images/2023/01/strongRef.png 1280w&quot; sizes=&quot;(min-width: 720px) 720px&quot;&gt;&lt;/figure&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-purple&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💥&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;This situation is called a &lt;b&gt;&lt;strong style=&quot;white-space: pre-wrap;&quot;&gt;memory leak&lt;/strong&gt;&lt;/b&gt;. In Swift, it occurs only in the case of a &lt;b&gt;&lt;strong style=&quot;white-space: pre-wrap;&quot;&gt;retain cycle.&lt;/strong&gt;&lt;/b&gt; Two objects depend on each other and they will never be deallocated.&lt;/div&gt;&lt;/div&gt;&lt;p&gt;That&apos;s where memory management modifiers come in handy.&lt;/p&gt;&lt;h2 id=&quot;weak-reference&quot;&gt;Weak Reference&lt;/h2&gt;&lt;p&gt;One of the solutions to the problem of a retain cycle is a &lt;strong&gt;weak reference.&lt;/strong&gt; It is created using the &lt;code&gt;weak&lt;/code&gt; modifier like that:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;let person = Person(name: &quot;John Appleseed&quot;) // person is a strong reference
weak var weakPerson = person // weak reference to the same object&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Weak var &lt;strong&gt;always has an optional type&lt;/strong&gt; and cannot be constant (&lt;code&gt;let&lt;/code&gt;). That&apos;s because the object can be deallocated while it is still referenced by a weak variable. In this case, the variable is automatically set to &lt;code&gt;nil&lt;/code&gt;.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Consider &lt;code spellcheck=&quot;false&quot; style=&quot;white-space: pre-wrap;&quot;&gt;weak&lt;/code&gt; reference like the one that needs an object but can go on correctly without it (using &lt;code spellcheck=&quot;false&quot; style=&quot;white-space: pre-wrap;&quot;&gt;nil&lt;/code&gt;), allowing it to deallocate when nobody else needs it&lt;/div&gt;&lt;/div&gt;&lt;p&gt;Let&apos;s take a look at the solution to the problem above using the &lt;code&gt;weak&lt;/code&gt; modifier:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;class Person {
    let name: String
    init(name: String) { self.name = name }
    var apartment: Apartment?
    deinit { print(&quot;\(name) is being deinitialized&quot;) }
}

class Apartment {
    let unit: String
    init(unit: String) { self.unit = unit }
    
    weak var tenant: Person?
    
    deinit { print(&quot;Apartment \(unit) is being deinitialized&quot;) }
}

var john: Person? = Person(name: &quot;John Appleseed&quot;)
var unit4A: Apartment? = Apartment(unit: &quot;4A&quot;)

john!.apartment = unit4A // Person -&amp;gt; Apartment: strong reference
unit4A!.tenant = john // Apartment -&amp;gt; Person: weak reference

john = nil
unit4A = nil&lt;/code&gt;&lt;/pre&gt;&lt;figure class=&quot;kg-card kg-image-card&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2023/01/weakRef.png&quot; class=&quot;kg-image&quot; alt=&quot;Strong and weak reference image&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;800&quot; srcset=&quot;https://alexdremov.me/content/images/size/w600/2023/01/weakRef.png 600w, https://alexdremov.me/content/images/size/w1000/2023/01/weakRef.png 1000w, https://alexdremov.me/content/images/2023/01/weakRef.png 1280w&quot; sizes=&quot;(min-width: 720px) 720px&quot;&gt;&lt;/figure&gt;&lt;p&gt;Now, retain cycle is no longer here. At first, the &lt;code&gt;Person&lt;/code&gt; object is deallocated because it has no strong references to it. Then, the &lt;code&gt;Apartment&lt;/code&gt; object is deallocated.&lt;/p&gt;&lt;p&gt;No memory leak!&lt;/p&gt;&lt;p&gt;That&apos;s it. That is how you break retention cycles in Swift. There is one more modifier that can help you with that.&lt;/p&gt;&lt;h2 id=&quot;unowned-reference&quot;&gt;Unowned Reference&lt;/h2&gt;&lt;p&gt;An &lt;code&gt;unowned&lt;/code&gt; reference is very similar to a &lt;code&gt;weak&lt;/code&gt; reference cause it also does not increase a strong reference count. The difference is that it&apos;s up to the developer to not use an invalid object.&lt;/p&gt;&lt;p&gt;Unowned variables &lt;strong&gt;can be constant or non-optional. &lt;/strong&gt;When an object is deallocated, &lt;strong&gt;ARC does not set the unowned reference’s value to &lt;code&gt;nil&lt;/code&gt;&lt;/strong&gt;. However, if you try to access a deallocated object, you will catch a runtime error.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-red&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;😡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Use an unowned reference only when you are sure that the reference always refers to an instance that has not been deallocated&lt;/div&gt;&lt;/div&gt;&lt;p&gt;Here&apos;s a similar example:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;class Customer {
    let name: String
    var card: CreditCard?
    init(name: String) {
        self.name = name
    }
    deinit { print(&quot;\(name) is being deinitialized&quot;) }
}

class CreditCard {
    let number: UInt64
    unowned let customer: Customer
    init(number: UInt64, customer: Customer) {
        self.number = number
        self.customer = customer
    }
    deinit { print(&quot;Card #\(number) is being deinitialized&quot;) }
}

var john: Customer? = Customer(name: &quot;John Appleseed&quot;)
john!.card = CreditCard(number: 1234_5678_9012_3456, customer: john!)

john = nil // No retain cycle, both objects are deallocated&lt;/code&gt;&lt;/pre&gt;
&lt;!--kg-card-begin: html--&gt;
&lt;section class=&quot;custom-replace&quot; data-replace=&quot;.subscription-article&quot;&gt;
    &lt;a class=&quot;gh-portal-triggerbtn-container&quot;&gt;Subscribe and don&apos;t miss posts!&lt;/a&gt;
&lt;/section&gt;
&lt;!--kg-card-end: html--&gt;
&lt;h2 id=&quot;three-reference-counters&quot;&gt;Three Reference Counters&lt;/h2&gt;&lt;p&gt;So, how does all this magic works inside? &lt;a href=&quot;https://github.com/apple/swift/blob/main/stdlib/public/SwiftShims/swift/shims/RefCount.h?ref=alexdremov.me&quot;&gt;Swift sources&lt;/a&gt; have an amazing detailed description of all processes under the hood. &lt;/p&gt;&lt;p&gt;The &lt;strong&gt;strong RC&lt;/strong&gt; counts strong references to the object. When the strong RC reaches zero the object is deinited, unowned reference reads become errors, and weak reference reads become nil. The strong RC is stored as an extra count: when the physical field is 0 the logical value is 1.&lt;/p&gt;&lt;p&gt;The &lt;strong&gt;unowned RC&lt;/strong&gt; counts unowned references to the object. The unowned RC also has an extra &lt;code&gt;+1&lt;/code&gt; on behalf of the strong references; this &lt;code&gt;+1&lt;/code&gt; is decremented after deinit completes. When the unowned RC reaches zero the object&apos;s allocation is freed.&lt;/p&gt;&lt;p&gt;The &lt;strong&gt;weak RC&lt;/strong&gt; counts weak references to the object. The weak RC also has an extra &lt;code&gt;+1&lt;/code&gt; on behalf of the unowned references; this &lt;code&gt;+1&lt;/code&gt; is decremented after the object&apos;s allocation is freed. When the weak RC reaches zero the object&apos;s side table entry is freed.&lt;/p&gt;&lt;p&gt;But what is a side table and why is it needed?&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-purple&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💥&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;What&apos;s side table is another popular interview question, usually more advanced&lt;/div&gt;&lt;/div&gt;&lt;h2 id=&quot;side-table&quot;&gt;Side Table&lt;/h2&gt;&lt;p&gt;An object conceptually has three refcounts. These refcounts are stored either &quot;inline&quot; or in a &quot;side table entry&quot; pointed to by the internal field. You cannot access these fields from Swift directly&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;class User {
	var id: Int
    var name: String
    
    init(id: Int, name: String) {
    	self.id = id
        self.name = name
    }
}

let user = User(id: 0, name: &quot;John&quot;)&lt;/code&gt;&lt;/pre&gt;&lt;figure class=&quot;kg-card kg-image-card&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2023/01/Screenshot-2023-01-08-at-15.12.40.png&quot; class=&quot;kg-image&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;1746&quot; height=&quot;914&quot; srcset=&quot;https://alexdremov.me/content/images/size/w600/2023/01/Screenshot-2023-01-08-at-15.12.40.png 600w, https://alexdremov.me/content/images/size/w1000/2023/01/Screenshot-2023-01-08-at-15.12.40.png 1000w, https://alexdremov.me/content/images/size/w1600/2023/01/Screenshot-2023-01-08-at-15.12.40.png 1600w, https://alexdremov.me/content/images/2023/01/Screenshot-2023-01-08-at-15.12.40.png 1746w&quot; sizes=&quot;(min-width: 720px) 720px&quot;&gt;&lt;/figure&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Remember that unowned has &lt;code spellcheck=&quot;false&quot; style=&quot;white-space: pre-wrap;&quot;&gt;+1&lt;/code&gt; on behalf of strong reference and weak has &lt;code spellcheck=&quot;false&quot; style=&quot;white-space: pre-wrap;&quot;&gt;+1&lt;/code&gt; on behalf of unowned references&lt;/div&gt;&lt;/div&gt;&lt;p&gt;Objects initially start with no side table. They can gain a side table when a weak reference is formed.&lt;/p&gt;&lt;p&gt;Gaining a side table entry is a one-way operation; an object with a side table entry never loses it. This prevents some thread races.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;weak var weakUser = user // Side table implicitly created&lt;/code&gt;&lt;/pre&gt;&lt;figure class=&quot;kg-card kg-image-card kg-card-hascaption&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2023/01/Screenshot-2023-01-08-at-15.20.34.png&quot; class=&quot;kg-image&quot; alt=&quot;A side table is created&quot; loading=&quot;lazy&quot; width=&quot;1248&quot; height=&quot;1160&quot; srcset=&quot;https://alexdremov.me/content/images/size/w600/2023/01/Screenshot-2023-01-08-at-15.20.34.png 600w, https://alexdremov.me/content/images/size/w1000/2023/01/Screenshot-2023-01-08-at-15.20.34.png 1000w, https://alexdremov.me/content/images/2023/01/Screenshot-2023-01-08-at-15.20.34.png 1248w&quot; sizes=&quot;(min-width: 720px) 720px&quot;&gt;&lt;figcaption&gt;&lt;span style=&quot;white-space: pre-wrap;&quot;&gt;A side table is created&lt;/span&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;Strong and unowned variables point at the object. Weak variables point at the object&apos;s side table.&lt;/p&gt;&lt;p&gt;This idea is fundamental to understanding how &lt;code&gt;weak&lt;/code&gt; references work. By pointing not to the object but to the side table, the object itself can be deinitialized and fully deallocated.&lt;/p&gt;&lt;h2 id=&quot;weak-and-unowned-deep-differences&quot;&gt;Weak and Unowned. Deep Differences&lt;/h2&gt;&lt;p&gt;Now, by looking at the implementation we can notice important differences between &lt;code&gt;weak&lt;/code&gt; and &lt;code&gt;unowned&lt;/code&gt;. &lt;/p&gt;&lt;h3 id=&quot;performance&quot;&gt;Performance&lt;/h3&gt;&lt;p&gt;Using &lt;code&gt;unowned&lt;/code&gt; introduces less overhead than using &lt;code&gt;weak&lt;/code&gt;. That&apos;s because &lt;code&gt;weak&lt;/code&gt; variables reference the object through a side table. This means that there&apos;s one more pointer hop to reach the object.&lt;/p&gt;&lt;p&gt;Unowned references point directly to the object, so they do not have such overhead.&lt;/p&gt;&lt;h3 id=&quot;deallocation-vs-deinitialization&quot;&gt;Deallocation vs deinitialization&lt;/h3&gt;&lt;p&gt;According to the sources, when the strong RC reaches zero the object is &lt;strong&gt;deinited. &lt;/strong&gt;And when the unowned RC reaches zero the &lt;strong&gt;object&apos;s allocation is freed&lt;/strong&gt;. &lt;/p&gt;&lt;p&gt;That means that object memory is not available for realocation until all unowned references disappear.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-purple&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💥&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;If an object holds a large amount of memory, its memory will not be available until the last unowned reference disappear.If lack of memory is a problem, consider using &lt;code spellcheck=&quot;false&quot; style=&quot;white-space: pre-wrap;&quot;&gt;weak&lt;/code&gt; reference because it allows objects to be fully deallocated even when there are alive &lt;code spellcheck=&quot;false&quot; style=&quot;white-space: pre-wrap;&quot;&gt;weak&lt;/code&gt; references.&lt;/div&gt;&lt;/div&gt;&lt;h2 id=&quot;common-problems&quot;&gt;Common Problems&lt;/h2&gt;&lt;p&gt;The example of &lt;code&gt;Person&lt;/code&gt; and &lt;code&gt;Apartment&lt;/code&gt; retain cycle can be trivial. It&apos;s important to know about common cases when retain cycle appears.&lt;/p&gt;&lt;h3 id=&quot;closures-strong-capture-and-self&quot;&gt;Closures, strong capture, and self&lt;/h3&gt;&lt;p&gt;By default, a closure expression captures constants and variables from its surrounding scope with strong references to those values.&lt;/p&gt;&lt;p&gt;As we&apos;ve already noted, uncontrollable strong references may create a retain cycle. An escaping closure that refers to &lt;code&gt;self&lt;/code&gt; needs special consideration if &lt;code&gt;self&lt;/code&gt; refers to an instance of a class. Capturing &lt;code&gt;self&lt;/code&gt; in an escaping closure makes it easy to accidentally create a strong reference cycle.&lt;/p&gt;&lt;p&gt;For example:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;class Person {
  var name: String
  var voice: Voice? = nil

  init(name: String) {
    self.name = name
    self.voice = Voice {
      print(&quot;I&apos;m \(self.name)&quot;)
    }
  }
  func say() { voice?.say() }
  deinit {
    print(&quot;Person deallocated&quot;)
  }
}

class Voice {
  var say: () -&amp;gt; ()
  init(say: @escaping () -&amp;gt; ()) { self.say = say }
  deinit {
    print(&quot;Voice deallocated&quot;)
  }
}

var person: Person? = Person(name: &quot;Alex&quot;)
person!.say()

person = nil&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Which outputs only this line — without &lt;code&gt;deinit&lt;/code&gt; prints&lt;/p&gt;&lt;pre&gt;&lt;code&gt;My name is Alex&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;What&apos;s going on here? Let&apos;s draw a strong references graph:&lt;/p&gt;&lt;figure class=&quot;kg-card kg-image-card kg-card-hascaption&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2023/01/Screenshot-2023-01-08-at-16.22.35.png&quot; class=&quot;kg-image&quot; alt=&quot;Retain cycle with closure&quot; loading=&quot;lazy&quot; width=&quot;1116&quot; height=&quot;744&quot; srcset=&quot;https://alexdremov.me/content/images/size/w600/2023/01/Screenshot-2023-01-08-at-16.22.35.png 600w, https://alexdremov.me/content/images/size/w1000/2023/01/Screenshot-2023-01-08-at-16.22.35.png 1000w, https://alexdremov.me/content/images/2023/01/Screenshot-2023-01-08-at-16.22.35.png 1116w&quot; sizes=&quot;(min-width: 720px) 720px&quot;&gt;&lt;figcaption&gt;&lt;span style=&quot;white-space: pre-wrap;&quot;&gt;Retain cycle with closure&lt;/span&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;And, as expected, there is a pretty notable strong reference cycle. The problem is in  the creation of the &lt;code&gt;Voice&lt;/code&gt; instance:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;self.voice = Voice {
	print(&quot;My name is \(self.name)&quot;)
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Here, &lt;code&gt;self&lt;/code&gt; is captured with a strong reference to the escaping closure. To solve that, we can capture &lt;code&gt;self&lt;/code&gt; with the &lt;code&gt;weak&lt;/code&gt; modifier:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;self.voice = Voice {[weak self] in
	guard let self = self else { return; }
	print(&quot;My name is \(self.name)&quot;)
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;With such modification, we receive an expected output:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;My name is Alex
Person deallocated
Voice deallocated&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-purple&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💥&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Do not use &lt;code spellcheck=&quot;false&quot; style=&quot;white-space: pre-wrap;&quot;&gt;weak self&lt;/code&gt; when it is not needed. Remember that strong reference is required so that object is not deallocated before it is needed.&lt;/div&gt;&lt;/div&gt;&lt;h2 id=&quot;final-notes&quot;&gt;Final notes&lt;/h2&gt;&lt;p&gt;If you want to achieve an even deeper understanding of ARC internals, definitely check the ARC source code. You can start with this amazing description of an object&apos;s lifetime state machine.&lt;/p&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://github.com/apple/swift/blob/3bac57d9ac20eb9a6e41fd3c32e8d6fb23e37a47/stdlib/public/SwiftShims/swift/shims/RefCount.h?ref=alexdremov.me#L112&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;swift/RefCount.h at 3bac57d9ac20eb9a6e41fd3c32e8d6fb23e37a47 · apple/swift&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;The Swift Programming Language. Contribute to apple/swift development by creating an account on GitHub.&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://github.com/fluidicon.png&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;GitHub&lt;/span&gt;&lt;span class=&quot;kg-bookmark-publisher&quot;&gt;apple&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://opengraph.githubassets.com/1b258a6f968eb7c1af3235e6e4954c458d8edcd16fdb3a7e0e477002d51f4095/apple/swift&quot; alt=&quot;&quot; onerror=&quot;this.style.display = &apos;none&apos;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;p&gt;Hope that this post was helpful to you. Feel free to leave a comment or to reach me through my social nets!&lt;/p&gt;&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://github.com/apple/swift/blob/main/stdlib/public/SwiftShims/swift/shims/RefCount.h?ref=alexdremov.me&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;swift/RefCount.h at main · apple/swift&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;The Swift Programming Language. Contribute to apple/swift development by creating an account on GitHub.&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://github.com/fluidicon.png&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;GitHub&lt;/span&gt;&lt;span class=&quot;kg-bookmark-publisher&quot;&gt;apple&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://opengraph.githubassets.com/1b258a6f968eb7c1af3235e6e4954c458d8edcd16fdb3a7e0e477002d51f4095/apple/swift&quot; alt=&quot;&quot; onerror=&quot;this.style.display = &apos;none&apos;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://medium.com/appcoda-tutorials/memory-management-in-swift-understanding-strong-weak-and-unowned-references-b80a06c82460?ref=alexdremov.me&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;Memory Management in Swift: Understanding Strong, Weak and Unowned References&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;Behind all the coding that we are doing, you probably have noticed some of your variables with the reference of strong, weak or unowned…&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://cdn-static-1.medium.com/_/fp/icons/Medium-Avatar-500x500.svg&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;AppCoda Tutorials&lt;/span&gt;&lt;span class=&quot;kg-bookmark-publisher&quot;&gt;AppCoda&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://miro.medium.com/max/1200/1*ky03wTVr4G93J_b1pi4VFQ.jpeg&quot; alt=&quot;&quot; onerror=&quot;this.style.display = &apos;none&apos;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://docs.swift.org/swift-book/documentation/the-swift-programming-language/automaticreferencecounting?ref=alexdremov.me&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;Documentation&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://alexdremov.me/content/images/icon/favicon-080bba1d-a206-471b-a088-0204225c8ba7.ico&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;Swift.org&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;</content:encoded></item><item><title>Data Binding in SwiftUI: Tips, Tricks, and Best Practices</title><link>https://alexdremov.me/data-binding-in-swiftui-tips-tricks-and-best-practices/</link><guid isPermaLink="true">https://alexdremov.me/data-binding-in-swiftui-tips-tricks-and-best-practices/</guid><description>Master data binding with this comprehensive tutorial. Learn how to use @State, @ObservedObject, @EnvironmentObject to create dynamic UI.</description><pubDate>Fri, 30 Dec 2022 13:16:07 GMT</pubDate><content:encoded>&lt;p&gt;Are you building an app with SwiftUI and wondering how to manage your app&apos;s state? Data binding is a powerful tool that can help you build dynamic and responsive interfaces.&lt;/p&gt;&lt;p&gt;In this tutorial, we&apos;ll explore how to use &lt;code&gt;@State&lt;/code&gt;, &lt;code&gt;@ObservedObject&lt;/code&gt;, and &lt;code&gt;@EnvironmentObject&lt;/code&gt;.&lt;/p&gt;&lt;h2 id=&quot;what-is-data-binding-in-swiftui&quot;&gt;What is data binding in SwiftUI?&lt;/h2&gt;&lt;p&gt;Data binding connects UI element to a piece of data in your app. When the data changes, the UI element automatically updates to reflect the new value, and when the user interacts with the element, the data updates to reflect the new input.&lt;/p&gt;&lt;p&gt;SwiftUI provides several tools for data binding: &lt;code&gt;@State&lt;/code&gt;, &lt;code&gt;@ObservedObject&lt;/code&gt;, and &lt;code&gt;@EnvironmentObject&lt;/code&gt;. These tools allow you to bind values, objects, and even global objects to your user interface.&lt;/p&gt;&lt;h2 id=&quot;how-to-use-state-to-bind-a-simple-value-to-your-user-interface&quot;&gt;How to use @State to bind a simple value to your user interface&lt;/h2&gt;&lt;p&gt;&lt;code&gt;@State&lt;/code&gt; is a property wrapper that allows you to bind a simple value, like a string or an integer, to your user interface. &lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-purple&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💥&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Strictly, &lt;code spellcheck=&quot;false&quot; style=&quot;white-space: pre-wrap;&quot;&gt;@State&lt;/code&gt; can be used to bind value-type objects only. So, any &lt;code spellcheck=&quot;false&quot; style=&quot;white-space: pre-wrap;&quot;&gt;struct&lt;/code&gt; also can be binded using &lt;code spellcheck=&quot;false&quot; style=&quot;white-space: pre-wrap;&quot;&gt;@State&lt;/code&gt;.&lt;/div&gt;&lt;/div&gt;&lt;p&gt;To use &lt;code&gt;@State&lt;/code&gt;, you first define a property with the &lt;code&gt;@State&lt;/code&gt; wrapper, and then use the property in your user interface as a usual. For example, here&apos;s how you might use &lt;code&gt;@State&lt;/code&gt; to bind a string to a text field:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;struct ContentView: View {
    @State private var name: String = &quot;&quot;
    
    var body: some View {
        VStack {
            TextField(&quot;Enter your name&quot;, text: $name)
            Text(&quot;Hello, \(name)!&quot;)
        }
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You may notice that &lt;code&gt;$name&lt;/code&gt; is used. It allows to access &lt;code&gt;projectedValue&lt;/code&gt; of the wrapper. In case of &lt;code&gt;@State&lt;/code&gt; it is &lt;code&gt;Binding&amp;lt;Type&amp;gt;&lt;/code&gt;. &lt;/p&gt;&lt;p&gt;Now, whenever name is changed, the UI updates automatically. And when the user modifies the text field, variable data gets updated too.&lt;/p&gt;&lt;h2 id=&quot;using-binding&quot;&gt;Using @Binding&lt;/h2&gt;&lt;p&gt;&lt;code&gt;@Binding&lt;/code&gt; is used when you want to bind a value or object &lt;strong&gt;that is owned by a different view&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;To use &lt;code&gt;@Binding&lt;/code&gt;, you first define a property with the &lt;code&gt;@Binding&lt;/code&gt; wrapper, and then pass the binding to another view as an argument. The other view can then use the binding to read and write the data from the original view.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;struct CustomTextField: View {
    @Binding var text: String
    
    var body: some View {
        HStack {
            Image(systemName: &quot;person.circle&quot;)
            TextField(&quot;Enter your name&quot;, text: $text)
        }
        .padding()
    }
}

struct ContentView: View {
    @State private var name: String = &quot;&quot;
    
    var body: some View {
        VStack {
            CustomTextField(text: $name)
            Text(&quot;Hello, \(name)!&quot;)
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You also can pass binding in &lt;code&gt;init&lt;/code&gt; using direct access to property wrapper through underscore.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;struct CustomTextField: View {
    @Binding var text: String
    
    init(text: Binding&amp;lt;String&amp;gt;) {
        self._text = text
    }
    
    var body: some View {
        HStack {
            Image(systemName: &quot;person.circle&quot;)
            TextField(&quot;Enter your name&quot;, text: $text)
        }
        .padding()
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-purple&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💥&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;You can view &lt;code spellcheck=&quot;false&quot; style=&quot;white-space: pre-wrap;&quot;&gt;@Binding&lt;/code&gt; as a channel that gets value from the source and sets value to the source. It does not own an object.&lt;/div&gt;&lt;/div&gt;&lt;p&gt;Therefore, &lt;code&gt;@Binding&lt;/code&gt; is great for the view decomposition as it allows to inject dependencies to subviews.&lt;/p&gt;&lt;p&gt;Read more about modular app architecture with SwiftUI in my previous post:&lt;/p&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://alexdremov.me/ios-app-as-a-microservice-using-swiftui-in-modular-app/&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;iOS App As a Microservice. Using SwiftUI in Modular App&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;The modular architecture is excellent. But how to implement it effectively with SwiftUI? From its core, SwiftUI is state-driven, and it can be tricky to modularize an app and define exact responsibility borders.&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://alexdremov.me/assets/icons/apple-touch-icon.png?v=812a8f874f&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;Alex Dremov&lt;/span&gt;&lt;span class=&quot;kg-bookmark-publisher&quot;&gt;Alex Dremov&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://images.unsplash.com/photo-1581291518633-83b4ebd1d83e?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=max&amp;amp;fm=jpg&amp;amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDJ8fGludGVyZmFjZXxlbnwwfHx8fDE2NjYxMjA1NzM&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;w=2000&quot; alt=&quot;&quot; onerror=&quot;this.style.display = &apos;none&apos;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;h2 id=&quot;how-to-use-observedobject-to-bind-a-class-to-your-user-interface&quot;&gt;How to use @ObservedObject to bind a class to your user interface&lt;/h2&gt;&lt;p&gt;&lt;code&gt;@ObservedObject&lt;/code&gt; allows you to bind a &lt;strong&gt;class&lt;/strong&gt; to your user interface. The class must conform to the &lt;code&gt;ObservableObject&lt;/code&gt; protocol and use the &lt;code&gt;@Published&lt;/code&gt; property wrapper for any properties that you want to bind to your user interface. When the object&apos;s &lt;code&gt;@Published&lt;/code&gt; properties change, the user interface updates.&lt;/p&gt;&lt;p&gt;Here&apos;s an example of how you might use &lt;code&gt;@ObservedObject&lt;/code&gt; to bind a &lt;code&gt;User&lt;/code&gt; object to a form:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;class User: ObservableObject {
    @Published var name: String = &quot;&quot;
    @Published var email: String = &quot;&quot;
    
    var someUntrackedValue = &quot;&quot;
}

struct ContentView: View {
    @ObservedObject private var user = User()
    
    var body: some View {
        VStack {
            TextField(&quot;Enter your name&quot;, text: $user.name)
            TextField(&quot;Enter your email&quot;, text: $user.email)
            Text(&quot;Hello, \(user.name)!&quot;)
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In this example, the &lt;code&gt;user&lt;/code&gt; property is bound to the text fields using the &lt;code&gt;$user.name&lt;/code&gt; and &lt;code&gt;$user.email&lt;/code&gt; syntax. When the user types in the text fields, the &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;email&lt;/code&gt; properties of the &lt;code&gt;User&lt;/code&gt; object update to reflect the new input, and the &lt;code&gt;Text&lt;/code&gt; view updates to show the new value.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-purple&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💥&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Mind that if you publish a reference type in &lt;code spellcheck=&quot;false&quot; style=&quot;white-space: pre-wrap;&quot;&gt;ObservableObject&lt;/code&gt;, then changes inside it will not be propagated.&lt;/div&gt;&lt;/div&gt;&lt;h2 id=&quot;how-to-use-environmentobject-to-bind-a-global-object-to-your-user-interface&quot;&gt;How to use @EnvironmentObject to bind a global object to your user interface&lt;/h2&gt;&lt;p&gt;EnvironmentObject allows you to bind a global object. The object must conform to the &lt;code&gt;ObservableObject&lt;/code&gt; protocol the same way as with &lt;code&gt;@ObservedObject&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;&lt;code&gt;@EnvironmentObject&lt;/code&gt; is particularly useful when you want to share data across multiple views in your app. For example, you might use &lt;code&gt;@EnvironmentObject&lt;/code&gt; to bind a &lt;code&gt;UserSettings&lt;/code&gt; object to your app&apos;s main view, like this:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;class UserSettings: ObservableObject {
    @Published var theme: String = &quot;light&quot;
    @Published var fontSize: Double = 16
}

struct ContentView: View {
    @EnvironmentObject var userSettings: UserSettings
    
    var body: some View {
        VStack {
            if userSettings.theme == &quot;light&quot; {
                Text(&quot;Light mode&quot;)
            } else {
                Text(&quot;Dark mode&quot;)
            }
            Text(&quot;Font size: \(userSettings.fontSize)&quot;)
        }
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You can pass the &lt;code&gt;@EnvironmentObject&lt;/code&gt; down to child views using the &lt;code&gt;environmentObject(_:)&lt;/code&gt; modifier. For example:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;struct ChildView: View {
    @EnvironmentObject var userSettings: UserSettings
    
    var body: some View {
        Text(&quot;Font size: \(userSettings.fontSize)&quot;)
    }
}

struct ContentView: View {
    @ObservedObject var userSettings: UserSettings
    
    var body: some View {
        VStack {
            if userSettings.theme == &quot;light&quot; {
                Text(&quot;Light mode&quot;)
            } else {
                Text(&quot;Dark mode&quot;)
            }
            ChildView()
        }.environmentObject(userSettings)
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;br&gt;However, I would suggest &lt;strong&gt;not using &lt;code&gt;@EnvironmentObject&lt;/code&gt; or only using it on a small scale&lt;/strong&gt;, as it introduces global dependencies and makes the app&apos;s architecture messier. I covered modular architecture principles in one of my previous posts:&lt;/p&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://alexdremov.me/ios-app-as-a-microservice-build-robust-app-architecture/&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;iOS App As a Microservice. Build Robust App Architecture&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;What will you choose: MVVM, MVC, VIPER? Those all are local and problem-specific architectures. But how to structure your app on a larger scale to make it scalable and well-organized?&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://alexdremov.me/assets/icons/apple-touch-icon.png&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;Alex Dremov&lt;/span&gt;&lt;span class=&quot;kg-bookmark-publisher&quot;&gt;Alex Dremov&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://images.unsplash.com/photo-1532622785990-d2c36a76f5a6?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=max&amp;amp;fm=jpg&amp;amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDV8fHN0cnVjdHVyZXxlbnwwfHx8fDE2NjMyMzA3ODU&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;w=2000&quot; alt=&quot;&quot; onerror=&quot;this.style.display = &apos;none&apos;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;h2 id=&quot;best-practices-for-using-data-binding-in-swiftui&quot;&gt;Best practices for using data binding in SwiftUI&lt;/h2&gt;&lt;p&gt;Here are a few best practices for using data binding in SwiftUI:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Use &lt;code&gt;@State&lt;/code&gt; for simple values that are specific to a single view.&lt;/li&gt;&lt;li&gt;Use &lt;code&gt;@ObservedObject&lt;/code&gt; for complex objects that you need to share with other parts of your app.&lt;/li&gt;&lt;li&gt;Use &lt;code&gt;@EnvironmentObject&lt;/code&gt; sparingly, as it can introduce global dependencies and make your app&apos;s architecture messier. Only use it when you need to share data across a small portion of your app and there is no cleaner way to do it.&lt;/li&gt;&lt;li&gt;Use &lt;code&gt;@Binding&lt;/code&gt; to create custom views with data binding. This allows you to reuse your views and keep your code more modular.&lt;/li&gt;&lt;li&gt;Organize and structure your data bindings in a logical and easy-to-maintain way. Use modular architecture principles to break your app into smaller, more manageable pieces.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;By following these best practices, you can create dynamic and responsive user interfaces in SwiftUI that stay in sync with your data and are easy to maintain and test.&lt;/p&gt;&lt;h2 id=&quot;tips-for-debugging-and-testing-your-data-bindings&quot;&gt;Tips for debugging and testing your data bindings&lt;/h2&gt;&lt;p&gt;Data binding can be a powerful tool, but it can also be a source of bugs and issues if you&apos;re not careful. Here are a few tips for debugging and testing your data bindings:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Make sure that your classes conform to the &lt;code&gt;ObservableObject&lt;/code&gt; protocol and use the &lt;code&gt;@Published&lt;/code&gt; property wrapper for any properties that you want to bind to your user interface.&lt;/li&gt;&lt;li&gt;Check that you use &lt;code&gt;@State&lt;/code&gt; for value type objects and &lt;code&gt;@ObservedObject&lt;/code&gt; for reference type. &lt;/li&gt;&lt;li&gt;Check that &lt;code&gt;@Published&lt;/code&gt; properties are value types&lt;/li&gt;&lt;li&gt;Use the debugging tools in Xcode to identify and fix issues in your data bindings. You can use the debugger to inspect the values of your bound properties and step through your code to see how the data is flowing through your app.&lt;/li&gt;&lt;li&gt;Use Xcode&apos;s preview feature to test your layouts and behaviors in real-time as you build your app. This can be a great way to catch issues with your data bindings early on and ensure that your user interface is working as expected.&lt;/li&gt;&lt;li&gt;Consider using unit tests to validate your data bindings and ensure that they are working correctly. You can use the &lt;code&gt;XCTest&lt;/code&gt; framework to write tests that verify the values of your bound properties and check that your user interface is behaving as expected.&lt;/li&gt;&lt;/ol&gt;&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;&lt;p&gt;Data binding is a powerful tool that allows you to create dynamic and responsive user interfaces that stay in sync with your data. &lt;/p&gt;&lt;p&gt;As you continue to develop your app, remember to keep your data bindings organized and well-structured to ensure that your app is easy to maintain and test. Use modular architecture principles to break your app into smaller, more manageable pieces, and be sure to test your data bindings thoroughly to catch any bugs or issues before you release your app.&lt;/p&gt;&lt;p&gt;With these tips in mind, you&apos;ll be well on your way to creating amazing apps with SwiftUI and data binding. Thanks for reading!&lt;/p&gt;&lt;h2 id=&quot;see-also&quot;&gt;See also&lt;/h2&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://alexdremov.me/ios-app-as-a-microservice-build-robust-app-architecture/&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;iOS App As a Microservice. Build Robust App Architecture&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;What will you choose: MVVM, MVC, VIPER? Those all are local and problem-specific architectures. But how to structure your app on a larger scale to make it scalable and well-organized?&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://alexdremov.me/assets/icons/apple-touch-icon.png&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;Alex Dremov&lt;/span&gt;&lt;span class=&quot;kg-bookmark-publisher&quot;&gt;Alex Dremov&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://images.unsplash.com/photo-1532622785990-d2c36a76f5a6?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=max&amp;amp;fm=jpg&amp;amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDV8fHN0cnVjdHVyZXxlbnwwfHx8fDE2NjMyMzA3ODU&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;w=2000&quot; alt=&quot;&quot; onerror=&quot;this.style.display = &apos;none&apos;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://alexdremov.me/ios-app-as-a-microservice-using-swiftui-in-modular-app/#whats-the-problem&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;iOS App As a Microservice. Using SwiftUI in Modular App&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;The modular architecture is excellent. But how to implement it effectively with SwiftUI? From its core, SwiftUI is state-driven, and it can be tricky to modularize an app and define exact responsibility borders.&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://alexdremov.me/assets/icons/apple-touch-icon.png&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;Alex Dremov&lt;/span&gt;&lt;span class=&quot;kg-bookmark-publisher&quot;&gt;Alex Dremov&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://images.unsplash.com/photo-1581291518633-83b4ebd1d83e?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=max&amp;amp;fm=jpg&amp;amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDJ8fGludGVyZmFjZXxlbnwwfHx8fDE2NjYxMjA1NzM&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;w=2000&quot; alt=&quot;&quot; onerror=&quot;this.style.display = &apos;none&apos;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://alexdremov.me/ios-app-as-a-microservice-modularize-your-app-with-tuist/&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;iOS App As a Microservice. Modularize Your App With Tuist&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;This is the second article in a series on modular app architecture. In this post, I will cover implementation details using Tuist&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://alexdremov.me/assets/icons/apple-touch-icon.png&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;Alex Dremov&lt;/span&gt;&lt;span class=&quot;kg-bookmark-publisher&quot;&gt;Alex Dremov&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://images.unsplash.com/photo-1613645695025-20e3f38de4a6?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=max&amp;amp;fm=jpg&amp;amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDJ8fG1vZHVsYXJ8ZW58MHx8fHwxNjY0OTk5NDQ5&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;w=2000&quot; alt=&quot;&quot; onerror=&quot;this.style.display = &apos;none&apos;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;</content:encoded></item><item><title>iOS App As a Microservice. Using SwiftUI in Modular App</title><link>https://alexdremov.me/ios-app-as-a-microservice-using-swiftui-in-modular-app/</link><guid isPermaLink="true">https://alexdremov.me/ios-app-as-a-microservice-using-swiftui-in-modular-app/</guid><description>How to implement modular architecture effectively with SwiftUI? In this post, I will describe tips on using SwiftUI with modular app design</description><pubDate>Wed, 19 Oct 2022 13:00:23 GMT</pubDate><content:encoded>&lt;p&gt;In this post, I will describe features of SwiftUI that work well in modular design and those that are better to avoid.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-purple&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💥&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;This is the third and the last post in the series on a modular architecture.Check out the previous issues to boost your understanding of critical concepts!&lt;/div&gt;&lt;/div&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://alexdremov.me/ios-app-as-a-microservice-build-robust-app-architecture/&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;iOS App As a Microservice. Build Robust App Architecture&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;What will you choose: MVVM, MVC, VIPER? Those all are local and problem-specific architectures. But how to structure your app on a larger scale to make it scalable and well-organized?&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://alexdremov.me/assets/icons/apple-touch-icon.png&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;Alex Dremov&lt;/span&gt;&lt;span class=&quot;kg-bookmark-publisher&quot;&gt;Alex Dremov&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://images.unsplash.com/photo-1532622785990-d2c36a76f5a6?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=max&amp;amp;fm=jpg&amp;amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDV8fHN0cnVjdHVyZXxlbnwwfHx8fDE2NjMyMzA3ODU&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;w=2000&quot; alt=&quot;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://alexdremov.me/ios-app-as-a-microservice-modularize-your-app-with-tuist/&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;iOS App As a Microservice. Modularize Your App With Tuist&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;This is the second article in a series on modular app architecture. In this post, I will cover implementation details using Tuist&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://alexdremov.me/assets/icons/apple-touch-icon.png&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;Alex Dremov&lt;/span&gt;&lt;span class=&quot;kg-bookmark-publisher&quot;&gt;Alex Dremov&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://images.unsplash.com/photo-1613645695025-20e3f38de4a6?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=max&amp;amp;fm=jpg&amp;amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDJ8fG1vZHVsYXJ8ZW58MHx8fHwxNjY0OTk5NDQ5&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;w=2000&quot; alt=&quot;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;h2 id=&quot;whats-the-problem&quot;&gt;What&apos;s The Problem&lt;/h2&gt;&lt;p&gt;Why SwiftUI use in modular design is different, and why do I need a whole new post for it? As I already mentioned, SwiftUI is state-driven and trying to avoid that leads to ineffective and messy solutions.&lt;/p&gt;&lt;p&gt;For example&lt;/p&gt;&lt;p&gt;Let&apos;s suggest that you have settings and homepage modules. Users can log out on the settings screen and your app needs to &lt;em&gt;handle&lt;/em&gt; this case correctly. The first intent is to pass a closure to the settings module that will be called on the logout button press. Sounds reasonable, right?&lt;/p&gt;&lt;p&gt;Ok, but how does it connect with SwiftUI? Notice that &lt;em&gt;handling &lt;/em&gt;action does not necessarily mean that there will be a change in state. There is a logical change, though. But how can SwiftUI know about that?&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;State-driven means that views are a function of the state. So, the only way to update the view is to change its state.&lt;/div&gt;&lt;/div&gt;&lt;h2 id=&quot;data-flow&quot;&gt;Data Flow&lt;/h2&gt;&lt;p&gt;Apple released a nice presentation on WWDC19 about the role of data in SwiftUI. The presentation covers cases where &lt;code&gt;@Binding&lt;/code&gt;, &lt;code&gt;@EnvironmentObject&lt;/code&gt;, etc. are the most applicable.&lt;/p&gt;&lt;figure class=&quot;kg-card kg-image-card kg-card-hascaption&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2022/10/Screenshot-2022-10-18-at-23.19.45.png&quot; class=&quot;kg-image&quot; alt=&quot;Apple WWDC19 — Swift Data Flow&quot; loading=&quot;lazy&quot; width=&quot;2000&quot; height=&quot;1185&quot; srcset=&quot;https://alexdremov.me/content/images/size/w600/2022/10/Screenshot-2022-10-18-at-23.19.45.png 600w, https://alexdremov.me/content/images/size/w1000/2022/10/Screenshot-2022-10-18-at-23.19.45.png 1000w, https://alexdremov.me/content/images/size/w1600/2022/10/Screenshot-2022-10-18-at-23.19.45.png 1600w, https://alexdremov.me/content/images/2022/10/Screenshot-2022-10-18-at-23.19.45.png 2000w&quot; sizes=&quot;(min-width: 720px) 720px&quot;&gt;&lt;figcaption&gt;&lt;span style=&quot;white-space: pre-wrap;&quot;&gt;Apple WWDC19 — Swift Data Flow&lt;/span&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;But also the crucial point is made — the view is not the result of a sequence of events, but rather a &lt;strong&gt;representation of data or state&lt;/strong&gt;. It&apos;s also essential where this data comes from. There should be a single source of truth.&lt;/p&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://developer.apple.com/videos/play/wwdc2019/226/?ref=alexdremov.me&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;Data Flow Through SwiftUI - WWDC19 - Videos - Apple Developer&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;SwiftUI was built from the ground up to let you write beautiful and correct user interfaces free of inconsistencies. Learn how to connect...&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://developer.apple.com/apple-logo.svg&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;Apple Developer&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://devimages-cdn.apple.com/wwdc-services/images/48/2828/2828_wide_250x141_2x.jpg&quot; alt=&quot;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;p&gt;Keeping this in mind, let&apos;s move on to the first tip that will solve the issue proposed in &lt;a href=&quot;#whats-the-problem&quot;&gt;the &quot;problem&quot; section&lt;/a&gt; of this article.&lt;/p&gt;&lt;h2 id=&quot;use-data-flows-and-not-callbacks&quot;&gt;Use Data Flows and Not Callbacks&lt;/h2&gt;&lt;p&gt;The problem with &lt;em&gt;handling &lt;/em&gt;the&lt;em&gt; &lt;/em&gt;logout&lt;em&gt; &lt;/em&gt;action is in the word &lt;em&gt;&lt;code&gt;handle&lt;/code&gt;&lt;/em&gt; itself. There is no explicit change in state and it&apos;s unknown who&apos;s responsible for changing the state if it is even defined. &lt;/p&gt;&lt;p&gt;So, if SwiftUI is state-driven, let&apos;s define the source of truth for this state. It must be a variable that stores the current &lt;code&gt;logged-in&lt;/code&gt; / &lt;code&gt;logged-out&lt;/code&gt; state. Depending on the state&apos;s complexity, it can be a bool, enum, or struct. &lt;/p&gt;&lt;p&gt;Singleton or global state? No.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-purple&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💥&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;As described in previous posts, &lt;b&gt;&lt;strong style=&quot;white-space: pre-wrap;&quot;&gt;dependencies should be explicit&lt;/strong&gt;&lt;/b&gt;.In this case, the logged-in / logged-out variable should be passed as a dependency to the settings module and to the homepage module.&lt;/div&gt;&lt;/div&gt;&lt;p&gt;But we need to listen for changes in this variable and update views respectively. Also, it&apos;s bad if every module can change this variable. There should be restrictions on which module can modify state and which can only read. &lt;/p&gt;&lt;h3 id=&quot;swiftui-combine-its-a-match&quot;&gt;SwiftUI + Combine. It&apos;s a Match&lt;/h3&gt;&lt;p&gt;You may already know that SwiftUI automatically listens for &lt;code&gt;ObservableObject&lt;/code&gt; changes and updates views when something is changed. So, we can create such a class:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;class LogInState: ObservableObject {
    @Published var isLoggedIn: Bool
    
    init(isLoggedIn: Bool) {
        self.isLoggedIn = isLoggedIn
    }
    
    func loggedOut() {
        isLoggedIn = false
    }
    
    func loggedIn() {
        isLoggedIn = true
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It later can be injected into a SwiftUI view as simple as that&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;struct MyView: View {
	@ObservedObject var logInState: LogInState

	var body: some View {
    	Text(logInState.isLoggedIn ? &quot;Yes&quot; : &quot;No&quot;)
    }
}

...
let logInState = LogInState(isLoggedIn: true)
HomePageModule(logInState: logInState)
...
SettingsModule(logInState: logInState)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Don&apos;t you think that creating such a distinct class for every state is bad? It may be fine for complex data types, but definitely not for a single boolean value.&lt;/p&gt;&lt;p&gt;Also, notice that both &lt;code&gt;HomePageModule&lt;/code&gt; and &lt;code&gt;SettingsModule&lt;/code&gt; can change the state. What if you have many more modules that depend on &lt;code&gt;logInState&lt;/code&gt;? They all could change it!&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-purple&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💥&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;If every part of your app can hypothetically change the shared state, then if a bug arises, you start playing an amazing game&quot;Who the hell changed this value?&quot;&lt;/div&gt;&lt;/div&gt;
&lt;!--kg-card-begin: html--&gt;
&lt;section class=&quot;custom-replace&quot; data-replace=&quot;.subscription-article&quot;&gt;
    &lt;a class=&quot;gh-portal-triggerbtn-container&quot;&gt;Subscribe and don&apos;t miss posts!&lt;/a&gt;
&lt;/section&gt;
&lt;!--kg-card-end: html--&gt;
&lt;h2 id=&quot;better-combine-use&quot;&gt;Better Combine Use&lt;/h2&gt;&lt;p&gt;Ok, we&apos;ve solved the problem with callbacks. Though we still have a problem with the boilerplate code needed to define a new &lt;code&gt;ObservableObject&lt;/code&gt;, and a problem with state modification privileges.&lt;/p&gt;&lt;p&gt;We can solve those by creating a custom ObservableObject!&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;You also can use third-party reactive frameworks, but I will cover implementation using Combine as it seamlessly integrates with SwiftUI&lt;/div&gt;&lt;/div&gt;&lt;p&gt;To use SwiftUI&apos;s automatic listening to updates, we need to conform to &lt;code&gt;ObservableObject&lt;/code&gt;. Here&apos;s a generic class to make any type observable. It also utilizes &lt;code&gt;@propertyWrapper&lt;/code&gt; and &lt;code&gt;@dynamicMemberLookup&lt;/code&gt; features.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;import Foundation
import Combine

@dynamicMemberLookup
@propertyWrapper
public class ObservableProperty&amp;lt;Output&amp;gt;: ObservableObject {
    @Published private var storedValue: Output
    
    public var wrappedValue: Output {
        get {
            storedValue
        }
        set {
            storedValue = newValue
        }
    }
    
    public init(wrappedValue initialValue: Output) {
        self.storedValue = initialValue
    }
    
    public subscript&amp;lt;Result&amp;gt;(dynamicMember keyPath: WritableKeyPath&amp;lt;Output, Result&amp;gt;) -&amp;gt; Result {
        get {
            storedValue[keyPath: keyPath]
        }
        set {
            storedValue[keyPath: keyPath] = newValue
        }
    }
    
    public subscript&amp;lt;Result&amp;gt;(dynamicMember keyPath: KeyPath&amp;lt;Output, Result&amp;gt;) -&amp;gt; Result {
        storedValue[keyPath: keyPath]
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It can be used as simply as that&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;struct MyView: View {
    @ObservedObject
    @ObservableProperty
    var logInState: Bool
    
    init(logInState: ObservableProperty&amp;lt;Bool&amp;gt;) {
        self._logInState = .init(initialValue: logInState)
    }
    
    var body: some View {
        VStack {
            Text(logInState ? &quot;Yes&quot; : &quot;No&quot;)
            Button(&quot;toggle&quot;) {
                logInState = !logInState
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-purple&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💥&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;However, ObservableProperty works with value types only. Passing reference types will not trigger updates&lt;/div&gt;&lt;/div&gt;&lt;h2 id=&quot;restrict-modules-to-read-only-variables&quot;&gt;Restrict Modules To Read-Only Variables&lt;/h2&gt;&lt;p&gt;In the example above, &lt;code&gt;MyView&lt;/code&gt; can modify the value. But how we can restrict it to read-only mode? We can create a similar class that will prohibit modification&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;@dynamicMemberLookup
@propertyWrapper
public class ObservableValue&amp;lt;Output&amp;gt;: ObservableObject {
    @Published private var storedValue: Output
    public var wrappedValue: Output {
        storedValue
    }

    public var value: Output {
        storedValue
    }

    public init(wrappedValue initialValue: Output) {
        fatalError(&quot;ObservableValue cannot be initialized with value. Use constant()&quot;)
    }

    init&amp;lt;Pub: Publisher&amp;lt;Output, Never&amp;gt;&amp;gt;(initialValue: Output, publisher: Pub) {
        storedValue = initialValue
        publisher.assign(to: &amp;amp;$storedValue)
    }

    public subscript&amp;lt;Result&amp;gt;(dynamicMember keyPath: WritableKeyPath&amp;lt;Output, Result&amp;gt;) -&amp;gt; Result {
        get {
            storedValue[keyPath: keyPath]
        }
        set {
            storedValue[keyPath: keyPath] = newValue
        }
    }

    public subscript&amp;lt;Result&amp;gt;(dynamicMember keyPath: KeyPath&amp;lt;Output, Result&amp;gt;) -&amp;gt; Result {
        storedValue[keyPath: keyPath]
    }

    public static func constant(initialValue: Output) -&amp;gt; ObservableValue&amp;lt;Output&amp;gt; {
        .init(
            initialValue: initialValue,
            publisher: Empty()
        )
    }

    public var publisher: Published&amp;lt;Output&amp;gt;.Publisher {
        $storedValue
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Then, we can add &lt;code&gt;projectedValue&lt;/code&gt; to &lt;code&gt;ObservableProperty&lt;/code&gt; to create &lt;code&gt;ObservableValue&lt;/code&gt; from it.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;public class ObservableProperty&amp;lt;Output&amp;gt;: ObservableObject {
	...
    public var publisher: AnyPublisher&amp;lt;Output, Never&amp;gt; {
        $storedValue.eraseToAnyPublisher()
    }
    
    public var projectedValue: ObservableValue&amp;lt;Output&amp;gt; {
        ObservableValue&amp;lt;Output&amp;gt;(
            initialValue: storedValue,
            publisher: publisher
        )
    }
	...
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Great!&lt;/p&gt;&lt;p&gt;Now we can create an observable source of truth, and pass it to modules, restricting some of them to read-only mode. Check out the example:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;struct ReadOnlyModule: View {
    @ObservedObject
    @ObservableValue
    var logInState: Bool
    
    init(logInState: ObservableValue&amp;lt;Bool&amp;gt;) {
        self._logInState = .init(wrappedValue: logInState)
    }
    
    var body: some View {
        Text(logInState ? &quot;Yes&quot; : &quot;No&quot;)
    }
}

struct ModifyModule: View {
    @ObservableProperty
    var logInState: Bool
    
    init(logInState: ObservableProperty&amp;lt;Bool&amp;gt;) {
        self._logInState = logInState
    }
    
    var body: some View {
        Button(&quot;toggle&quot;) {
            logInState = !logInState
        }
    }
}

struct MyView: View {
    @ObservableProperty
    var logInState: Bool
    
    init(logInState: ObservableProperty&amp;lt;Bool&amp;gt;) {
        self._logInState = logInState
    }
    
    var body: some View {
        VStack {
        	// projected read-only value (ObservableValue)
            ReadOnlyModule(logInState: $logInState)
            
            // ObservableProperty reference
            ModifyModule(logInState: _logInState)
        }
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;So, the callbacks problem is solved and we can move on to the next idea.&lt;/p&gt;&lt;h2 id=&quot;do-not-use-environmentobjects&quot;&gt;Do Not Use EnvironmentObjects&lt;/h2&gt;&lt;p&gt;Yes, I&apos;m this definite about it. Environment objects in their core are global variables that create implicit dependencies. Also, they are easily overlooked and can produce unexpected crashes when not set.&lt;/p&gt;&lt;p&gt;Apart from that, you can&apos;t set two environment objects of the same type and it results in messy decisions and code modifications.&lt;/p&gt;&lt;p&gt;And the third reason is that they simply don&apos;t work with dependency inversion. You cannot hide the environment object behind the protocol as only ObservableObject can be passed as an environment object.&lt;/p&gt;&lt;h2 id=&quot;go-for-programmatic-navigation&quot;&gt;Go For Programmatic Navigation &lt;/h2&gt;&lt;p&gt;SwiftUI is trying to introduce ways for implementing programmatic navigation, but it is not ready yet. Though, it&apos;s essential for modular architecture because of loose coupling.&lt;/p&gt;&lt;p&gt;There are frameworks that can be used to achieve that. I have a post on this topic. Check it out!&lt;/p&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://alexdremov.me/swiftui-navigation-is-a-mess-heres-what-you-can-do/&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;SwiftUI Navigation Is a Mess. Here’s What You Can Do&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;Managing navigation in pure SwiftUI is hard and leads to messy solutions. In this post, I will show you how you can manage views effectively&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://alexdremov.me/assets/icons/apple-touch-icon.png&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;Alex Dremov&lt;/span&gt;&lt;span class=&quot;kg-bookmark-publisher&quot;&gt;Alex Dremov&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://images.unsplash.com/photo-1597945161640-9366e6d4253b?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=max&amp;amp;fm=jpg&amp;amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDF8fE5hdmlnYXRpb258ZW58MHx8fHwxNjU5MjAzNjQy&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;w=2000&quot; alt=&quot;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;p&gt;Alternatively, you can use other open-source solutions. For example, I recently found a similar framework:&lt;/p&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://github.com/johnpatrickmorgan/FlowStacks?ref=alexdremov.me&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;GitHub - johnpatrickmorgan/FlowStacks: FlowStacks allows you to hoist SwiftUI navigation and presentation state into a Coordinator&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;FlowStacks allows you to hoist SwiftUI navigation and presentation state into a Coordinator - GitHub - johnpatrickmorgan/FlowStacks: FlowStacks allows you to hoist SwiftUI navigation and presentati...&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://github.com/fluidicon.png&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;GitHub&lt;/span&gt;&lt;span class=&quot;kg-bookmark-publisher&quot;&gt;johnpatrickmorgan&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://opengraph.githubassets.com/c4f40a4363f317ae1c8fb69c0fd9a888dcfd7718b0be9c0afaa7f3ccfe2f669d/johnpatrickmorgan/FlowStacks&quot; alt=&quot;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;p&gt;As always, let me know what you think in the comments!&lt;/p&gt;&lt;h2 id=&quot;references&quot;&gt;References &lt;/h2&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://developer.apple.com/videos/play/wwdc2019/226/?ref=alexdremov.me&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;Data Flow Through SwiftUI - WWDC19 - Videos - Apple Developer&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;SwiftUI was built from the ground up to let you write beautiful and correct user interfaces free of inconsistencies. Learn how to connect...&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://developer.apple.com/apple-logo.svg&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;Apple Developer&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://devimages-cdn.apple.com/wwdc-services/images/48/2828/2828_wide_250x141_2x.jpg&quot; alt=&quot;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://developer.apple.com/documentation/combine?ref=alexdremov.me&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;Apple Developer Documentation&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://developer.apple.com/apple-logo.svg&quot; alt=&quot;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;</content:encoded></item><item><title>iOS App As a Microservice. Modularize Your App With Tuist</title><link>https://alexdremov.me/ios-app-as-a-microservice-modularize-your-app-with-tuist/</link><guid isPermaLink="true">https://alexdremov.me/ios-app-as-a-microservice-modularize-your-app-with-tuist/</guid><description>I will cover implementation details using Tuist. It is an excellent CLI that helps you generate, maintain and interact with Xcode projects</description><pubDate>Fri, 07 Oct 2022 10:14:04 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;Tuist &lt;/strong&gt;is an excellent command line tool that helps you generate, maintain and interact with Xcode projects.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-purple&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💥&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;I covered the core ideas of modular architecture in the previous post. Check it out if you haven&apos;t yet!&lt;/div&gt;&lt;/div&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://alexdremov.me/ios-app-as-a-microservice-build-robust-app-architecture/&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;iOS App As a Microservice. Build Robust App Architecture&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;What will you choose: MVVM, MVC, VIPER? Those all are local and problem-specific architectures. But how to structure your app on a larger scale to make it scalable and well-organized?&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://alexdremov.me/assets/icons/apple-touch-icon.png&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;Alex Dremov&lt;/span&gt;&lt;span class=&quot;kg-bookmark-publisher&quot;&gt;Alex Dremov&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://images.unsplash.com/photo-1532622785990-d2c36a76f5a6?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=max&amp;amp;fm=jpg&amp;amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDV8fHN0cnVjdHVyZXxlbnwwfHx8fDE2NjMyMzA3ODU&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;w=2000&quot; alt=&quot;&quot; onerror=&quot;this.style.display = &apos;none&apos;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;h2 id=&quot;what%E2%80%99s-next&quot;&gt;What’s next?&lt;/h2&gt;&lt;p&gt;In the next and last post in this series, I will cover implementation tips with SwiftUI. Subscribe so you don’t miss it&lt;br&gt;&lt;strong&gt;UPD: &lt;/strong&gt;now available&lt;/p&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://alexdremov.me/ios-app-as-a-microservice-using-swiftui-in-modular-app/#whats-the-problem&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;iOS App As a Microservice. Using SwiftUI in Modular App&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;The modular architecture is excellent. But how to implement it effectively with SwiftUI? From its core, SwiftUI is state-driven, and it can be tricky to modularize an app and define exact responsibility borders.&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://alexdremov.me/assets/icons/apple-touch-icon.png&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;Alex Dremov&lt;/span&gt;&lt;span class=&quot;kg-bookmark-publisher&quot;&gt;Alex Dremov&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://images.unsplash.com/photo-1581291518633-83b4ebd1d83e?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=max&amp;amp;fm=jpg&amp;amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDJ8fGludGVyZmFjZXxlbnwwfHx8fDE2NjYxMjA1NzM&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;w=2000&quot; alt=&quot;&quot; onerror=&quot;this.style.display = &apos;none&apos;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;h2 id=&quot;why-tuist&quot;&gt;Why Tuist?&lt;/h2&gt;&lt;p&gt;It encourages you to further code modularization as it provides an elegant way to create separate Xcode projects for different modules, making tight coupling or implicit dependencies less viable&lt;/p&gt;&lt;p&gt;Also, it&apos;s&lt;strong&gt; great for teamwork.&lt;/strong&gt; Have you tried to commit an Xcode project to a VCS like GitHub?&lt;/p&gt;&lt;p&gt;It&apos;s a mess&lt;/p&gt;&lt;p&gt;Diff of the modified Xcode project is not human-readable. It&apos;s simply impossible to trace changes or review a PR. What if you could define the Xcode project in a simple config file? Tuist does that. Moreover, &lt;strong&gt;tuist&lt;/strong&gt; &lt;strong&gt;config files are written in Swift&lt;/strong&gt;.&lt;/p&gt;&lt;h2 id=&quot;our-goal&quot;&gt;Our goal&lt;/h2&gt;&lt;p&gt;We want to divide our project into separate Xcode projects according to the architecture I proposed in the previous article.&lt;/p&gt;&lt;p&gt;To reiterate, our app will consist of a combination of modules and for every module or feature, we will create a new Tuist project.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Remember that each feature should not depend on other features&apos; implementation. Only interfaces should be public&lt;/div&gt;&lt;/div&gt;&lt;p&gt;So, for each feature, we will create several targets corresponding to the feature interface, implementation, and testing or mocking targets if required.&lt;/p&gt;&lt;h2 id=&quot;defining-project&quot;&gt;Defining project&lt;/h2&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-purple&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💥&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Sources for this post are published on GitHub. So, before reading this article you can see how elegant describing a project could be when using Tuist&lt;/div&gt;&lt;/div&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://github.com/AlexRoar/TuistExample?ref=alexdremov.me&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;GitHub - AlexRoar/TuistExample: Using Tuist for modular app architecture&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;Using Tuist for modular app architecture. Contribute to AlexRoar/TuistExample development by creating an account on GitHub.&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://github.com/fluidicon.png&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;GitHub&lt;/span&gt;&lt;span class=&quot;kg-bookmark-publisher&quot;&gt;AlexRoar&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://opengraph.githubassets.com/504ecc1c580a5e07e18a7a1546831b4328d13bfcbbd90a7c6cc0b0d35be53e18/AlexRoar/TuistExample&quot; alt=&quot;&quot; onerror=&quot;this.style.display = &apos;none&apos;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;h3 id=&quot;structure&quot;&gt;Structure&lt;/h3&gt;&lt;p&gt;Tuist project is a simple folder with config files describing your workspace structure&lt;/p&gt;&lt;pre&gt;&lt;code&gt;Your project root
├── Workspace.swift
├── Tuist
│   ├── Config.swift
│   ├── Dependencies.swift
│   └── ProjectDescriptionHelpers
│       └── &amp;lt;tuist helpers&amp;gt;
└── modules
    ├── Foo
    │   ├── Project.swift
    │   └── &amp;lt;module code, folders&amp;gt;
    ├── Biz
    │   ├── Project.swift
    │   └── &amp;lt;module code, folders&amp;gt;
    └── ...&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;But as I said early, &lt;em&gt;each&lt;/em&gt; module should have at least an implementation and interface target&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;There could be modules that contain common tools and that are not dependent on any other module. Then, it might have implementation only&lt;/div&gt;&lt;/div&gt;&lt;p&gt;So, let&apos;s modify the structure according to that&lt;/p&gt;&lt;pre&gt;&lt;code&gt;Your project root
├── Workspace.swift
├── Tuist
│   ├── Config.swift
│   ├── Dependencies.swift
│   └── ProjectDescriptionHelpers
│       └── &amp;lt;tuist helpers&amp;gt;
└── modules
    ├── Foo
    │   └── Project.swift
    │       ├── interface
    │       │   └── &amp;lt;interface files&amp;gt;
    │       └── src
    │           └── &amp;lt;implementation files&amp;gt;
    ├── Biz
    │   └── Project.swift
    │       ├── interface
    │       │   └── &amp;lt;interface files&amp;gt;
    │       └── src
    │           └── &amp;lt;implementation files&amp;gt;
    └── ...&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Before defining modules, we need to define where Tuist should search for these modules. This can be done in &lt;code&gt;Workspace.swift&lt;/code&gt; file &lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;import ProjectDescription

let workspace = Workspace(
    name: &quot;ExampleWorkspace&quot;,
    projects: [
        &quot;modules/*&quot;
    ]
)
&lt;/code&gt;&lt;/pre&gt;
&lt;!--kg-card-begin: html--&gt;
&lt;section class=&quot;custom-replace&quot; data-replace=&quot;.subscription-article&quot;&gt;
    &lt;a class=&quot;gh-portal-triggerbtn-container&quot;&gt;Subscribe and don&apos;t miss posts!&lt;/a&gt;
&lt;/section&gt;
&lt;!--kg-card-end: html--&gt;
&lt;h3 id=&quot;project-file&quot;&gt;Project file&lt;/h3&gt;&lt;p&gt;Tuist defines the Xcode project with a simple Swift file.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;// Project.swift
import ProjectDescription
import ProjectDescriptionHelpers

let project = Project(
  name: &quot;ProjectName&quot;,
  targets: [
  	...
  ]
)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;But this post is not just a review of Tuist&lt;/p&gt;&lt;p&gt;Let&apos;s define a project, knowing that we need to have an interface and implementation targets. Also, let&apos;s create an enum for feature names so that we don&apos;t have to use strings and remember all namings&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;As config is defined in Swift, you can use the power of suggestions and auto-completion in Xcode while defining your project structure.&lt;br&gt;&lt;br&gt;For example, Xcode will suggest other modules&apos; names when using enums&lt;/div&gt;&lt;/div&gt;&lt;p&gt;With several simple helpers, we could define project structure with Swift&apos;s beauty:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;import ProjectDescription
import ProjectDescriptionHelpers

let project = Project(
    name: Feature.Foo.rawValue,
    targets: [
        .feature(
            implementation: .Foo,
            dependencies: [
                .feature(interface: .Biz),
                .external(.AsyncAlgorithms)
            ]
        ),
        .feature(
            interface: .Foo,
            dependencies: [
                .feature(interface: .Biz)
            ]
        )
    ]
)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Features are going to be separate frameworks.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;All &lt;code spellcheck=&quot;false&quot; style=&quot;white-space: pre-wrap;&quot;&gt;swift&lt;/code&gt; files that help to describe tuist configs should be placed in the &lt;code spellcheck=&quot;false&quot; style=&quot;white-space: pre-wrap;&quot;&gt;ProjectDescriptionHelpers&lt;/code&gt; folder&lt;/div&gt;&lt;/div&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;public extension Target {
    static func makeFramework(
        name: String,
        sources: ProjectDescription.SourceFilesList,
        dependencies: [ProjectDescription.TargetDependency] = [],
        resources: ProjectDescription.ResourceFileElements? = []
    ) -&amp;gt; Target {
        Target(
            name: name,
            platform: .iOS,
            product: defaultPackageType,
            bundleId: makeBundleID(with: name + &quot;.framework&quot;),
            sources: sources,
            resources: resources,
            dependencies: dependencies
        )
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Then, we can define what feature is&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;public extension Target {
    static func feature(
        interface featureName: Feature,
        dependencies: [ProjectDescription.TargetDependency] = [],
        resources: ProjectDescription.ResourceFileElements? = []
    ) -&amp;gt; Target {
        .makeFramework(
            name: featureName.rawValue + &quot;Interface&quot;,
            sources: [ &quot;interface/**&quot; ],
            dependencies: dependencies,
            resources: resources
        )
    }
    
    static func feature(
        interface featureName: Feature,
        dependencies: [ProjectDescription.TargetDependency] = [],
        resources: ProjectDescription.ResourceFileElements? = []
    ) -&amp;gt; Target {
        .makeFramework(
            name: featureName.rawValue,
            sources: [ &quot;src/**&quot; ],
            dependencies: dependencies,
            resources: resources
        )
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Finally, we combine modules in an app target. It&apos;s defined in the same way&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;public extension Target {
    static func makeApp(
        name: String,
        sources: ProjectDescription.SourceFilesList,
        dependencies: [ProjectDescription.TargetDependency]
    ) -&amp;gt; Target {
        Target(
            name: name,
            platform: .iOS,
            product: .app,
            bundleId: makeBundleID(with: &quot;app&quot;),
            deploymentTarget: .iOS(targetVersion: &quot;16.0&quot;, devices: .iphone),
            sources: sources,
            dependencies: dependencies
        )
    }
}

let project = Project(
    name: &quot;ExampleApp&quot;,
    targets: [
        .makeApp(
            name: &quot;ExampleApp&quot;,
            sources: [
                &quot;src/**&quot;
            ],
            dependencies: [
                .common,
                .feature(implementation: .Foo),
                .feature(interface: .Foo),

                .feature(implementation: .Biz),
                .feature(interface: .Biz),

                .external(.FoggyColors)
            ]
        )
    ]
)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;That&apos;s it.&lt;/p&gt;&lt;p&gt;Now we can create different features and state dependencies between them. After that, we simply use &lt;code&gt;tuist generate&lt;/code&gt; command and it generates Xcode workspace and Xcode projects for us. &lt;/p&gt;&lt;figure class=&quot;kg-card kg-image-card kg-card-hascaption&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2022/10/Screenshot-2022-10-07-at-00.43.44-min.png&quot; class=&quot;kg-image&quot; alt=&quot;Tuist-generated workspace&quot; loading=&quot;lazy&quot; width=&quot;2000&quot; height=&quot;1339&quot; srcset=&quot;https://alexdremov.me/content/images/size/w600/2022/10/Screenshot-2022-10-07-at-00.43.44-min.png 600w, https://alexdremov.me/content/images/size/w1000/2022/10/Screenshot-2022-10-07-at-00.43.44-min.png 1000w, https://alexdremov.me/content/images/size/w1600/2022/10/Screenshot-2022-10-07-at-00.43.44-min.png 1600w, https://alexdremov.me/content/images/size/w2400/2022/10/Screenshot-2022-10-07-at-00.43.44-min.png 2400w&quot; sizes=&quot;(min-width: 720px) 720px&quot;&gt;&lt;figcaption&gt;&lt;span style=&quot;white-space: pre-wrap;&quot;&gt;Tuist-generated workspace&lt;/span&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;Great!&lt;/p&gt;&lt;p&gt;Now we have our project bootstrapped, and it is fully defined in nice Swift files with a clean structure and explicit dependencies. You can add all &lt;code&gt;.xcodeproj&lt;/code&gt; and &lt;code&gt;.xcworkspace&lt;/code&gt; to gitignore and forget about a mess in GitHub repositories.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-purple&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💥&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Some details are not covered for the brevity of this post. The full example is published on GitHub and do not hesitate to ask me about anything in the comments!&lt;/div&gt;&lt;/div&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://github.com/AlexRoar/TuistExample?ref=alexdremov.me&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;GitHub - AlexRoar/TuistExample: Using Tuist for modular app architecture&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;Using Tuist for modular app architecture. Contribute to AlexRoar/TuistExample development by creating an account on GitHub.&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://github.com/fluidicon.png&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;GitHub&lt;/span&gt;&lt;span class=&quot;kg-bookmark-publisher&quot;&gt;AlexRoar&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://opengraph.githubassets.com/504ecc1c580a5e07e18a7a1546831b4328d13bfcbbd90a7c6cc0b0d35be53e18/AlexRoar/TuistExample&quot; alt=&quot;&quot; onerror=&quot;this.style.display = &apos;none&apos;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;h2 id=&quot;creating-an-app-with-tuist&quot;&gt;Creating an app with Tuist&lt;/h2&gt;&lt;p&gt;I already showed how to define project structure in the examples above. Let&apos;s get even more specific and write a simple app that will show a random value in a range.&lt;/p&gt;&lt;figure class=&quot;kg-card kg-image-card kg-card-hascaption&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2022/10/graph.svg&quot; class=&quot;kg-image&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;560&quot; height=&quot;346&quot;&gt;&lt;figcaption&gt;&lt;span style=&quot;white-space: pre-wrap;&quot;&gt;App Architecture&lt;/span&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;strong&gt;RandomProvider &lt;/strong&gt;defines a protocol for generating a random number and several implementations for it&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;// Interface
public protocol NumberProvider {
    var number: Int { get }
}

// Implementation
public struct NumberProviderZero: NumberProvider {
    public let number = 0
    
    public init() {
        
    }
}

public struct NumberProviderRandom: NumberProvider {
    private let range: ClosedRange&amp;lt;Int&amp;gt;
    
    public var number: Int {
        Int.random(in: range)
    }
    
    public init(range: ClosedRange&amp;lt;Int&amp;gt;) {
        self.range = range
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;RandomScreen &lt;/strong&gt;defines several UI screens to display random number and re-generate it. Notice that it depends only on &lt;strong&gt;RandomProviderInterface&lt;/strong&gt; and not on &lt;strong&gt;RandomProvider &lt;/strong&gt;which is the implementation&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;public struct RandomScreenSimple: RandomScreen {
    let randomProvider: NumberProvider
    
    @State var number: Int = 0
    
    public init(randomProvider: NumberProvider) {
        self.randomProvider = randomProvider
    }
    
    public var body: some View {
        VStack {
            Text(&quot;\(number)&quot;)
            Button(&quot;generate&quot;) {
                number = randomProvider.number
            }
        }.onAppear {
            number = randomProvider.number
        }
        .animation(.default, value: number)
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Common &lt;/strong&gt;is a module that provides common tools. Actually, it is used only by the App module, but I wanted to show that many modules can depend on it&lt;/p&gt;&lt;p&gt;&lt;strong&gt;ExampleApp &lt;/strong&gt;is an app module that combines other modules and builds the final app&lt;/p&gt;&lt;p&gt;This is the only module that can depend on other modules&apos; implementation. Moreover, it chooses which implementation to use depending on the scenario. In the example app, &lt;code&gt;NumberProvider&lt;/code&gt; implementation is changed in runtime &lt;/p&gt;&lt;figure class=&quot;kg-card kg-video-card kg-width-regular&quot; data-kg-thumbnail=&quot;https://alexdremov.me/content/media/2026/07/194519593-14910e2a-aef3-453a-9741-055ac1d6fde4_thumb.jpg&quot; data-kg-custom-thumbnail=&quot;&quot;&gt;
            &lt;div class=&quot;kg-video-container&quot;&gt;
                &lt;video src=&quot;https://alexdremov.me/content/media/2026/07/194519593-14910e2a-aef3-453a-9741-055ac1d6fde4.mp4&quot; poster=&quot;https://img.spacergif.org/v1/1170x2532/0a/spacer.png&quot; width=&quot;1170&quot; height=&quot;2532&quot; playsinline=&quot;&quot; preload=&quot;metadata&quot; style=&quot;background: transparent url(&apos;https://alexdremov.me/content/media/2026/07/194519593-14910e2a-aef3-453a-9741-055ac1d6fde4_thumb.jpg&apos;) 50% 50% / cover no-repeat;&quot;&gt;&lt;/video&gt;
                &lt;div class=&quot;kg-video-overlay&quot;&gt;
                    &lt;button class=&quot;kg-video-large-play-icon&quot; aria-label=&quot;Play video&quot;&gt;
                        &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot;&gt;
                            &lt;path d=&quot;M23.14 10.608 2.253.164A1.559 1.559 0 0 0 0 1.557v20.887a1.558 1.558 0 0 0 2.253 1.392L23.14 13.393a1.557 1.557 0 0 0 0-2.785Z&quot;&gt;&lt;/path&gt;
                        &lt;/svg&gt;
                    &lt;/button&gt;
                &lt;/div&gt;
                &lt;div class=&quot;kg-video-player-container&quot;&gt;
                    &lt;div class=&quot;kg-video-player&quot;&gt;
                        &lt;button class=&quot;kg-video-play-icon&quot; aria-label=&quot;Play video&quot;&gt;
                            &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot;&gt;
                                &lt;path d=&quot;M23.14 10.608 2.253.164A1.559 1.559 0 0 0 0 1.557v20.887a1.558 1.558 0 0 0 2.253 1.392L23.14 13.393a1.557 1.557 0 0 0 0-2.785Z&quot;&gt;&lt;/path&gt;
                            &lt;/svg&gt;
                        &lt;/button&gt;
                        &lt;button class=&quot;kg-video-pause-icon kg-video-hide&quot; aria-label=&quot;Pause video&quot;&gt;
                            &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot;&gt;
                                &lt;rect x=&quot;3&quot; y=&quot;1&quot; width=&quot;7&quot; height=&quot;22&quot; rx=&quot;1.5&quot; ry=&quot;1.5&quot;&gt;&lt;/rect&gt;
                                &lt;rect x=&quot;14&quot; y=&quot;1&quot; width=&quot;7&quot; height=&quot;22&quot; rx=&quot;1.5&quot; ry=&quot;1.5&quot;&gt;&lt;/rect&gt;
                            &lt;/svg&gt;
                        &lt;/button&gt;
                        &lt;span class=&quot;kg-video-current-time&quot;&gt;0:00&lt;/span&gt;
                        &lt;div class=&quot;kg-video-time&quot;&gt;
                            /&lt;span class=&quot;kg-video-duration&quot;&gt;0:09&lt;/span&gt;
                        &lt;/div&gt;
                        &lt;input type=&quot;range&quot; class=&quot;kg-video-seek-slider&quot; max=&quot;100&quot; value=&quot;0&quot;&gt;
                        &lt;button class=&quot;kg-video-playback-rate&quot; aria-label=&quot;Adjust playback speed&quot;&gt;1×&lt;/button&gt;
                        &lt;button class=&quot;kg-video-unmute-icon&quot; aria-label=&quot;Unmute&quot;&gt;
                            &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot;&gt;
                                &lt;path d=&quot;M15.189 2.021a9.728 9.728 0 0 0-7.924 4.85.249.249 0 0 1-.221.133H5.25a3 3 0 0 0-3 3v2a3 3 0 0 0 3 3h1.794a.249.249 0 0 1 .221.133 9.73 9.73 0 0 0 7.924 4.85h.06a1 1 0 0 0 1-1V3.02a1 1 0 0 0-1.06-.998Z&quot;&gt;&lt;/path&gt;
                            &lt;/svg&gt;
                        &lt;/button&gt;
                        &lt;button class=&quot;kg-video-mute-icon kg-video-hide&quot; aria-label=&quot;Mute&quot;&gt;
                            &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot;&gt;
                                &lt;path d=&quot;M16.177 4.3a.248.248 0 0 0 .073-.176v-1.1a1 1 0 0 0-1.061-1 9.728 9.728 0 0 0-7.924 4.85.249.249 0 0 1-.221.133H5.25a3 3 0 0 0-3 3v2a3 3 0 0 0 3 3h.114a.251.251 0 0 0 .177-.073ZM23.707 1.706A1 1 0 0 0 22.293.292l-22 22a1 1 0 0 0 0 1.414l.009.009a1 1 0 0 0 1.405-.009l6.63-6.631A.251.251 0 0 1 8.515 17a.245.245 0 0 1 .177.075 10.081 10.081 0 0 0 6.5 2.92 1 1 0 0 0 1.061-1V9.266a.247.247 0 0 1 .073-.176Z&quot;&gt;&lt;/path&gt;
                            &lt;/svg&gt;
                        &lt;/button&gt;
                        &lt;input type=&quot;range&quot; class=&quot;kg-video-volume-slider&quot; max=&quot;100&quot; value=&quot;100&quot;&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            
        &lt;/figure&gt;&lt;h2 id=&quot;final-notes&quot;&gt;Final notes&lt;/h2&gt;&lt;p&gt;So, in this post, we constructed a modular app using Tuist. In the example project, I added useful tools like&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Additions to default Info.plist&lt;/li&gt;&lt;li&gt;Template for creating a new feature that can be invoked by&lt;br&gt;&lt;code&gt;tuist scaffold framework --name ModuleName&lt;/code&gt;. This will create a new module folder, Project.swift file&lt;/li&gt;&lt;li&gt;Building for release mode. You can invoke generation with an environment variable and this will make all modules static. Using static frameworks improves app speed and is good for production.&lt;br&gt;&lt;code&gt;TUIST_BUILD_TYPE_RELEASE=TRUE tuist generate --no-cache&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Also, If you have not read my article on a general overview of modular architecture, check it out!&lt;/p&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://alexdremov.me/ios-app-as-a-microservice-build-robust-app-architecture/&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;iOS App As a Microservice. Build Robust App Architecture&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;What will you choose: MVVM, MVC, VIPER? Those all are local and problem-specific architectures. But how to structure your app on a larger scale to make it scalable and well-organized?&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://alexdremov.me/assets/icons/apple-touch-icon.png&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;Alex Dremov&lt;/span&gt;&lt;span class=&quot;kg-bookmark-publisher&quot;&gt;Alex Dremov&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://images.unsplash.com/photo-1532622785990-d2c36a76f5a6?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=max&amp;amp;fm=jpg&amp;amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDV8fHN0cnVjdHVyZXxlbnwwfHx8fDE2NjMyMzA3ODU&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;w=2000&quot; alt=&quot;&quot; onerror=&quot;this.style.display = &apos;none&apos;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;p&gt;Do not hesitate to ask anything in the comments&lt;/p&gt;&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://tuist.io/?ref=alexdremov.me&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;Xcode on steroids | Tuist&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;Tuist is a tool that helps developers manage large Xcode projects by leveraging project generation. Moreover, it provides some tools to automate most common tasks, allowing developers to focus on building apps.&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://tuist.io/icons/icon-512x512.png?v=afd926b5da3ecabd886495871849f751&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;Tuist - Xcode on steroids&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://tuist.io/squared-logo.png&quot; alt=&quot;&quot; onerror=&quot;this.style.display = &apos;none&apos;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;</content:encoded></item><item><title>iOS App As a Microservice. Build Robust App Architecture</title><link>https://alexdremov.me/ios-app-as-a-microservice-build-robust-app-architecture/</link><guid isPermaLink="true">https://alexdremov.me/ios-app-as-a-microservice-build-robust-app-architecture/</guid><description>MVVM, MVC, VIPER? Those all are problem-specific architectures. How to structure your app on a larger scale? More in this post </description><pubDate>Fri, 16 Sep 2022 07:43:25 GMT</pubDate><content:encoded>&lt;p&gt;In this post, I will discuss microfeature architecture that is, simply said, amazing when implemented correctly in an iOS app.&lt;/p&gt;&lt;h2 id=&quot;next-episodes&quot;&gt;Next Episodes&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;Ideas on implementation with &lt;strong&gt;SwiftUI&lt;/strong&gt;&lt;/li&gt;&lt;/ul&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://alexdremov.me/ios-app-as-a-microservice-using-swiftui-in-modular-app/#whats-the-problem&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;iOS App As a Microservice. Using SwiftUI in Modular App&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;The modular architecture is excellent. But how to implement it effectively with SwiftUI? From its core, SwiftUI is state-driven, and it can be tricky to modularize an app and define exact responsibility borders.&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://alexdremov.me/assets/icons/apple-touch-icon.png&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;Alex Dremov&lt;/span&gt;&lt;span class=&quot;kg-bookmark-publisher&quot;&gt;Alex Dremov&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://images.unsplash.com/photo-1581291518633-83b4ebd1d83e?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=max&amp;amp;fm=jpg&amp;amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDJ8fGludGVyZmFjZXxlbnwwfHx8fDE2NjYxMjA1NzM&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;w=2000&quot; alt=&quot;&quot; onerror=&quot;this.style.display = &apos;none&apos;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;ul&gt;&lt;li&gt;Using &lt;strong&gt;tuist&lt;/strong&gt; to structure microfeature application&lt;/li&gt;&lt;/ul&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://alexdremov.me/ios-app-as-a-microservice-modularize-your-app-with-tuist/&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;iOS App As a Microservice. Modularize Your App With Tuist&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;This is the second article in a series on modular app architecture. In this post, I will cover implementation details using Tuist&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://alexdremov.me/assets/icons/apple-touch-icon.png&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;Alex Dremov&lt;/span&gt;&lt;span class=&quot;kg-bookmark-publisher&quot;&gt;Alex Dremov&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://images.unsplash.com/photo-1613645695025-20e3f38de4a6?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=max&amp;amp;fm=jpg&amp;amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDJ8fG1vZHVsYXJ8ZW58MHx8fHwxNjY0OTk5NDQ5&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;w=2000&quot; alt=&quot;&quot; onerror=&quot;this.style.display = &apos;none&apos;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;h2 id=&quot;core-idea&quot;&gt;Core Idea&lt;/h2&gt;&lt;p&gt;The idea comes from microservice server-side application infrastructure. The whole app is divided into logical components corresponding to different functional areas of the application. &lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-purple&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💥&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Considering how complex mobile apps can be, why not apply the same architecture to iOS apps?&lt;/div&gt;&lt;/div&gt;&lt;p&gt;Briefly, microfeature architecture implies splitting your app into different components that accept other components&apos; interfaces or data as &lt;strong&gt;explicit dependencies&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;Therefore, your app can be represented as a graph of modules that explicitly interact with each other.&lt;/p&gt;&lt;h2 id=&quot;main-benefits&quot;&gt;Main Benefits&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Improved maintainability&lt;/strong&gt; — each component is small and so is easier to understand and change.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Better testability&lt;/strong&gt; — components explicitly define their public interface. So, they are easier to mock and test.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Team organization &lt;/strong&gt;— different teams can work on different components independently.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Scalability, code reuse&lt;/strong&gt; —when an app is a combination of modules, you can robustly change the app&apos;s behaviour by recombining modules. If you decide to create an app extension, watchOS app, or App Clip, just pick the required components and you&apos;re all set up.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Explicit dependencies&lt;/strong&gt; — implicit dependencies are one of the worst things that can happen to an app&apos;s architecture. This architecture requires defining explicit dependencies for each module.&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;details&quot;&gt;Details&lt;/h2&gt;&lt;p&gt;So, how to structure an iOS app once you decided to use microfeature architecture? The core concept is separation. But you still can use one Xcode project for that and separate features purely by architecture.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-purple&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💥&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;You can put each feature into &lt;b&gt;&lt;strong style=&quot;white-space: pre-wrap;&quot;&gt;a separate Xcode project&lt;/strong&gt;&lt;/b&gt;. This will push you to a strict separation of components.I will cover how to do this effectively with &lt;b&gt;&lt;strong style=&quot;white-space: pre-wrap;&quot;&gt;tuist&lt;/strong&gt;&lt;/b&gt; in the next episode!&lt;/div&gt;&lt;/div&gt;&lt;p&gt; Your codebase will be divided into several blocks:&lt;/p&gt;&lt;h3 id=&quot;features&quot;&gt;Features&lt;/h3&gt;&lt;p&gt;That&apos;s where elements of your app live. Later in this post, I will show by example what this part includes.&lt;/p&gt;&lt;p&gt;Components are logical blocks of your app. Each component explicitly defines an interface to interact with it.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Swift does not have namespaces, but you can use enums to hide internal module logic.&lt;/div&gt;&lt;/div&gt;&lt;h3 id=&quot;apps&quot;&gt;Apps&lt;/h3&gt;&lt;p&gt;You can have a WatchOS app, widgets, and the main iOS app. Each app depends on features and builds the final app using features, combining them like bricks.&lt;/p&gt;&lt;figure class=&quot;kg-card kg-image-card kg-card-hascaption&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2022/09/graphviz-10.svg&quot; class=&quot;kg-image&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;485&quot; height=&quot;346&quot;&gt;&lt;figcaption&gt;&lt;span style=&quot;white-space: pre-wrap;&quot;&gt;General apps structure&lt;/span&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;figure class=&quot;kg-card kg-image-card kg-card-hascaption&quot;&gt;&lt;img src=&quot;https://images.unsplash.com/photo-1591040092219-081fb773589c?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=max&amp;amp;fm=jpg&amp;amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDR8fHB1enpsZXxlbnwwfHx8fDE2NjMyNjcwNTM&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;w=2000&quot; class=&quot;kg-image&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;5568&quot; height=&quot;3712&quot; srcset=&quot;https://images.unsplash.com/photo-1591040092219-081fb773589c?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=max&amp;amp;fm=jpg&amp;amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDR8fHB1enpsZXxlbnwwfHx8fDE2NjMyNjcwNTM&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;w=600 600w, https://images.unsplash.com/photo-1591040092219-081fb773589c?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=max&amp;amp;fm=jpg&amp;amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDR8fHB1enpsZXxlbnwwfHx8fDE2NjMyNjcwNTM&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;w=1000 1000w, https://images.unsplash.com/photo-1591040092219-081fb773589c?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=max&amp;amp;fm=jpg&amp;amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDR8fHB1enpsZXxlbnwwfHx8fDE2NjMyNjcwNTM&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;w=1600 1600w, https://images.unsplash.com/photo-1591040092219-081fb773589c?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=max&amp;amp;fm=jpg&amp;amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDR8fHB1enpsZXxlbnwwfHx8fDE2NjMyNjcwNTM&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;w=2400 2400w&quot; sizes=&quot;(min-width: 720px) 720px&quot;&gt;&lt;figcaption&gt;&lt;span style=&quot;white-space: pre-wrap;&quot;&gt;Photo by &lt;/span&gt;&lt;a href=&quot;https://unsplash.com/@ashkfor121?utm_source=ghost&amp;utm_medium=referral&amp;utm_campaign=api-credit&quot;&gt;&lt;span style=&quot;white-space: pre-wrap;&quot;&gt;Ashkan Forouzani&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot;white-space: pre-wrap;&quot;&gt; / &lt;/span&gt;&lt;a href=&quot;https://unsplash.com/?utm_source=ghost&amp;utm_medium=referral&amp;utm_campaign=api-credit&quot;&gt;&lt;span style=&quot;white-space: pre-wrap;&quot;&gt;Unsplash&lt;/span&gt;&lt;/a&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;h3 id=&quot;tests-testing-data-and-mock&quot;&gt;Tests + Testing Data And Mock&lt;/h3&gt;&lt;p&gt;This logic also lies apart from the feature&apos;s main parts. It&apos;s separate because:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;We don&apos;t want to use mock data accidentally in the app&lt;/li&gt;&lt;li&gt;We don&apos;t want to include irrelevant data in the final app binary&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;feature-design&quot;&gt;Feature design&lt;/h2&gt;&lt;p&gt;The feature consists of four blocks. Tests and mocks may not be present, but the feature always has an interface and implementation.&lt;/p&gt;&lt;figure class=&quot;kg-card kg-image-card kg-card-hascaption&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2022/09/graphviz-6.svg&quot; class=&quot;kg-image&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;354&quot; height=&quot;250&quot;&gt;&lt;figcaption&gt;&lt;span style=&quot;white-space: pre-wrap;&quot;&gt;One feature structure&lt;/span&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;h3 id=&quot;interface&quot;&gt;Interface&lt;/h3&gt;&lt;p&gt;This part defines parts visible for other features. Public interfaces and models or entities of the feature stay here.&lt;/p&gt;&lt;p&gt;Interfaces define ways that are used to interact with the feature.&lt;/p&gt;&lt;p&gt;Models or entities are simple structures with almost no logic that simply define data used to communicate with the feature.&lt;/p&gt;&lt;p&gt;You can include other components in the interface but remember that &lt;strong&gt;interface must not expose implementation details&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-purple&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💥&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;If the feature depends on another feature, then it depends on the other feature&apos;s interface.Features &lt;b&gt;&lt;strong style=&quot;white-space: pre-wrap;&quot;&gt;must not&lt;/strong&gt;&lt;/b&gt; depend on other feature&apos;s implementation&lt;/div&gt;&lt;/div&gt;&lt;h3 id=&quot;implementation&quot;&gt;Implementation&lt;/h3&gt;&lt;p&gt;Implementation depends on an interface and provides classes and structures conforming to defined protocols in the interface. Resources, images, and other implementation details also stay here.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Separation Interface/Implementation forces you to write code conforming to the letter &lt;b&gt;&lt;strong style=&quot;white-space: pre-wrap;&quot;&gt;D &lt;/strong&gt;&lt;/b&gt;from&lt;b&gt;&lt;strong style=&quot;white-space: pre-wrap;&quot;&gt; SOLID&lt;/strong&gt;&lt;/b&gt;.Dependency inversion happens naturally when other modules know about interfaces and not about implementations.&lt;/div&gt;&lt;/div&gt;&lt;p&gt;Knowing this information, we can add details to our app&apos;s graph image:&lt;/p&gt;&lt;figure class=&quot;kg-card kg-image-card kg-card-hascaption&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2022/09/graphviz-11.svg&quot; class=&quot;kg-image&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;568&quot; height=&quot;405&quot;&gt;&lt;figcaption&gt;&lt;span style=&quot;white-space: pre-wrap;&quot;&gt;Detailed apps structure&lt;/span&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;Notice that none of the features depends on the other feature&apos;s interface. Each feature interface strictly depends on the other feature&apos;s interface.&lt;/p&gt;&lt;p&gt;Now you see that apps take building blocks and combine them to make an app.&lt;/p&gt;
&lt;!--kg-card-begin: html--&gt;
&lt;section class=&quot;custom-replace&quot; data-replace=&quot;.subscription-article&quot;&gt;
    &lt;a class=&quot;gh-portal-triggerbtn-container&quot;&gt;Subscribe and don&apos;t miss posts!&lt;/a&gt;
&lt;/section&gt;
&lt;!--kg-card-end: html--&gt;
&lt;h2 id=&quot;case-example&quot;&gt;Case Example&lt;/h2&gt;&lt;p&gt;Let&apos;s architect a scheduling app. It will have:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Schedule view&lt;/li&gt;&lt;li&gt;Add event/edit view&lt;/li&gt;&lt;li&gt;Schedule WatchOS View&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Pretty simple.&lt;/p&gt;&lt;p&gt;Let&apos;s split this app into several features:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;UICommon&lt;/strong&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Contains common UI elements that can be used to create more complex views&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Schedule&lt;/strong&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Contains main schedule views and logic associated with them. The interface defines ways to interact with views or present them.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;WatchSchedule&lt;/strong&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Contains watch-specific schedule views and logic associated with them&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;EventModification&lt;/strong&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Contains event modification logic and views&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;ScheduleData&lt;/strong&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Data provider. Defines data structures and entities to obtain them.&lt;/p&gt;&lt;p&gt;The interface will contain simple data entities and model protocols defining ways of obtaining these entities.&lt;/p&gt;&lt;p&gt;Implementation defines models conforming to protocols defined in the interface. For example, you may want to define a local storage model or network model. It&apos;s up to the final app to decide which option to use.&lt;/p&gt;&lt;h3 id=&quot;app-graph&quot;&gt;App Graph&lt;/h3&gt;&lt;figure class=&quot;kg-card kg-image-card kg-card-hascaption&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2022/09/graphviz-13.svg&quot; class=&quot;kg-image&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;613&quot; height=&quot;346&quot;&gt;&lt;figcaption&gt;&lt;span style=&quot;white-space: pre-wrap;&quot;&gt;Case app graph&lt;/span&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;As you see, WatchOS and the main iOS app reuse common components. Also, Each app decides which implementation of modules&apos; interfaces they pick. For example, the WatchOS app can choose different data sources in ScheduleData feature rather than the main iOS app.&lt;/p&gt;&lt;p&gt;In a monolithic app, you would probably need to write almost a second app and copy a lot of code&lt;/p&gt;&lt;h2 id=&quot;next-episodes-1&quot;&gt;Next Episodes&lt;/h2&gt;&lt;p&gt;In the next posts, I will share my ideas on using microfeature architecture with &lt;strong&gt;SwiftUI &lt;/strong&gt;and&lt;strong&gt; tuist&lt;/strong&gt; to structure code efficiently.&lt;/p&gt;&lt;h2 id=&quot;faq&quot;&gt;FAQ&lt;/h2&gt;&lt;h3 id=&quot;when-should-i-create-a-new-feature-and-when-its-better-not-to&quot;&gt;When should I create a new feature and when It&apos;s better not to?&lt;/h3&gt;&lt;p&gt;It purely depends on the case and on what you think the best option is. If you can come up with some use case when your feature will be reused in some other context, then it&apos;s a separate feature.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-purple&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💥&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Do not overcomplicate things!Making a new feature for each class will do more harm than good.&lt;/div&gt;&lt;/div&gt;&lt;p&gt;If some block probably will not be reused, but you &lt;strong&gt;just feel&lt;/strong&gt; that it&apos;s logically separate functionality, then also go with a new feature as it will help to keep your architecture clean.&lt;/p&gt;&lt;h3 id=&quot;what-to-do-with-circular-references&quot;&gt;What to do with circular references? &lt;/h3&gt;&lt;p&gt;Circular references can be a pain and they happen if two features depend on each other&apos;s interfaces. If such a situation happens, critically consider if your feature separation is correct. There are two possible options.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Two features are actually one feature. Then, you can merge these two features and get rid of circular references.&lt;/li&gt;&lt;li&gt;Two features are actually three features. If features depend on each other, then there is some part that&apos;s needed by both features. What if this part is an independent feature? If this is the case, extract the third feature and fix dependencies.&lt;/li&gt;&lt;/ul&gt;&lt;figure class=&quot;kg-card kg-image-card kg-card-hascaption&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2022/09/graphviz-12.svg&quot; class=&quot;kg-image&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;738&quot; height=&quot;193&quot;&gt;&lt;figcaption&gt;&lt;span style=&quot;white-space: pre-wrap;&quot;&gt;Possible circular reference solution&lt;/span&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;h3 id=&quot;theres-a-lot-said-about-making-dependencies-explicit-whats-the-point&quot;&gt;There&apos;s a lot said about making dependencies explicit. What&apos;s the point?&lt;/h3&gt;&lt;p&gt;It&apos;s nearly impossible to scale or modify big apps when components are implicitly dependent. Just imagine the mess that is going to happen if you modify some class that is a dependency of all other modules through a singleton.&lt;/p&gt;&lt;p&gt;Your app may start to have unexpected behaviour here and there and you can&apos;t even know how your modification will affect the whole app.&lt;/p&gt;&lt;p&gt;It&apos;s like sitting on a box of TNT.&lt;/p&gt;&lt;figure class=&quot;kg-card kg-image-card kg-card-hascaption&quot;&gt;&lt;img src=&quot;https://images.unsplash.com/photo-1613834927301-1c96a302e074?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=max&amp;amp;fm=jpg&amp;amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDJ8fGR5bmFtaXRlfGVufDB8fHx8MTY2MzI3MzE5MA&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;w=2000&quot; class=&quot;kg-image&quot; alt=&quot;🃏&quot; loading=&quot;lazy&quot; width=&quot;6000&quot; height=&quot;4000&quot; srcset=&quot;https://images.unsplash.com/photo-1613834927301-1c96a302e074?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=max&amp;amp;fm=jpg&amp;amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDJ8fGR5bmFtaXRlfGVufDB8fHx8MTY2MzI3MzE5MA&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;w=600 600w, https://images.unsplash.com/photo-1613834927301-1c96a302e074?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=max&amp;amp;fm=jpg&amp;amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDJ8fGR5bmFtaXRlfGVufDB8fHx8MTY2MzI3MzE5MA&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;w=1000 1000w, https://images.unsplash.com/photo-1613834927301-1c96a302e074?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=max&amp;amp;fm=jpg&amp;amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDJ8fGR5bmFtaXRlfGVufDB8fHx8MTY2MzI3MzE5MA&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;w=1600 1600w, https://images.unsplash.com/photo-1613834927301-1c96a302e074?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=max&amp;amp;fm=jpg&amp;amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDJ8fGR5bmFtaXRlfGVufDB8fHx8MTY2MzI3MzE5MA&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;w=2400 2400w&quot; sizes=&quot;(min-width: 720px) 720px&quot;&gt;&lt;figcaption&gt;&lt;span style=&quot;white-space: pre-wrap;&quot;&gt;Photo by &lt;/span&gt;&lt;a href=&quot;https://unsplash.com/@messrro?utm_source=ghost&amp;utm_medium=referral&amp;utm_campaign=api-credit&quot;&gt;&lt;span style=&quot;white-space: pre-wrap;&quot;&gt;Mehdi MeSSrro&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot;white-space: pre-wrap;&quot;&gt; / &lt;/span&gt;&lt;a href=&quot;https://unsplash.com/?utm_source=ghost&amp;utm_medium=referral&amp;utm_campaign=api-credit&quot;&gt;&lt;span style=&quot;white-space: pre-wrap;&quot;&gt;Unsplash&lt;/span&gt;&lt;/a&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;I encourage you to avoid implicit dependencies whenever possible. Microfeatures architecture will help you with doing that.&lt;/p&gt;&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://alexdremov.me/ios-app-as-a-microservice-modularize-your-app-with-tuist/&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;iOS App As a Microservice. Modularize Your App With Tuist&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;This is the second article in a series on modular app architecture. In this post, I will cover implementation details using Tuist&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://alexdremov.me/assets/icons/apple-touch-icon.png&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;Alex Dremov&lt;/span&gt;&lt;span class=&quot;kg-bookmark-publisher&quot;&gt;Alex Dremov&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://images.unsplash.com/photo-1613645695025-20e3f38de4a6?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=max&amp;amp;fm=jpg&amp;amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDJ8fG1vZHVsYXJ8ZW58MHx8fHwxNjY0OTk5NDQ5&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;w=2000&quot; alt=&quot;&quot; onerror=&quot;this.style.display = &apos;none&apos;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://docs.tuist.io/building-at-scale/microfeatures?ref=alexdremov.me&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;µFeatures Architecture | Tuist Documentation&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;This document describes an approach for architecting a modular Apple OS application to enable scalability, optimize build and test cycles, and ensure good practices.&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://docs.tuist.io/img/favicon.ico&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;Tuist&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://docs.tuist.io/img/logo.svg&quot; alt=&quot;&quot; onerror=&quot;this.style.display = &apos;none&apos;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;figure class=&quot;kg-card kg-embed-card&quot;&gt;&lt;iframe id=&quot;talk_frame_430480&quot; class=&quot;speakerdeck-iframe&quot; src=&quot;//speakerdeck.com/player/f1759993c7d54294bbcfab419acae8f0&quot; width=&quot;710&quot; height=&quot;399&quot; style=&quot;aspect-ratio:710/399; border:0; padding:0; margin:0; background:transparent;&quot; frameborder=&quot;0&quot; allowtransparency=&quot;true&quot; allowfullscreen=&quot;allowfullscreen&quot; mozallowfullscreen=&quot;true&quot; webkitallowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;/figure&gt;</content:encoded></item><item><title>Exploring SwiftUI Layout Protocol | Creating Custom Layout</title><link>https://alexdremov.me/exploring-swiftui-layout-protocol-creating-custom-layout/</link><guid isPermaLink="true">https://alexdremov.me/exploring-swiftui-layout-protocol-creating-custom-layout/</guid><description>Apple introduces new SwiftUI&apos;s Layout protocol with the release of iOS 16. In this post, I will discuss the protocol and implement a custom view</description><pubDate>Thu, 11 Aug 2022 21:00:18 GMT</pubDate><content:encoded>&lt;p&gt;Apple introduces a new SwiftUI &lt;code&gt;Layout&lt;/code&gt; protocol with the release of iOS 16. It is a powerful tool for constructing custom views with SwiftUI elegance. In this post, I will cover what &lt;code&gt;Layout&lt;/code&gt; is and how it can be used.&lt;/p&gt;&lt;p&gt;In the end, we will construct a custom table view that auto-arranges its subviews. Complete code is provided!&lt;/p&gt;&lt;figure class=&quot;kg-card kg-image-card&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2022/08/Screenshot-2022-08-12-at-00.21.52.png&quot; class=&quot;kg-image&quot; alt loading=&quot;lazy&quot; width=&quot;2000&quot; height=&quot;1030&quot; srcset=&quot;https://alexdremov.me/content/images/size/w600/2022/08/Screenshot-2022-08-12-at-00.21.52.png 600w, https://alexdremov.me/content/images/size/w1000/2022/08/Screenshot-2022-08-12-at-00.21.52.png 1000w, https://alexdremov.me/content/images/size/w1600/2022/08/Screenshot-2022-08-12-at-00.21.52.png 1600w, https://alexdremov.me/content/images/2022/08/Screenshot-2022-08-12-at-00.21.52.png 2288w&quot; sizes=&quot;(min-width: 720px) 720px&quot;&gt;&lt;/figure&gt;&lt;h2 id=&quot;conforming-to-layout&quot;&gt;Conforming to Layout&lt;/h2&gt;&lt;p&gt;The discussed &lt;code&gt;Layout&lt;/code&gt; is a new protocol that allows you to select a way of arranging your views.&lt;/p&gt;&lt;p&gt;Through it, you literally can say at what coordinates you want to place subviews. For example, now &lt;code&gt;HStack&lt;/code&gt;, &lt;code&gt;VStack&lt;/code&gt;, and &lt;code&gt;ZStack&lt;/code&gt; can easily be implemented through it in iOS 16.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;protocol Layout : Animatable&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To conform to the protocol, you need to define two methods&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;func sizeThatFits(
    proposal: ProposedViewSize,
    subviews: Self.Subviews,
    cache: inout Self.Cache
) -&amp;gt; CGSize


func placeSubviews(
    in bounds: CGRect,
    proposal: ProposedViewSize,
    subviews: Self.Subviews,
    cache: inout Self.Cache
)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You also can define &lt;code&gt;makeCache(subviews:)&lt;/code&gt; if your layout has some calculations that do not depend on a proposal and depend only on subviews. Then, you can make your calculations in &lt;code&gt;makeCache(subviews:)&lt;/code&gt; and then use these values.&lt;/p&gt;&lt;h3 id=&quot;method-sizethatfits&quot;&gt;Method &lt;code&gt;sizeThatFits&lt;/code&gt;&lt;/h3&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;func sizeThatFits(
    proposal: ProposedViewSize,
    subviews: Self.Subviews,
    cache: inout Self.Cache
) -&amp;gt; CGSize&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Returns a size that indicates how much space the container needs to arrange its subviews. SwiftUI can call this method several times, probing your view and finally deciding the best option&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-purple&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💥&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Only finite sizes can be returned. Returning size with infinite coordinate &lt;strong&gt;results in a crash without a reasonable call stack&lt;/strong&gt;, so keep attention to sizes that you return&lt;/div&gt;&lt;/div&gt;&lt;p&gt;To calculate it, you can use passed arguments:&lt;/p&gt;&lt;h4 id=&quot;proposal&quot;&gt;proposal&lt;/h4&gt;&lt;p&gt;Basically, it&apos;s SwiftUI&apos;s proposal for your view&apos;s size. I like to think about it as a negotiation.&lt;/p&gt;&lt;blockquote&gt;I can give you this much space. What&apos;s your size is going to be? Will you even fit?&lt;br&gt;&lt;br&gt;— SwiftUI negotiator&lt;/blockquote&gt;&lt;p&gt;&lt;code&gt;ProposedViewSize&lt;/code&gt; is like a &lt;code&gt;CGSize&lt;/code&gt; that also can have some specific values. &lt;/p&gt;&lt;ul&gt;&lt;li&gt;The &lt;code&gt;zero&lt;/code&gt; proposal; the view responds with its minimum size.&lt;/li&gt;&lt;li&gt;The &lt;code&gt;infinity&lt;/code&gt; proposal; the view responds with its maximum size.&lt;/li&gt;&lt;li&gt;The &lt;code&gt;unspecified&lt;/code&gt; proposal; the view responds with its ideal size.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;You can also access &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; of proposal if it is not of the above values. &lt;/p&gt;&lt;p&gt;The proposal can have one dimension fixed and the second one as &lt;code&gt;nil&lt;/code&gt;. For example, an &lt;code&gt;HStack&lt;/code&gt; might measure the flexibility of its subviews’ widths, while using a fixed value for the height.&lt;/p&gt;&lt;h4 id=&quot;subviews&quot;&gt;subviews&lt;/h4&gt;&lt;p&gt;It is just a container of subviews&apos; proxies &lt;code&gt;LayoutSubview&lt;/code&gt;. Through it, you can ask subviews about their size, and also give them your proposal&lt;/p&gt;&lt;blockquote&gt;Dear subview, I give you this much space. What&apos;s your size is going to be?&lt;br&gt;&lt;br&gt;— Custom Layout negotiator&lt;/blockquote&gt;&lt;p&gt;You can ask for subview size through &lt;/p&gt;&lt;p&gt;&lt;code&gt;func sizeThatFits(ProposedViewSize) -&amp;gt; CGSize&lt;/code&gt; &lt;/p&gt;&lt;p&gt;and&lt;/p&gt;&lt;p&gt; &lt;code&gt;func dimensions(in: ProposedViewSize) -&amp;gt; ViewDimensions&lt;/code&gt;&lt;/p&gt;&lt;h4 id=&quot;cache&quot;&gt;cache&lt;/h4&gt;&lt;p&gt;It is a cache provided by your &lt;code&gt;makeCache(subviews:)&lt;/code&gt; function. It also can be &lt;code&gt;Void&lt;/code&gt; (no cache).&lt;/p&gt;&lt;h3 id=&quot;method-placesubviews&quot;&gt;Method &lt;code&gt;placeSubviews&lt;/code&gt;&lt;/h3&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;func placeSubviews(
    in bounds: CGRect,
    proposal: ProposedViewSize,
    subviews: Self.Subviews,
    cache: inout Self.Cache
)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It&apos;s where the magic happens. In this method (and only this) you are given bounds for your view and subviews for your disposal. &lt;/p&gt;&lt;p&gt;To place subviews, you need to call &lt;code&gt;place&lt;/code&gt; method on &lt;code&gt;subviews&lt;/code&gt; elements.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;func place(
    at position: CGPoint,
    anchor: UnitPoint = .topLeading,
    proposal: ProposedViewSize
)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The definition is pretty self-explanatory. For every subview, you need to specify a point to place it, an anchor for this point, and &lt;strong&gt;your&lt;/strong&gt; proposal for the selected subview.&lt;/p&gt;&lt;h4 id=&quot;bounds&quot;&gt;bounds&lt;/h4&gt;&lt;p&gt;It&apos;s bounds for your view to use. It is one of your &lt;code&gt;sizeThatFits&lt;/code&gt; outputs. &lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;While it is named &lt;code&gt;bounds&lt;/code&gt;, it is actually &lt;code&gt;frame&lt;/code&gt;. So, the origin point is also specified &lt;strong&gt;and you need to arrange subviews with respect to that&lt;/strong&gt;&lt;/div&gt;&lt;/div&gt;&lt;h4 id=&quot;proposal-1&quot;&gt;proposal&lt;/h4&gt;&lt;p&gt;The size proposal from which the container generated the size that the parent used to create the &lt;code&gt;bounds&lt;/code&gt; parameter.&lt;/p&gt;&lt;h3 id=&quot;about-caching&quot;&gt;About caching&lt;/h3&gt;&lt;p&gt;You may not use it, but usually, some subviews-concerned calculations can be cached which is a good practice and great for performance. &lt;/p&gt;&lt;p&gt;When subviews are changed, &lt;code&gt;func updateCache(inout Self.Cache, subviews: Self.Subviews)&lt;/code&gt; is called. Its default implementation is just to call &lt;code&gt;makeCache(subviews:)&lt;/code&gt;.&lt;/p&gt;&lt;h2 id=&quot;creating-auto-filled-table&quot;&gt;Creating auto-filled table&lt;/h2&gt;&lt;p&gt;SwiftUI has a &lt;code&gt;Grid&lt;/code&gt; to construct table-like structures, but what if you have an unknown number of subviews? Then, you need to construct &lt;code&gt;GridRow&lt;/code&gt; somehow correctly.&lt;/p&gt;&lt;p&gt;Let&apos;s better use the new &lt;code&gt;Layout&lt;/code&gt; protocol feature!&lt;/p&gt;&lt;!--kg-card-begin: html--&gt;&lt;section class=&quot;custom-replace&quot; data-replace=&quot;.subscription-article&quot;&gt;
    &lt;a class=&quot;gh-portal-triggerbtn-container&quot;&gt;Subscribe and don&apos;t miss posts!&lt;/a&gt;
&lt;/section&gt;&lt;!--kg-card-end: html--&gt;&lt;h3 id=&quot;calculating-sizes&quot;&gt;Calculating sizes&lt;/h3&gt;&lt;p&gt;Deciding what size the result view will have is relatively simple. &lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;public func sizeThatFits(
        proposal: ProposedViewSize,
        subviews: Subviews,
        cache: inout ()
    ) -&amp;gt; CGSize {
    
        let subviewProposal = getSubviewProposal(
            subviewsCount: subviews.count,
            from: proposal
        )
        
        let rowHeights = getRowHeights(
        	subviews: subviews,
            globalProposal: proposal
        )
        
        let resultWidth = proposal.width ?? 
        		((subviewProposal.width ?? 0) * CGFloat(columnsNumber))
        return CGSize(
            width: resultWidth,
            height: rowHeights.reduce(0, +)
        )
    }&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It uses several helper-functions&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;/**
 Get array of heights for every row.
 Just get max height on every row
 */
private func getRowHeights(subviews: Subviews, subviewProposal: ProposedViewSize) -&amp;gt; [CGFloat] {
    var subviewProposalNoHLimit = subviewProposal
    subviewProposalNoHLimit.height = .infinity
    
    var rowHeights = [CGFloat]()
    var index = 0
    while index &amp;lt; subviews.count {
        var rowMax: CGFloat = 0
        for _ in 0..&amp;lt;columnsNumber where index &amp;lt; subviews.count {
            let size = subviews[index].sizeThatFits(subviewProposalNoHLimit)
            rowMax = max(rowMax, size.height)
            index += 1
        }
        rowHeights.append(rowMax)
    }
    return rowHeights
}

/**
 Calculates proposal for subview — one cell in table
 */
func getSubviewProposal(subviewsCount: Int, from globalProposal: ProposedViewSize) -&amp;gt; ProposedViewSize {
    let rowHeight = max(ceil(Double(subviewsCount / columnsNumber)), 1)
    return ProposedViewSize(
        width: (globalProposal.width ?? 0)
                        / CGFloat(columnsNumber),
        height: (globalProposal.height ?? 0) / rowHeight
    )
}&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;placing-subviews&quot;&gt;Placing subviews&lt;/h2&gt;&lt;p&gt;Finally, we just need to carefully place views on their places. Just iterating over subviews and calculating their &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt; position.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;public func placeSubviews(
    in bounds: CGRect,
    proposal: ProposedViewSize,
    subviews: Subviews,
    cache: inout ()
) {
    var subviewProposal = getSubviewProposal(
        subviewsCount: subviews.count,
        from: proposal
    )
    let colRealWidth = subviewProposal.width ?? 0
    let rowHeights = getRowHeights(subviews: subviews, subviewProposal: subviewProposal)
    
    var curPos: CGFloat = bounds.minX
    var curHeight: CGFloat = bounds.minY
    
    var rowIndex = 0
    for (index, subview) in subviews.enumerated() {
        subviewProposal.height = rowHeights[rowIndex]
        let size = subview.dimensions(in: subviewProposal)
        
        subview.place(
            at: CGPoint(x: curPos, y: curHeight),
            anchor: .topLeading,
            proposal: subviewProposal
        )
        
        if index % columnsNumber == columnsNumber - 1 {
            curPos = bounds.minX
            curHeight += rowHeights[rowIndex]
            rowIndex += 1
        } else {
        	curPos += colRealWidth
        }
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;example&quot;&gt;Example&lt;/h2&gt;&lt;p&gt;Now, we can construct a table with the needed number of columns as easy as just a regular view.&lt;/p&gt;&lt;figure class=&quot;kg-card kg-image-card&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2022/08/Screenshot-2022-08-11-at-23.35.29.png&quot; class=&quot;kg-image&quot; alt loading=&quot;lazy&quot; width=&quot;1712&quot; height=&quot;1000&quot; srcset=&quot;https://alexdremov.me/content/images/size/w600/2022/08/Screenshot-2022-08-11-at-23.35.29.png 600w, https://alexdremov.me/content/images/size/w1000/2022/08/Screenshot-2022-08-11-at-23.35.29.png 1000w, https://alexdremov.me/content/images/size/w1600/2022/08/Screenshot-2022-08-11-at-23.35.29.png 1600w, https://alexdremov.me/content/images/2022/08/Screenshot-2022-08-11-at-23.35.29.png 1712w&quot; sizes=&quot;(min-width: 720px) 720px&quot;&gt;&lt;/figure&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;ColumnsLayout(columnsNumber: 2) {
    VStack {
        Text(&quot;That&apos;s one view&quot;)
        Image(systemName: &quot;tortoise.fill&quot;)
    }
    .padding()
    .border(.red)
    Text(&quot;That&apos;s the second view &quot;)
        .padding()
        .border(.red)
    Text(&quot;That&apos;s the third view with long lines that are warped automatically&quot;)
        .fixedSize(horizontal: false, vertical: true)
        .padding()
        .border(.red)
}
.border(.blue)
.padding()&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And it magically re-assembles after changing the number of columns to three.&lt;/p&gt;&lt;figure class=&quot;kg-card kg-image-card&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2022/08/Screenshot-2022-08-11-at-23.36.48.png&quot; class=&quot;kg-image&quot; alt loading=&quot;lazy&quot; width=&quot;1708&quot; height=&quot;894&quot; srcset=&quot;https://alexdremov.me/content/images/size/w600/2022/08/Screenshot-2022-08-11-at-23.36.48.png 600w, https://alexdremov.me/content/images/size/w1000/2022/08/Screenshot-2022-08-11-at-23.36.48.png 1000w, https://alexdremov.me/content/images/size/w1600/2022/08/Screenshot-2022-08-11-at-23.36.48.png 1600w, https://alexdremov.me/content/images/2022/08/Screenshot-2022-08-11-at-23.36.48.png 1708w&quot; sizes=&quot;(min-width: 720px) 720px&quot;&gt;&lt;/figure&gt;&lt;h2 id=&quot;final-notes&quot;&gt;Final notes&lt;/h2&gt;&lt;p&gt;I believe that you see how powerful this tool is. For example, &lt;a href=&quot;https://developer.apple.com/documentation/swiftui/composing_custom_layouts_with_swiftui?ref=alexdremov.me&quot;&gt;Apple creates a radial view in their example&lt;/a&gt; with &lt;code&gt;Layout&lt;/code&gt; protocol.&lt;/p&gt;&lt;figure class=&quot;kg-card kg-image-card&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2022/08/Screenshot-2022-08-11-at-23.43.38.png&quot; class=&quot;kg-image&quot; alt loading=&quot;lazy&quot; width=&quot;1914&quot; height=&quot;1400&quot; srcset=&quot;https://alexdremov.me/content/images/size/w600/2022/08/Screenshot-2022-08-11-at-23.43.38.png 600w, https://alexdremov.me/content/images/size/w1000/2022/08/Screenshot-2022-08-11-at-23.43.38.png 1000w, https://alexdremov.me/content/images/size/w1600/2022/08/Screenshot-2022-08-11-at-23.43.38.png 1600w, https://alexdremov.me/content/images/2022/08/Screenshot-2022-08-11-at-23.43.38.png 1914w&quot; sizes=&quot;(min-width: 720px) 720px&quot;&gt;&lt;/figure&gt;&lt;p&gt;So, it&apos;s only up to you how to place views inside your container and it&apos;s finally a room of flexibility so needed for SwiftUI in iOS 16.&lt;/p&gt;
        &lt;div class=&quot;kg-card kg-file-card &quot;&gt;
            &lt;a class=&quot;kg-file-card-container&quot; href=&quot;https://alexdremov.me/content/files/2022/08/ColumnsLayout.swift&quot; title=&quot;Download&quot; download&gt;
                &lt;div class=&quot;kg-file-card-contents&quot;&gt;
                    &lt;div class=&quot;kg-file-card-title&quot;&gt;ColumnsLayout&lt;/div&gt;
                    &lt;div class=&quot;kg-file-card-caption&quot;&gt;Complete example&lt;/div&gt;
                    &lt;div class=&quot;kg-file-card-metadata&quot;&gt;
                        &lt;div class=&quot;kg-file-card-filename&quot;&gt;ColumnsLayout.swift&lt;/div&gt;
                        &lt;div class=&quot;kg-file-card-filesize&quot;&gt;4 KB&lt;/div&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
                &lt;div class=&quot;kg-file-card-icon&quot;&gt;
                    &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot;&gt;&lt;defs&gt;&lt;style&gt;.a{fill:none;stroke:currentColor;stroke-linecap:round;stroke-linejoin:round;stroke-width:1.5px;}&lt;/style&gt;&lt;/defs&gt;&lt;title&gt;download-circle&lt;/title&gt;&lt;polyline class=&quot;a&quot; points=&quot;8.25 14.25 12 18 15.75 14.25&quot;/&gt;&lt;line class=&quot;a&quot; x1=&quot;12&quot; y1=&quot;6.75&quot; x2=&quot;12&quot; y2=&quot;18&quot;/&gt;&lt;circle class=&quot;a&quot; cx=&quot;12&quot; cy=&quot;12&quot; r=&quot;11.25&quot;/&gt;&lt;/svg&gt;
                &lt;/div&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;p&gt;Let me know what you think about it in the comments!&lt;/p&gt;</content:encoded></item><item><title>SwiftUI Navigation Is a Mess. Here’s What You Can Do</title><link>https://alexdremov.me/swiftui-navigation-is-a-mess-heres-what-you-can-do/</link><guid isPermaLink="true">https://alexdremov.me/swiftui-navigation-is-a-mess-heres-what-you-can-do/</guid><description>Managing navigation in pure SwiftUI is hard and leads to messy solutions. In this post, I will show you how you can manage views effectively</description><pubDate>Sat, 30 Jul 2022 17:55:14 GMT</pubDate><content:encoded>&lt;h2 id=&quot;why-messy&quot;&gt;Why messy?&lt;/h2&gt;&lt;p&gt;It&apos;s because of the core idea of SwiftUI — a view is a function of the state, or a view is state-driven. Don&apos;t get me wrong, this concept is great, but SwiftUI&apos;s navigation is not this advanced yet.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;The view is a function of the state and navigation is not an exception&lt;/div&gt;&lt;/div&gt;&lt;p&gt;However, SwiftUI does not have the means to construct robust navigation inside your app.&lt;/p&gt;&lt;h2 id=&quot;messy-example&quot;&gt;Messy example&lt;/h2&gt;&lt;p&gt;Consider the common case of the onboarding screen when you need to present some sequence of views with nice transitions. What can you do with SwiftUI? Probably, create an &lt;code&gt;enum&lt;/code&gt; that tells which screen is active and then use &lt;code&gt;switch&lt;/code&gt; to present the sequence of views.&lt;/p&gt;&lt;figure class=&quot;kg-card kg-video-card kg-width-wide&quot; data-kg-thumbnail=&quot;https://alexdremov.me/content/images/2022/07/media-thumbnail-ember46.jpg&quot; data-kg-custom-thumbnail=&quot;https://alexdremov.me/content/images/2022/07/swiftUIOnboarding-0003.png&quot;&gt;
            &lt;div class=&quot;kg-video-container&quot;&gt;
                &lt;video src=&quot;https://alexdremov.me/content/media/2022/07/swiftUIOnboarding.mp4&quot; poster=&quot;https://img.spacergif.org/v1/1706x802/0a/spacer.png&quot; width=&quot;1706&quot; height=&quot;802&quot; playsinline=&quot;&quot; preload=&quot;metadata&quot; style=&quot;background: transparent url(&apos;https://alexdremov.me/content/images/2022/07/swiftUIOnboarding-0003.png&apos;) 50% 50% / cover no-repeat;&quot;&gt;&lt;/video&gt;
                &lt;div class=&quot;kg-video-overlay&quot;&gt;
                    &lt;button class=&quot;kg-video-large-play-icon&quot; aria-label=&quot;Play video&quot;&gt;
                        &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot;&gt;
                            &lt;path d=&quot;M23.14 10.608 2.253.164A1.559 1.559 0 0 0 0 1.557v20.887a1.558 1.558 0 0 0 2.253 1.392L23.14 13.393a1.557 1.557 0 0 0 0-2.785Z&quot;&gt;&lt;/path&gt;
                        &lt;/svg&gt;
                    &lt;/button&gt;
                &lt;/div&gt;
                &lt;div class=&quot;kg-video-player-container&quot;&gt;
                    &lt;div class=&quot;kg-video-player&quot;&gt;
                        &lt;button class=&quot;kg-video-play-icon&quot; aria-label=&quot;Play video&quot;&gt;
                            &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot;&gt;
                                &lt;path d=&quot;M23.14 10.608 2.253.164A1.559 1.559 0 0 0 0 1.557v20.887a1.558 1.558 0 0 0 2.253 1.392L23.14 13.393a1.557 1.557 0 0 0 0-2.785Z&quot;&gt;&lt;/path&gt;
                            &lt;/svg&gt;
                        &lt;/button&gt;
                        &lt;button class=&quot;kg-video-pause-icon kg-video-hide&quot; aria-label=&quot;Pause video&quot;&gt;
                            &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot;&gt;
                                &lt;rect x=&quot;3&quot; y=&quot;1&quot; width=&quot;7&quot; height=&quot;22&quot; rx=&quot;1.5&quot; ry=&quot;1.5&quot;&gt;&lt;/rect&gt;
                                &lt;rect x=&quot;14&quot; y=&quot;1&quot; width=&quot;7&quot; height=&quot;22&quot; rx=&quot;1.5&quot; ry=&quot;1.5&quot;&gt;&lt;/rect&gt;
                            &lt;/svg&gt;
                        &lt;/button&gt;
                        &lt;span class=&quot;kg-video-current-time&quot;&gt;0:00&lt;/span&gt;
                        &lt;div class=&quot;kg-video-time&quot;&gt;
                            /&lt;span class=&quot;kg-video-duration&quot;&gt;0:14&lt;/span&gt;
                        &lt;/div&gt;
                        &lt;input type=&quot;range&quot; class=&quot;kg-video-seek-slider&quot; max=&quot;100&quot; value=&quot;0&quot;&gt;
                        &lt;button class=&quot;kg-video-playback-rate&quot; aria-label=&quot;Adjust playback speed&quot;&gt;1×&lt;/button&gt;
                        &lt;button class=&quot;kg-video-unmute-icon&quot; aria-label=&quot;Unmute&quot;&gt;
                            &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot;&gt;
                                &lt;path d=&quot;M15.189 2.021a9.728 9.728 0 0 0-7.924 4.85.249.249 0 0 1-.221.133H5.25a3 3 0 0 0-3 3v2a3 3 0 0 0 3 3h1.794a.249.249 0 0 1 .221.133 9.73 9.73 0 0 0 7.924 4.85h.06a1 1 0 0 0 1-1V3.02a1 1 0 0 0-1.06-.998Z&quot;&gt;&lt;/path&gt;
                            &lt;/svg&gt;
                        &lt;/button&gt;
                        &lt;button class=&quot;kg-video-mute-icon kg-video-hide&quot; aria-label=&quot;Mute&quot;&gt;
                            &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot;&gt;
                                &lt;path d=&quot;M16.177 4.3a.248.248 0 0 0 .073-.176v-1.1a1 1 0 0 0-1.061-1 9.728 9.728 0 0 0-7.924 4.85.249.249 0 0 1-.221.133H5.25a3 3 0 0 0-3 3v2a3 3 0 0 0 3 3h.114a.251.251 0 0 0 .177-.073ZM23.707 1.706A1 1 0 0 0 22.293.292l-22 22a1 1 0 0 0 0 1.414l.009.009a1 1 0 0 0 1.405-.009l6.63-6.631A.251.251 0 0 1 8.515 17a.245.245 0 0 1 .177.075 10.081 10.081 0 0 0 6.5 2.92 1 1 0 0 0 1.061-1V9.266a.247.247 0 0 1 .073-.176Z&quot;&gt;&lt;/path&gt;
                            &lt;/svg&gt;
                        &lt;/button&gt;
                        &lt;input type=&quot;range&quot; class=&quot;kg-video-volume-slider&quot; max=&quot;100&quot; value=&quot;100&quot;&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            
        &lt;/figure&gt;&lt;p&gt;What if you need to modify the order or change the number of views? You&apos;ll need to modify the corresponding &lt;code&gt;enum&lt;/code&gt;, modify the logic of switching inside the views, and other stuff.&lt;/p&gt;&lt;p&gt;Not so flexible, right?&lt;/p&gt;&lt;p&gt;Oh, and then you decide to present one view right in the middle through &lt;code&gt;.sheet&lt;/code&gt;. That&apos;s when &lt;em&gt;the mess&lt;/em&gt; starts to show up. You create an additional &lt;code&gt;@State&lt;/code&gt; to check if the sheet is open, make sure that it&apos;s updated correctly, and restructure the &lt;code&gt;switch&lt;/code&gt; block that you used before. &lt;/p&gt;&lt;p&gt;Now, it&apos;s a chaotic view that is prone to unexpected bugs.&lt;/p&gt;&lt;h2 id=&quot;existing-navigation-views&quot;&gt;Existing navigation views&lt;/h2&gt;&lt;p&gt;The most obvious one is &lt;a href=&quot;https://developer.apple.com/documentation/swiftui/navigationview?ref=alexdremov.me&quot;&gt;NavigationView&lt;/a&gt; which is deprecated in the new iOS 16.&lt;/p&gt;&lt;figure class=&quot;kg-card kg-image-card kg-card-hascaption&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2022/07/NavigationView-1@2x.png&quot; class=&quot;kg-image&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;1458&quot; height=&quot;676&quot; srcset=&quot;https://alexdremov.me/content/images/size/w600/2022/07/NavigationView-1@2x.png 600w, https://alexdremov.me/content/images/size/w1000/2022/07/NavigationView-1@2x.png 1000w, https://alexdremov.me/content/images/2022/07/NavigationView-1@2x.png 1458w&quot; sizes=&quot;(min-width: 720px) 720px&quot;&gt;&lt;figcaption&gt;&lt;span style=&quot;white-space: pre-wrap;&quot;&gt;Image by https://developer.apple.com/documentation/swiftui/navigationview&lt;/span&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;Using &lt;code&gt;NavigationLink&lt;/code&gt;, it can present new views and also adds a &quot;back&quot; button to return to the previous view.&lt;/p&gt;&lt;p&gt;And it does not support programmatic navigation.&lt;/p&gt;&lt;p&gt;Apple presented a new &lt;a href=&quot;https://developer.apple.com/documentation/swiftui/navigationstack?ref=alexdremov.me&quot;&gt;NavigationStack&lt;/a&gt; that addresses this issue &lt;strong&gt;but it is still not flexible enough. &lt;/strong&gt;For example, I like to have the ability to modify the view whatever I want, but NavugationStack inserts back buttons. Also, it does not support different transitions. While it is nice to see SwiftUI develop in this direction, yet we are not there.&lt;/p&gt;&lt;p&gt;So, even in iOS 16, SwiftUI is not powerful enough to manage any kind of navigation you can come up with.&lt;/p&gt;&lt;p&gt;And &lt;code&gt;.sheet()&lt;/code&gt;. &lt;code&gt;NavigationStack&lt;/code&gt; does not make it easier to handle &lt;code&gt;.sheet()&lt;/code&gt; either.&lt;/p&gt;&lt;h2 id=&quot;designing-a-flexible-navigation-library&quot;&gt;Designing a flexible navigation library&lt;/h2&gt;&lt;p&gt;I decided to create a library with several requirements:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Programmatic views navigation&lt;/li&gt;&lt;li&gt;Ability to present a sequence of views&lt;/li&gt;&lt;li&gt;Support for any SwiftUI transition and Animation&lt;/li&gt;&lt;li&gt;Completely state-driven: no singletons or environment objects&lt;/li&gt;&lt;li&gt;Handle &lt;code&gt;.sheet()&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Sounds cool, right?&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Straight to the point, I was able to create such a library.&lt;/strong&gt;&lt;/p&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://github.com/AlexRoar/PathPresenter?ref=alexdremov.me&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;GitHub - AlexRoar/PathPresenter: Pure SwiftUI state-driven library to present view sequences and hierarchies.&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;Pure SwiftUI state-driven library to present view sequences and hierarchies. - GitHub - AlexRoar/PathPresenter: Pure SwiftUI state-driven library to present view sequences and hierarchies.&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://github.com/fluidicon.png&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;GitHub&lt;/span&gt;&lt;span class=&quot;kg-bookmark-publisher&quot;&gt;AlexRoar&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://opengraph.githubassets.com/60817d12634f147ff4d20950055d1a547a93bf5b2fc1d224fe7241d9720670de/AlexRoar/PathPresenter&quot; alt=&quot;&quot; onerror=&quot;this.style.display = &apos;none&apos;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;I am always open to objective criticism and requests for a new feature. Do not hesitate to open an issue on GitHub!&lt;/div&gt;&lt;/div&gt;&lt;p&gt;So, if you just want a nice tool for the things I listed above, you can stop here. Now, let&apos;s see how I did it.&lt;/p&gt;&lt;h2 id=&quot;ways-to-present&quot;&gt;Ways to present&lt;/h2&gt;&lt;p&gt;At the core of the library is a structure that stores views and information about how to present them. Possible options for presentation are&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;enum PathType {
    /**
    * Just show a view. No animation, no transition.
    * Show view above all other views
    */
    case plain

    /**
    * Show view with in and out transitions.
    * Transition animation also can be specified.
    */
    case animated(transition: AnyTransition, animation: Animation)

    /**
    * Show view in .sheet()
    */
    case sheet(onDismiss: Action)
}&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;❗&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Note that presenting through &lt;code spellcheck=&quot;false&quot; style=&quot;white-space: pre-wrap;&quot;&gt;.sheet()&lt;/code&gt; is as easy as just presenting any other view.&lt;/div&gt;&lt;/div&gt;&lt;p&gt;So, you can present the view without any animation, present it with needed transitions, and present it in a sheet.&lt;/p&gt;&lt;h2 id=&quot;path&quot;&gt;Path&lt;/h2&gt;&lt;p&gt;This structure stores information about views. It just stores an array of type-erased views with presentation type information. You can append views on top and remove them from the top.&lt;/p&gt;&lt;p&gt;Honestly, I got this Idea from &lt;code&gt;NavigationStack&lt;/code&gt; as previously I tried to do a similar library with the ability to insert in the middle. However, I encountered several issues concerning animation when inserting it in the middle. Probably, it&apos;s possible to do it.&lt;/p&gt;&lt;h2 id=&quot;the-view-itself&quot;&gt;The view itself&lt;/h2&gt;&lt;p&gt;The key idea is how to present this array of views. PathPresenter uses ZStack to do that. It presents only views that are not marked as &lt;code&gt;.sheet&lt;/code&gt; type.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;ZStack(alignment: .topLeading) {
	Color.clear
    if let rootView = rootView, !sheet {
        rootView.zIndex(-1)
    }
    
    ForEach(content, id: \.hashValue) { elem in
        switch elem {
        case .plain(let view, hash: _, zIndex: let zIndex):
        	view.zIndex(zIndex)
        case .animated(
        	let view,
            transition: let transition,
            animation: _,
            hash: _,
            zIndex: let zIndex):
            view.zIndex(zIndex).transition(transition)
        case .sheet(let view, _, _):
            view
		}
	}
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The view manages &lt;code&gt;.sheet&lt;/code&gt; inside of itself and decides when it needs to be presented. If the last element must be presented as a sheet, then the sheet is activated.&lt;/p&gt;&lt;p&gt;That&apos;s it&lt;/p&gt;&lt;p&gt;I covered the core concepts of the implementation. You can check GitHub and see the full implementation. The code is fully documented and you can ask me about anything in the comments.&lt;/p&gt;&lt;h2 id=&quot;onboarding-example-with-pathpresenter&quot;&gt;Onboarding example with PathPresenter&lt;/h2&gt;&lt;p&gt;We simply use the library ;)&lt;/p&gt;&lt;p&gt;To construct the Path, we append needed views to it:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;let data = [
  (text: &quot;Nice&quot;, subtext: &quot;Onborading sequence of screens&quot;),
  (text: &quot;OK&quot;, subtext: &quot;But how to do it with SwiftUI?&quot;),
  (text: &quot;So that&quot;, subtext: &quot;it is nice and shiny&quot;),
]

let typeCommon: PathPresenter.PathType =
  .animated(
    transition: .asymmetric(
      insertion: .move(edge: .trailing),
      removal: .move(edge: .leading)),
    animation: .easeInOut
  )

path.append(
  data: data,
  type: typeCommon
) { (text, subtext) in
  boldText(
    text: text,
    subtext: subtext
  )
}

path.append(
  boldText(
    text: &quot;And also&quot;,
    subtext: &quot;flexible enough to cover all your needs&quot;
  ), type: .sheet(onDismiss: {}))

path.append(
  boldText(
    text: &quot;Read the post&quot;,
    subtext: &quot;to find the solution&quot;
  ), type: typeCommon)

path.reverse()&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;That&apos;s it. Then, you use &lt;code&gt;RoutingView(path: $path)&lt;/code&gt; to present this path. You can check out the full example in this project:&lt;/p&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://github.com/AlexRoar/PathPresenterExample/tree/main?ref=alexdremov.me&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;GitHub - AlexRoar/PathPresenterExample&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;Contribute to AlexRoar/PathPresenterExample development by creating an account on GitHub.&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://github.com/fluidicon.png&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;GitHub&lt;/span&gt;&lt;span class=&quot;kg-bookmark-publisher&quot;&gt;AlexRoar&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://opengraph.githubassets.com/11edd4ce6201a1929e46bfe65292d59bc53cbf5d6b8730fa5ded0b7344f4fb29/AlexRoar/PathPresenterExample&quot; alt=&quot;&quot; onerror=&quot;this.style.display = &apos;none&apos;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;p&gt;There are also similar frameworks available on GitHub. Recently I discovered quite a nice one:&lt;/p&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://github.com/johnpatrickmorgan/FlowStacks?ref=alexdremov.me&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;GitHub - johnpatrickmorgan/FlowStacks: FlowStacks allows you to hoist SwiftUI navigation and presentation state into a Coordinator&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;FlowStacks allows you to hoist SwiftUI navigation and presentation state into a Coordinator - GitHub - johnpatrickmorgan/FlowStacks: FlowStacks allows you to hoist SwiftUI navigation and presentati...&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://github.com/fluidicon.png&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;GitHub&lt;/span&gt;&lt;span class=&quot;kg-bookmark-publisher&quot;&gt;johnpatrickmorgan&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://opengraph.githubassets.com/c4f40a4363f317ae1c8fb69c0fd9a888dcfd7718b0be9c0afaa7f3ccfe2f669d/johnpatrickmorgan/FlowStacks&quot; alt=&quot;&quot; onerror=&quot;this.style.display = &apos;none&apos;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;h2 id=&quot;one-step-further&quot;&gt;One Step Further&lt;/h2&gt;&lt;p&gt;Actually, navigation implementation is part of the bigger picture as it lies in the core of app&apos;s architecture. Therefore, check out my articles on modulized app architecture.&lt;/p&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://alexdremov.me/ios-app-as-a-microservice-build-robust-app-architecture/&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;iOS App As a Microservice. Build Robust App Architecture&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;MVVM, MVC, VIPER? Those all are problem-specific architectures. How to structure your app on a larger scale? More in this post&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://alexdremov.me/content/images/icon/icon-192x192-f8fe379c-3304-4470-8920-4c9989e9e492.png&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;Alex Dremov&lt;/span&gt;&lt;span class=&quot;kg-bookmark-publisher&quot;&gt;Alex Dremov&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/thumbnail/photo-1532622785990-d2c36a76f5a6-6c9150b0-dd93-4399-a1f0-e809034a764f&quot; alt=&quot;&quot; onerror=&quot;this.style.display = &apos;none&apos;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://alexdremov.me/ios-app-as-a-microservice-modularize-your-app-with-tuist/&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;iOS App As a Microservice. Modularize Your App With Tuist&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;I will cover implementation details using Tuist. It is an excellent CLI that helps you generate, maintain and interact with Xcode projects&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://alexdremov.me/content/images/icon/icon-192x192-e234c319-2985-40b0-b39d-ff7f204d0d1d.png&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;Alex Dremov&lt;/span&gt;&lt;span class=&quot;kg-bookmark-publisher&quot;&gt;Alex Dremov&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/thumbnail/photo-1613645695025-20e3f38de4a6-98354ff7-7aef-4923-8028-ed1583f076ea&quot; alt=&quot;&quot; onerror=&quot;this.style.display = &apos;none&apos;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://alexdremov.me/ios-app-as-a-microservice-using-swiftui-in-modular-app/&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;iOS App As a Microservice. Using SwiftUI in Modular App&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;How to implement modular architecture effectively with SwiftUI? In this post, I will describe tips on using SwiftUI with modular app design&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://alexdremov.me/content/images/icon/icon-192x192-24f1b1c3-1b25-4133-8d27-62da8a0139ad.png&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;Alex Dremov&lt;/span&gt;&lt;span class=&quot;kg-bookmark-publisher&quot;&gt;Alex Dremov&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/thumbnail/photo-1581291518633-83b4ebd1d83e-b025cea9-32fa-4aab-88c3-6f1c6b378d74&quot; alt=&quot;&quot; onerror=&quot;this.style.display = &apos;none&apos;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;h2 id=&quot;final-notes&quot;&gt;Final notes&lt;/h2&gt;&lt;p&gt;I really like how this library turned out. You freely can construct any sequence of views and build your own navigation.&lt;/p&gt;&lt;p&gt;Do not hesitate to contact me if you have noticed any bugs.&lt;/p&gt;</content:encoded></item><item><title>Suffix Automaton and Rickroll Lyrics Graph</title><link>https://alexdremov.me/suffix-automaton-and-rickroll/</link><guid isPermaLink="true">https://alexdremov.me/suffix-automaton-and-rickroll/</guid><description>Easy to understand explanation of suffix automaton with implementation. Finally, generating correct Rickroll lyrics suffix automaton </description><pubDate>Sun, 17 Jul 2022 13:57:17 GMT</pubDate><content:encoded>&lt;p&gt;Suffix automaton is a robust data structure that allows you to solve complex string-related problems such as: checking the presence of a substring in a string, counting the number of total distinct substrings, finding substring, and many others. In this article, I cover the suffix automaton algorithm, provide implementation, and finally &lt;strong&gt;create the correct rickroll lyrics automaton.&lt;/strong&gt;&lt;/p&gt;&lt;h2 id=&quot;why-rickroll&quot;&gt;Why Rickroll?&lt;/h2&gt;&lt;p&gt;First of all&lt;/p&gt;&lt;figure class=&quot;kg-card kg-embed-card&quot;&gt;&lt;iframe width=&quot;200&quot; height=&quot;113&quot; src=&quot;https://www.youtube.com/embed/iik25wqIuFo?feature=oembed&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen&gt;&lt;/iframe&gt;&lt;/figure&gt;&lt;p&gt;Now we can continue.&lt;/p&gt;&lt;p&gt;There is a meme that I&apos;ve seen a couple of times with all possible Never Gonna Give You Up central lines. It&apos;s nice, but it&apos;s not fully correct. &lt;/p&gt;&lt;figure class=&quot;kg-card kg-image-card kg-card-hascaption&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2022/05/q75ok4vlrpj61.png-1.webp&quot; class=&quot;kg-image&quot; alt loading=&quot;lazy&quot; width=&quot;960&quot; height=&quot;674&quot; srcset=&quot;https://alexdremov.me/content/images/size/w600/2022/05/q75ok4vlrpj61.png-1.webp 600w, https://alexdremov.me/content/images/2022/05/q75ok4vlrpj61.png-1.webp 960w&quot; sizes=&quot;(min-width: 720px) 720px&quot;&gt;&lt;figcaption&gt;Rickroll lyrics graph | https://www.reddit.com/r/memes/comments/lskvsq/never_gonna_make_a_flow_chart/&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;The problem is that it conforms to incorrect lines too:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Never gonna give you cry&lt;/li&gt;&lt;li&gt;Never gonna tell a lie and desert you down&lt;/li&gt;&lt;li&gt;Never gonna make you up&lt;/li&gt;&lt;li&gt;Never gonna give you down&lt;/li&gt;&lt;li&gt;Never gonna make you never&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;And many others. So, we can conclude that this graph is incorrect as incorrect lyrics must be unreachable. Then, we need to correct this immense mistake against humanity and generate the correct automaton for Rickroll lyrics.&lt;/p&gt;&lt;h2 id=&quot;what-is-the-suffix-automaton&quot;&gt;What is the suffix automaton?&lt;/h2&gt;&lt;p&gt;Intuitively, it&apos;s a data structure that contains information about all substrings of a string and stores it in compressed form. More specifically, it&apos;s a directed acyclic word graph in which each node is a state and all edges are transitions between these states by some letter.&lt;/p&gt;&lt;p&gt;Each state corresponds to some substring in the initial string. There is also one start state and some states are marked as terminal. We also require that suffix automaton contains the minimal possible number of states.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-purple&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💥&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;So, if each node is some substring and each edge is a transition by some letter, by navigating through this graph we can collect information about substrings.&lt;/div&gt;&lt;/div&gt;&lt;p&gt;If a substring is not presented in the text, then this state will be unreachable. There&apos;s simply no state or for an absent substring. So, at some point we will need transition that does not exists. &lt;/p&gt;&lt;p&gt;Here is the example of suffix automaton for string &lt;code&gt;abcbac&lt;/code&gt;.&lt;/p&gt;&lt;figure class=&quot;kg-card kg-image-card kg-width-full kg-card-hascaption&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2022/05/graphviz-5.svg&quot; class=&quot;kg-image&quot; alt=&quot;Suffix automaton for abcbac&quot; loading=&quot;lazy&quot; width=&quot;1102&quot; height=&quot;277&quot;&gt;&lt;figcaption&gt;Suffix automaton for abcbac&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;The leftmost state corresponds to empty string (start state) and the rightmost corresponds to the whole string (terminal). Notice that if you start from the start and somehow end up in the terminal state, then the path you followed corresponds to some suffix of the string. Also, every substring corresponds to one path from the start.&lt;/p&gt;&lt;h2 id=&quot;rickroll-suffix-automate&quot;&gt;Rickroll suffix automate&lt;/h2&gt;&lt;p&gt;For this, I generated suffix automate for every line and then merged these suffix automates. &lt;/p&gt;&lt;figure class=&quot;kg-card kg-image-card kg-width-full kg-card-hascaption&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2022/05/graphviz-7.svg&quot; class=&quot;kg-image&quot; alt loading=&quot;lazy&quot; width=&quot;7926&quot; height=&quot;8112&quot;&gt;&lt;figcaption&gt;Full Never Gonna Give You Up lyrics&lt;/figcaption&gt;&lt;/figure&gt;&lt;h2 id=&quot;final-thoughts&quot;&gt;Final thoughts&lt;/h2&gt;&lt;p&gt;Even though this graph is not as nice as presented in the meme, it&apos;s &lt;strong&gt;correct. &lt;/strong&gt;You can explore the graph above by yourself; it&apos;s actually fun.&lt;/p&gt;&lt;p&gt; In the next post, I will discuss how I have built this graph using the suffix automaton. Subscribe so you do not miss it!&lt;/p&gt;</content:encoded></item><item><title>Using Threads in Swift</title><link>https://alexdremov.me/using-threads-in-swift/</link><guid isPermaLink="true">https://alexdremov.me/using-threads-in-swift/</guid><description>Swift provides DispatchQueue as an excellent layer above raw threads. But sometimes you want to use a low-level thread API</description><pubDate>Fri, 13 May 2022 08:44:49 GMT</pubDate><content:encoded>&lt;p&gt;Swift provides DispatchQueue as an excellent layer above raw threads. But sometimes you want to create a new thread dedicated to some specific task. Or maybe implement your own concurrent executor. Swift gives you access to raw threads and in this article, I&apos;ll show how to use it.&lt;/p&gt;&lt;h2 id=&quot;thread&quot;&gt;Thread&lt;/h2&gt;&lt;p&gt;Creating a thread in Swift is pretty simple using &lt;code&gt;Thread&lt;/code&gt; class. You can either specify &lt;code&gt;objc&lt;/code&gt; function through a selector as a starting point, or pass a closure, and, more convenient way, subclass &lt;code&gt;Thread&lt;/code&gt;.&lt;/p&gt;&lt;figure class=&quot;kg-card kg-code-card&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;class MyThread: Thread {
    override func main() { // Thread&apos;s starting point
        print(&quot;Hi from thread&quot;)
    }
}

let thread = MyThread()
thread.start()&lt;/code&gt;&lt;/pre&gt;&lt;figcaption&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre-wrap;&quot;&gt;Simple thread&lt;/span&gt;&lt;/p&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;The thread is not started when the initializer is called. You need to call &lt;code&gt;start()&lt;/code&gt;  method explicitly to start the thread.&lt;/p&gt;&lt;p&gt;The thread runs despite its handle returned by &lt;code&gt;Thread&lt;/code&gt; initializer. That&apos;s it — the variable can no longer exist and the thread will still run. That&apos;s fine, but you will lose the ability to control the thread: check if it&apos;s completed, wait for its completion, cancel it, etc. &lt;/p&gt;&lt;h2 id=&quot;wait-for-completion-join-a-thread&quot;&gt;Wait for completion, join a thread&lt;/h2&gt;&lt;p&gt;Swift does not provide a way to wait for the thread&apos;s completion.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;The main thread can finish before the new thread. In this case, the latter is also terminated&lt;/div&gt;&lt;/div&gt;&lt;p&gt;To wait for thread completion, we can join threads using &lt;code&gt;DispatchGroup&lt;/code&gt;&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;class MyThread: Thread {
    let waiter = DispatchGroup()

    override func start() {
        waiter.enter()
        super.start()
    }

    override func main() {
        task()
        waiter.leave()
    }

    func task() {
        print(&quot;Hi from thread&quot;)
    }

    func join() {
        waiter.wait()
    }
}

let thread = MyThread()
thread.start()

thread.join() // Waits for thread completion&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;terminate-the-thread&quot;&gt;Terminate the thread&lt;/h2&gt;&lt;p&gt;The thread terminates automatically after reaching &lt;code&gt;main&lt;/code&gt;&apos;s end. To exit the thread in advance, you can call &lt;code&gt;Thread.exit()&lt;/code&gt; function from the thread. To use it correctly with created &lt;code&gt;DispatchGroup&lt;/code&gt;, it&apos;s better to create a custom exit method:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;class MyThread: Thread {
    ...
	func exit() {
        waiter.leave()
        Thread.exit()
    }
    ...
}&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;cancel-the-thread&quot;&gt;Cancel the thread&lt;/h2&gt;&lt;p&gt;Apart from terminating the thread, you can cancel it, by calling &lt;code&gt;cancel()&lt;/code&gt; method on the thread&apos;s handle or inside the thread itself. This sets &lt;code&gt;isCancelled&lt;/code&gt; property to &lt;code&gt;true&lt;/code&gt;. &lt;/p&gt;&lt;p&gt;For this feature to work, in the thread, you need to check this flag periodically and then call exit if the flag is &lt;code&gt;true&lt;/code&gt;.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Calling cancel does not stop the thread but rather notifies it that it must be stopped. You can even ignore it, but it&apos;s not a good practice&lt;/div&gt;&lt;/div&gt;&lt;p&gt;In the example below, we can use cancel to notify the thread about a timeout.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;class MyThread: Thread {
	let waiter = DispatchGroup()

	override func start() {
		waiter.enter()
		super.start()
	}

	override func main() {
		task()
		waiter.leave()
	}

	func exit() {
		waiter.leave()
		Thread.exit()
	}

	func task() {
		let start = Date.now
		for _ in 0...100500 {
			if isCancelled {
				let seconds = Double(Date.now.timeIntervalSince(start))
				print(&quot;Cancelled after \(seconds) seconds&quot;)
				exit()
			}
			Thread.sleep(forTimeInterval: 0.01) // Long task
		}
	}

	func join() {
		waiter.wait()
	}
}

let thread = MyThread()
DispatchQueue.global().asyncAfter(
	deadline: .now().advanced(by: .seconds(5))) {
	thread.cancel()
}
thread.start()
thread.join()
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Output: &lt;code&gt;Cancelled after 5.509860992431641 seconds&lt;/code&gt;&lt;/p&gt;&lt;h2 id=&quot;concurrency-must-be-safe&quot;&gt;Concurrency must be safe&lt;/h2&gt;&lt;p&gt;Mind that in case of using several threads, shared data and structures must be thread-safe. &lt;/p&gt;&lt;p&gt;I recently released an article on Actors model in Swift. Actors is an architectural approach to concurrency&lt;/p&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://alexdremov.me/conquer-data-races-with-swift-actors/&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;Conquer Data Races with Swift Actors&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;Unleash the power of Swift concurrency with Actors! Get all the information you need in this comprehensive article&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://alexdremov.me/content/images/icon/icon-192x192-a3f748ae-7904-47b5-ac22-84eaa4ce3fc9.png&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;Alex Dremov&lt;/span&gt;&lt;span class=&quot;kg-bookmark-publisher&quot;&gt;Alex Dremov&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/thumbnail/photo-1532800783378-1bed60adaf58-8a9cd7ee-c5c0-4f4f-830a-02cff06d21eb&quot; alt=&quot;&quot; onerror=&quot;this.style.display = &apos;none&apos;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://alexdremov.me/swift-actors-common-problems-and-tips/&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;Swift Actors — Common Problems and Tips&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;Swift actors are a powerful tool. However, it is also quite a sophisticated concept that requires deep understanding to write bug-free code&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://alexdremov.me/content/images/icon/icon-192x192-dcf526f5-2db3-42ae-9616-2582a0c318f4.png&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;Alex Dremov&lt;/span&gt;&lt;span class=&quot;kg-bookmark-publisher&quot;&gt;Alex Dremov&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/thumbnail/photo-1686153490072-cc31c6bf3686-copy-706005f5-42d8-438f-b1e5-de47a1dff491.png&quot; alt=&quot;&quot; onerror=&quot;this.style.display = &apos;none&apos;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;h2 id=&quot;use-cases&quot;&gt;Use cases&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;A long-running task in your app&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;If there is a long-running task in your app, then consider creating a dedicated thread for it.  &lt;/p&gt;&lt;ul&gt;&lt;li&gt;Creating concurrent executors&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Swift&apos;s DispatchQueue and OperationQueue are powerful tools, but even their functions are limited. &lt;/p&gt;&lt;p&gt;For example, there is no &lt;a href=&quot;https://www.boost.org/doc/libs/master/doc/html/boost_asio/overview/core/strands.html?ref=alexdremov.me&quot;&gt;strand executor&lt;/a&gt; or explicit thread pool.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Writing your own Thread Pool as a practice and a pet-project&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Why not? The best way to understand how DispatchQueue works is to write your own!&lt;/p&gt;&lt;h2 id=&quot;finally&quot;&gt;Finally&lt;/h2&gt;&lt;p&gt;Check out my quick guide to async/await in Swift to get a better grip on concurrency in Swift.&lt;/p&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://alexdremov.me/quick-guide-to-async-await-in-swift/&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;Quick Guide to Async Await in Swift | Alex Dremov&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;Everything you need to know about new Swift asynchronous features. Async await, main actor, task, async get, and possible use cases — all covered.&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://alexdremov.me/assets/icons/apple-touch-icon.png&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;Alex Dremov&lt;/span&gt;&lt;span class=&quot;kg-bookmark-publisher&quot;&gt;Alex Dremov&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2022/04/slide_17.jpg&quot; alt=&quot;&quot; onerror=&quot;this.style.display = &apos;none&apos;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;p&gt;Also, the iOS section of my blog has cool staff about iOS and Swift development. Check it out!&lt;/p&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://alexdremov.me/tag/ios/&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;Alex Dremov | iOS&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;One of my favorites. Here I write about Swift and iOS development&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://alexdremov.me/assets/icons/apple-touch-icon.png&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;Alex Dremov&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://images.unsplash.com/photo-1558126372-76b529458592?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=max&amp;amp;fm=jpg&amp;amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDExfHxpb3N8ZW58MHx8fHwxNjQ5NTA0MTQ5&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;w=2000&quot; alt=&quot;&quot; onerror=&quot;this.style.display = &apos;none&apos;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;</content:encoded></item><item><title>SwiftUI Advanced Animation: Morphing Shapes</title><link>https://alexdremov.me/swiftui-advanced-animation/</link><guid isPermaLink="true">https://alexdremov.me/swiftui-advanced-animation/</guid><description>I&apos;m going to show how complex SwiftUI views can be animated efficiently using VectorArithmetic protocol with Accelerate library for computations.</description><pubDate>Thu, 05 May 2022 04:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The regular &lt;code&gt;.animate()&lt;/code&gt; function already provides a powerful way of animating views. Yet, its usage is limited to simple transformations. In this guide, I&apos;m going to show how complex SwiftUI views can be animated efficiently using &lt;code&gt;VectorArithmetic&lt;/code&gt; protocol with &lt;code&gt;Accelerate&lt;/code&gt; library for fast computations.&lt;/p&gt;&lt;h2 id=&quot;inspiration&quot;&gt;Inspiration&lt;/h2&gt;&lt;p&gt;In the course of this guide, we will make a &lt;em&gt;morphing sphere &lt;/em&gt;animation inspired by lava lamp bubbles. Some kind of wobbling lava bubbles.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;The proposed technique can be used in other even more complex animations&lt;/div&gt;&lt;/div&gt;&lt;figure class=&quot;kg-card kg-image-card kg-card-hascaption&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2022/05/ezgif.com-gif-maker-2.gif&quot; class=&quot;kg-image&quot; alt=&quot;Wobbling bubble&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;471&quot;&gt;&lt;figcaption&gt;Wobbling bubble&lt;/figcaption&gt;&lt;/figure&gt;&lt;h2 id=&quot;creating-custom-animations&quot;&gt;Creating custom animations&lt;/h2&gt;&lt;p&gt;You may think about animation as a transition between two states. And this transition must be smooth! To display this smooth transition, SwiftUI needs to know how to draw in-between stages.&lt;/p&gt;&lt;figure class=&quot;kg-card kg-image-card kg-card-hascaption&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2022/05/statesTransition.png&quot; class=&quot;kg-image&quot; alt=&quot;Smooth change between two shapes (states)&quot; loading=&quot;lazy&quot; width=&quot;1440&quot; height=&quot;545&quot; srcset=&quot;https://alexdremov.me/content/images/size/w600/2022/05/statesTransition.png 600w, https://alexdremov.me/content/images/size/w1000/2022/05/statesTransition.png 1000w, https://alexdremov.me/content/images/2022/05/statesTransition.png 1440w&quot; sizes=&quot;(min-width: 720px) 720px&quot;&gt;&lt;figcaption&gt;Smooth change between two shapes (states)&lt;/figcaption&gt;&lt;/figure&gt;&lt;h3 id=&quot;animatablevector&quot;&gt;AnimatableVector&lt;/h3&gt;&lt;p&gt;The key idea of the animation is to represent objects&apos; states with properties that can change continuously.&lt;/p&gt;&lt;p&gt;For example, if we try to animate an object&apos;s positioning and it has integer coordinates, then creating in-between frames of an object smoothly moving from one coordinate to the other is impossible. On the opposite, if the object&apos;s position is represented by a floating-point variable, then we can gradually change the object&apos;s coordinate until the new coordinate is achieved.&lt;/p&gt;&lt;p&gt;The same goes for more complicated animations. But usually, states cannot be represented by a single float variable. In this case, we are going to use &lt;code&gt;AnimatableVector&lt;/code&gt;. It represents a mathematical vector, conforming to &lt;code&gt;VectorArithmetic&lt;/code&gt; protocol.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;If two animation stages are represented by objects conforming to &lt;code&gt;VectorArithmetic&lt;/code&gt; protocol, then SwiftUI can compute in-between vectors and draw transitioning.&lt;/div&gt;&lt;/div&gt;&lt;figure class=&quot;kg-card kg-image-card&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2022/05/vectoranimateex.png&quot; class=&quot;kg-image&quot; alt loading=&quot;lazy&quot; width=&quot;754&quot; height=&quot;482&quot; srcset=&quot;https://alexdremov.me/content/images/size/w600/2022/05/vectoranimateex.png 600w, https://alexdremov.me/content/images/2022/05/vectoranimateex.png 754w&quot; sizes=&quot;(min-width: 720px) 720px&quot;&gt;&lt;/figure&gt;&lt;p&gt;The &lt;code&gt;AnimatableVector&lt;/code&gt; is pretty simple. We store an array of coordinates and define basic math operations for them. In the code below Accelerate is used for fast computations. &lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-purple&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💥&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Accelerate can introduce too much overhead when the vector contains only several values. So, if your animation can be represented with a few values, then consider rewriting operators without Accelerate&lt;/div&gt;&lt;/div&gt;&lt;figure class=&quot;kg-card kg-code-card&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;import enum Accelerate.vDSP

struct AnimatableVector: VectorArithmetic {
    var values: [Float]
    
    static var zero = AnimatableVector(values: [0.0])

    static func + (lhs: AnimatableVector, rhs: AnimatableVector) -&amp;gt; AnimatableVector {
        let count = min(lhs.values.count, rhs.values.count)
        return AnimatableVector(
            values: vDSP.add(
                lhs.values[0..&amp;lt;count],
                rhs.values[0..&amp;lt;count]
            )
        )
    }

    static func += (lhs: inout AnimatableVector, rhs: AnimatableVector) {
        let count = min(lhs.values.count, rhs.values.count)
        vDSP.add(
            lhs.values[0..&amp;lt;count],
            rhs.values[0..&amp;lt;count],
            result: &amp;amp;lhs.values[0..&amp;lt;count]
        )
    }

    static func - (lhs: AnimatableVector, rhs: AnimatableVector) -&amp;gt; AnimatableVector {
        let count = min(lhs.values.count, rhs.values.count)
        return AnimatableVector(
            values: vDSP.subtract(
                lhs.values[0..&amp;lt;count],
                rhs.values[0..&amp;lt;count]
            )
        )
    }

    static func -= (lhs: inout AnimatableVector, rhs: AnimatableVector) {
        let count = min(lhs.values.count, rhs.values.count)
        vDSP.subtract(
            lhs.values[0..&amp;lt;count],
            rhs.values[0..&amp;lt;count],
            result: &amp;amp;lhs.values[0..&amp;lt;count]
        )
    }

    mutating func scale(by rhs: Double) {
        vDSP.multiply(
            Float(rhs),
            values,
            result: &amp;amp;values
        )
    }

    var magnitudeSquared: Double {
        Double(
            vDSP.sum(
                vDSP.multiply(values, values)
            )
        )
    }
    
    var count: Int {
        values.count
    }
    
    subscript(_ i: Int) -&amp;gt; Float {
        get {
            values[i]
        } set {
            values[i] = newValue
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;figcaption&gt;Animatable vector&lt;/figcaption&gt;&lt;/figure&gt;&lt;h2 id=&quot;wobbling-bubble&quot;&gt;Wobbling bubble&lt;/h2&gt;&lt;p&gt;So, as I already said, we need to define stages of animation with &lt;code&gt;AnimatableVector&lt;/code&gt; so that SwiftUI will be able to magically draw all in-between frames. &lt;/p&gt;&lt;p&gt;To do this with a circle, we first need to somehow make it able to &lt;em&gt;wobble. &lt;/em&gt;This is done through approximation with curves. To make the morphing effect, we will use &lt;code&gt;AnimatableVector&lt;/code&gt; to modify the radius at every specific point.&lt;/p&gt;&lt;p&gt;That&apos;s it&lt;/p&gt;&lt;p&gt;The first coordinate of the vector will say how much must be added to the distance of the first approximation point. The second is for the second point and so on.&lt;/p&gt;&lt;p&gt;You can see in a gif below how the radius at every specific point changes and how SwiftUI changes it smoothly. Curves&apos; control points are also displayed.&lt;/p&gt;&lt;figure class=&quot;kg-card kg-image-card kg-card-hascaption&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2022/05/wobbleWireframew.gif&quot; class=&quot;kg-image&quot; alt=&quot;Under the hood of wobbling&quot; loading=&quot;lazy&quot; width=&quot;1418&quot; height=&quot;1370&quot;&gt;&lt;figcaption&gt;Under the hood of wobbling&lt;/figcaption&gt;&lt;/figure&gt;&lt;!--kg-card-begin: html--&gt;&lt;section class=&quot;custom-replace&quot; data-replace=&quot;.subscription-article&quot;&gt;
    &lt;a class=&quot;gh-portal-triggerbtn-container&quot;&gt;Subscribe and don&apos;t miss posts!&lt;/a&gt;
&lt;/section&gt;&lt;!--kg-card-end: html--&gt;&lt;h2 id=&quot;implementation&quot;&gt;Implementation&lt;/h2&gt;&lt;p&gt;The concept of animation is determined. It&apos;s time to code!&lt;/p&gt;&lt;p&gt;As I said, the main idea is to approximate a circle with curves. There is an approximation of control points: &lt;code&gt;(4/3)*tan(pi/(2n))&lt;/code&gt; distance from a point in a circle with &lt;code&gt;n&lt;/code&gt; segments.&lt;/p&gt;&lt;figure class=&quot;kg-card kg-image-card kg-card-hascaption&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2022/05/270te.png&quot; class=&quot;kg-image&quot; alt loading=&quot;lazy&quot; width=&quot;635&quot; height=&quot;526&quot; srcset=&quot;https://alexdremov.me/content/images/size/w600/2022/05/270te.png 600w, https://alexdremov.me/content/images/2022/05/270te.png 635w&quot;&gt;&lt;figcaption&gt;https://stackoverflow.com/questions/1734745/how-to-create-circle-with-bézier-curves&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;We&apos;re going to represent the circle as an object conforming to &lt;code&gt;Shape&lt;/code&gt; protocol. For SwiftUI to know what to animate, you need to define &lt;code&gt;animatableData&lt;/code&gt; property. That&apos;s what SwiftUI is going to use to animate in-between frames.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;var animatableData: AnimatableVector {
    get { animatedValue }
    set { animatedValue = newValue }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;A little bit of linear algebra and all point coordinates are calculated. Some more advanced operations on &lt;code&gt;CGVector&lt;/code&gt; and &lt;code&gt;CGPoint&lt;/code&gt; are needed:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;import Foundation
import SwiftUI

extension CGPoint {
    public static func +(lhs: CGPoint, rhs: CGPoint) -&amp;gt; CGPoint {
        CGPoint(x: lhs.x + rhs.x, y: lhs.y + rhs.y)
    }
    
    static func +(lhs: CGPoint, rhs: CGVector) -&amp;gt; CGPoint {
        CGPoint(x: lhs.x + rhs.dx, y: lhs.y + rhs.dy)
    }
    
    static func -(lhs: CGPoint, rhs: CGVector) -&amp;gt; CGPoint {
        CGPoint(x: lhs.x - rhs.dx, y: lhs.y - rhs.dy)
    }
    
    public static func -(lhs: CGPoint, rhs: CGPoint) -&amp;gt; CGPoint {
        CGPoint(x: lhs.x - rhs.x, y: lhs.y - rhs.y)
    }
    
    init(_ vec: CGVector) {
        self = CGPoint(x: vec.dx, y: vec.dy)
    }
}

extension CGPoint: VectorArithmetic {
    public mutating func scale(by rhs: Double) {
        x = CGFloat(rhs) * x
        y = CGFloat(rhs) * y
    }
    
    public var magnitudeSquared: Double {
        Double(x * x + y * y)
    }
    

}

extension CGVector {
    init(_ point: CGPoint) {
        self = CGVector(dx: point.x, dy: point.y)
    }
    
    func scalar(_ vec: CGVector) -&amp;gt; CGFloat {
        dx * vec.dx + dy * vec.dy
    }
    
    func len() -&amp;gt; CGFloat {
        sqrt(dx * dx + dy * dy)
    }
    
    func perpendicular() -&amp;gt; CGVector {
        CGVector(dx: -dy, dy: dx) / len()
    }
    
    static func *(lhs: CGVector, rhs: CGFloat) -&amp;gt; CGVector {
        CGVector(dx: lhs.dx * rhs, dy: lhs.dy * rhs)
    }
    
    static func *(lhs: CGFloat, rhs: CGVector) -&amp;gt; CGVector {
        CGVector(dx: rhs.dx * lhs, dy: rhs.dy * lhs)
    }
    
    static func /(lhs: CGVector, rhs: CGFloat) -&amp;gt; CGVector {
        CGVector(dx: lhs.dx / rhs, dy: lhs.dy / rhs)
    }
    
    static func -(lhs: CGVector, rhs: CGVector) -&amp;gt; CGVector {
        CGVector(dx: lhs.dx - rhs.dx, dy: lhs.dy - rhs.dy)
    }
    
    static func +(lhs: CGVector, rhs: CGVector) -&amp;gt; CGVector {
        CGVector(dx: lhs.dx + rhs.dx, dy: lhs.dy + rhs.dy)
    }
    
    func angle(_ rhs: CGVector) -&amp;gt; CGFloat {
        return acos(scalar(rhs) / (rhs.len() * len()))
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Finally, implementing &lt;code&gt;Shape&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;import SwiftUI
import Foundation

struct MorphingCircleShape: Shape {
    let pointsNum: Int
    var morphing: AnimatableVector
    let tangentCoeficient: CGFloat
    
    var animatableData: AnimatableVector {
        get { morphing }
        set { morphing = newValue }
    }
    
    // Calculates control points
    func getTwoTangent(center: CGPoint, point: CGPoint) -&amp;gt; (first: CGPoint, second: CGPoint) {
        let a = CGVector(center - point)
        let dir = a.perpendicular() * a.len() * tangentCoeficient
        return (point - dir, point + dir)
    }
    
    // Draw circle
    func path(in rect: CGRect) -&amp;gt; Path {
        var path = Path()
        let radius = min(rect.width / 2, rect.height / 2)
        let center =  CGPoint(x: rect.width / 2, y: rect.height / 2)
        var nextPoint = CGPoint.zero
        
        let ithPoint: (Int) -&amp;gt; CGPoint = { i in
            let point = center + CGPoint(x: radius * sin(CGFloat(i) * CGFloat.pi * CGFloat(2) / CGFloat(pointsNum)),
                                         y: radius * cos(CGFloat(i) * CGFloat.pi * CGFloat(2) / CGFloat(pointsNum)))
            var direction = CGVector(point - center)
            direction = direction / direction.len()
            return point + direction * CGFloat(morphing[i &amp;gt;= pointsNum ? 0 : i])
        }
        var tangentLast = getTwoTangent(center: center,
                                        point: ithPoint(pointsNum - 1))
        for i in (0...pointsNum){
            nextPoint = ithPoint(i)
            let tangentNow = getTwoTangent(center: center, point: nextPoint)
            if i != 0 {
                path.addCurve(to: nextPoint, control1: tangentLast.1, control2: tangentNow.0)
            } else {
                path.move(to: nextPoint)
            }
            tangentLast = tangentNow
        }
        
        path.closeSubpath()
        return path
    }
    
    
    init(_ morph: AnimatableVector) {
        pointsNum = morph.count
        morphing = morph
        tangentCoeficient = (4 / 3) * tan(CGFloat.pi / CGFloat(2 * pointsNum))
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Finally, we can use this shape in a View. To make a wobbling effect, we need to change the vector responsible for radius modification.&lt;/p&gt;&lt;p&gt;This can be done by timer. &lt;/p&gt;&lt;h3 id=&quot;using-timer&quot;&gt;Using Timer&lt;/h3&gt;&lt;p&gt;We&apos;re going to randomly change the morphing vector in the timer&apos;s callback. Also, it looks weird to change all points at once, so we&apos;re going to animate only a subset of them.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;struct MorphingCircle: View &amp;amp; Identifiable &amp;amp; Hashable {
    static func == (lhs: MorphingCircle, rhs: MorphingCircle) -&amp;gt; Bool {
        lhs.id == rhs.id
    }
    
    func hash(into hasher: inout Hasher) {
        hasher.combine(id)
    }
    
    let id = UUID()
    @State var morph: AnimatableVector = AnimatableVector.zero
    @State var timer: Timer?
    
    func morphCreator() -&amp;gt; AnimatableVector {
        let range = Float(-morphingRange)...Float(morphingRange)
        var morphing = Array.init(repeating: Float.zero, count: self.points)
        for i in 0..&amp;lt;morphing.count where Int.random(in: 0...1) == 0 {
            morphing[i] = Float.random(in: range)
        }
        return AnimatableVector(values: morphing)
    }
    
    func update() {
        morph = morphCreator()
    }
    
    let duration: Double
    let points: Int
    let secting: Double
    let size: CGFloat
    let outerSize: CGFloat
    var color: Color
    let morphingRange: CGFloat
    
    var radius: CGFloat {
        outerSize / 2
    }
    
    var body: some View {
        MorphingCircleShape(morph)
            .fill(color)
            .frame(width: size, height: size, alignment: .center)
            .animation(Animation.easeInOut(duration: Double(duration + 1.0)), value: morph)
            .onAppear {
                update()
                timer = Timer.scheduledTimer(withTimeInterval: duration / secting, repeats: true) { timer in
                    update()
                }
            }.onDisappear {
                timer?.invalidate()
            }
            .frame(width: outerSize, height: outerSize, alignment: .center)
            .animation(nil, value: morph)
        
    }
    
    init(_ size:CGFloat = 300, morphingRange: CGFloat = 30, color: Color = .red, points: Int = 4,  duration: Double = 5.0, secting: Double = 2) {
        self.points = points
        self.color = color
        self.morphingRange = morphingRange
        self.duration = duration
        self.secting = secting
        self.size = morphingRange * 2 &amp;lt; size ? size - morphingRange * 2 : 5
        self.outerSize = size
        morph = AnimatableVector(values: [])
        update()
    }
    
    func color(_ newColor: Color) -&amp;gt; MorphingCircle {
        var morphNew = self
        morphNew.color = newColor
        return morphNew
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;results&quot;&gt;Results&lt;/h2&gt;&lt;p&gt;Created bubbles can be combined and animated to drift around the screen for example. Also, in the course of this guide, we created &lt;code&gt;AnimatableVector&lt;/code&gt; structure that you can use in your projects. &lt;/p&gt;&lt;p&gt;Feel free to share your results!&lt;/p&gt;&lt;figure class=&quot;kg-card kg-image-card kg-card-hascaption&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2022/05/ezgif.com-gif-maker.gif&quot; class=&quot;kg-image&quot; alt=&quot;More wobbling bubbles&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;471&quot;&gt;&lt;figcaption&gt;More wobbling bubbles&lt;/figcaption&gt;&lt;/figure&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Check my iOS section of the blog to learn more useful tips&lt;/div&gt;&lt;/div&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://alexdremov.me/tag/ios/&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;Alex Dremov | iOS&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;One of my favorites. Here I write about Swift and iOS development&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://alexdremov.me/assets/icons/apple-touch-icon.png&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;Alex Dremov&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://images.unsplash.com/photo-1558126372-76b529458592?crop&amp;#x3D;entropy&amp;amp;cs&amp;#x3D;tinysrgb&amp;amp;fit&amp;#x3D;max&amp;amp;fm&amp;#x3D;jpg&amp;amp;ixid&amp;#x3D;MnwxMTc3M3wwfDF8c2VhcmNofDExfHxpb3N8ZW58MHx8fHwxNjQ5NTA0MTQ5&amp;amp;ixlib&amp;#x3D;rb-1.2.1&amp;amp;q&amp;#x3D;80&amp;amp;w&amp;#x3D;2000&quot; alt=&quot;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/1734745/how-to-create-circle-with-b%C3%A9zier-curves?ref=alexdremov.me&quot;&gt;https://stackoverflow.com/questions/1734745/how-to-create-circle-with-bézier-curves&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://developer.apple.com/documentation/swiftui/animatable/animatabledata-swift.property-6nydg?ref=alexdremov.me&quot;&gt;https://developer.apple.com/documentation/swiftui/animatable/animatabledata-swift.property-6nydg&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;</content:encoded></item><item><title>New Package: Look at Swift Async Algorithms</title><link>https://alexdremov.me/swift-async-algorithms-module/</link><guid isPermaLink="true">https://alexdremov.me/swift-async-algorithms-module/</guid><description>Apple released an async swift algorithms package that introduces useful ways of working with asynchronous sequences</description><pubDate>Wed, 27 Apr 2022 20:21:00 GMT</pubDate><content:encoded>&lt;p&gt;About a month ago, Apple released the first version of the &lt;a href=&quot;https://github.com/apple/swift-async-algorithms?ref=alexdremov.me&quot;&gt;async swift algorithms&lt;/a&gt; package. It provides tools and algorithms to use with the introduced not that far ago asynchronous sequence. The package focuses on implementing already well-known tools like &lt;code&gt;zip&lt;/code&gt; as well as new features that transact in time (wow). It also makes available more sophisticated ways of creating and managing asynchronous sequences.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-purple&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💥&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;The module&apos;s latest version is &lt;code&gt;0.0.1&lt;/code&gt;, which means that it&apos;s still in development. So, some methods are not available yet, some may change or appear.&lt;br&gt;&lt;br&gt;Mostly, this article here is to get to know new features and, possibly, plan your code, keeping in mind that such features will appear in the future&lt;/div&gt;&lt;/div&gt;&lt;h2 id=&quot;installation&quot;&gt;Installation &lt;/h2&gt;&lt;p&gt;The new package is distributed through Swift PM. To add it to your project, you need to add it as a dependency in the Xcode project &lt;code&gt;File &amp;gt; Add Packages&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Or add it to your &lt;code&gt;Package.swift&lt;/code&gt; file:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;.package(url: &quot;https://github.com/apple/swift-async-algorithms&quot;),&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Don&apos;t forget to also add the dependency to the executable:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;.target(name: &quot;&amp;lt;target&amp;gt;&quot;, dependencies: [
    .product(name: &quot;AsyncAlgorithms&quot;, package: &quot;swift-async-algorithms&quot;),
]),&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The module will be available in your project after adding &lt;code&gt;import AsyncAlgorithms&lt;/code&gt;.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-purple&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💥&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;As I mentioned, the module is still in development. So, you need to install &lt;a href=&quot;https://www.swift.org/download/?ref=alexdremov.me#trunk-development-main&quot;&gt;Swift Trunk Development toolchain&lt;/a&gt; to have access to all features. &lt;br&gt;Some of them are available right away, though!&lt;/div&gt;&lt;/div&gt;&lt;h2 id=&quot;creating-asynchronous-sequences&quot;&gt;Creating asynchronous sequences&lt;/h2&gt;&lt;p&gt;To test all the beautiful functions the new module provides, we need to create an async sequence at first. And the package introduces new ways of doing so.&lt;/p&gt;&lt;h3 id=&quot;property-async&quot;&gt;Property &lt;code&gt;async&lt;/code&gt;&lt;/h3&gt;&lt;p&gt;The module adds the following extension to &lt;code&gt;Sequence&lt;/code&gt; protocol.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;extension Sequence {
  public var async: AsyncLazySequence&amp;lt;Self&amp;gt; { get }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Where &lt;code&gt;AsyncLazySequence&lt;/code&gt; conforms to &lt;code&gt;AsyncSequence&lt;/code&gt;.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;public struct AsyncLazySequence&amp;lt;Base: Sequence&amp;gt;: AsyncSequence {
}

extension AsyncLazySequence: Sendable where Base: Sendable {
	...
}
extension AsyncLazySequence.Iterator: Sendable where Base.Iterator: Sendable {
}&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Using the &lt;code&gt;async&lt;/code&gt; property, we can turn any existing Sequence into &lt;code&gt;AsyncSequence&lt;/code&gt; to use them in some async API, for example.&lt;/div&gt;&lt;/div&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;let numbers = [1, 2, 3, 4].async
let characters = &quot;Hello, world&quot;.async
let items = [1: &quot;one&quot;, 2: &quot;two&quot;, 3: &quot;three&quot;].async&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;However, creating &lt;code&gt;AsyncSequence&lt;/code&gt; this way does not really bring benefits as all elements are already here and available right away. There are more useful ways of creating &lt;code&gt;AsyncSequence&lt;/code&gt;. &lt;/p&gt;&lt;!--kg-card-begin: html--&gt;&lt;section class=&quot;custom-replace&quot; data-replace=&quot;.subscription-article&quot;&gt;
    &lt;a class=&quot;gh-portal-triggerbtn-container&quot;&gt;Subscribe and don&apos;t miss posts!&lt;/a&gt;
&lt;/section&gt;&lt;!--kg-card-end: html--&gt;&lt;h3 id=&quot;asyncchannel-and-asyncthrowingchannel&quot;&gt;AsyncChannel and AsyncThrowingChannel&lt;/h3&gt;&lt;p&gt;If you know what &lt;code&gt;Future&lt;/code&gt; or &lt;code&gt;Promise&lt;/code&gt; in other languages are, then &lt;code&gt;AsyncChannel&lt;/code&gt; will be familiar to you. Except that it provides a way of transferring a &lt;strong&gt;sequence&lt;/strong&gt; of values. &lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;❗&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Channel&apos;s element must conform to the &lt;code&gt;Sendable&lt;/code&gt; protocol, which basically means that public API is safe to use across concurrency domains.&lt;br&gt;&lt;br&gt;All basic types automatically conform to it. For custom types, you need to add the conformance before use.&lt;/div&gt;&lt;/div&gt;&lt;p&gt;Here&apos;s a pretty straightforward example of &lt;code&gt;AsyncChannel&lt;/code&gt; usage.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;let channel = AsyncChannel&amp;lt;String&amp;gt;()
Task {
    for word in [&quot;Hello&quot;, &quot;from&quot;, &quot;async&quot;, &quot;channel&quot;] {
      await channel.send(word)
    }
    await channel.finish()
}

for await message in channel {
    print(message)
}&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;Hello
from
async
channel&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Notice that &lt;code&gt;await&lt;/code&gt; keyword is used with send and finish. This is because the channel is &lt;strong&gt;actually both ways synchronized&lt;/strong&gt;. That means that &lt;code&gt;send&lt;/code&gt; awaits consumption and vice versa.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;The &lt;code&gt;await channel.send()&lt;/code&gt; waits until the sent value will be consumed in any way. This way, the one who produces values for the channel, will not generate more values than the receiver can consume&amp;nbsp;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;AsyncThrowingStream&lt;/code&gt; is almost the same except that it provides &lt;code&gt;fail(_ error: Error)&lt;/code&gt; method that can be used to throw an exception to the channel&apos;s consumer.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;let channel = AsyncThrowingChannel&amp;lt;String, Error&amp;gt;()

...

for try await message in channel {
    print(message)
}&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;and-converting-back&quot;&gt;And converting back&lt;/h3&gt;&lt;p&gt;The module adds initializers for three primary types: &lt;code&gt;Array&lt;/code&gt;, &lt;code&gt;Dictionary&lt;/code&gt;, and &lt;code&gt;Set&lt;/code&gt; that let you transform the async sequence to the regular one by fetching all elements during init.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;let table = await Dictionary(uniqueKeysWithValues: zip(keys, values))
let allItems = await Set(items.prefix(10))
let allMessages = await Array(channel)&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;manipulating-asynchronous-sequences&quot;&gt;Manipulating asynchronous sequences&lt;/h2&gt;&lt;p&gt;The module also provides new ways of combining asynchronous sequences. These functions are pretty straightforward.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;chain(_ s1: AsyncSequence, _ s2: AsyncSequence)&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Chains two or three asynchronous sequences together sequentially where the elements from the result are comprised in order from the elements of the first asynchronous sequence and then the second (and so on) or until an error occurs. Sequences must have the same &lt;code&gt;Element&lt;/code&gt; type.&lt;/p&gt;&lt;!--kg-card-begin: markdown--&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style=&quot;text-align:center&quot;&gt;Sequence 1&lt;/th&gt;
&lt;th style=&quot;text-align:center&quot;&gt;Sequence 2&lt;/th&gt;
&lt;th style=&quot;text-align:center&quot;&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align:center&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align:center&quot;&gt;4&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align:center&quot;&gt;&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;2&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align:center&quot;&gt;&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;3&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;!--kg-card-end: markdown--&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-purple&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💥&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Apple notes that it can be used for two&lt;strong&gt; or more &lt;/strong&gt;sequences. Though, only two or three arguments are available now.&amp;nbsp;&lt;/div&gt;&lt;/div&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;joined()&lt;/code&gt; or &lt;code&gt;joined(separator: AsyncSequence)&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Concatenates an asynchronous sequence of asynchronous sequences together where the result is comprised in order from the elements of the first asynchronous sequence and then the second (and so on) or until an error occurs. Similar to &lt;code&gt;chain()&lt;/code&gt;except the number of asynchronous sequences to concatenate is not known upfront. The separator also can be specified.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;combineLatest(_ base1: AsyncSequence, _ base2: AsyncSequence)&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Combines two &lt;em&gt;or more&lt;/em&gt; sequences, producing tuples of the latest values available from the sequence.&lt;/p&gt;&lt;!--kg-card-begin: markdown--&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style=&quot;text-align:center&quot;&gt;Sequence 1&lt;/th&gt;
&lt;th style=&quot;text-align:center&quot;&gt;Sequence 2&lt;/th&gt;
&lt;th style=&quot;text-align:center&quot;&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align:center&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;&lt;em&gt;awaits&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align:center&quot;&gt;&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;2&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;(1, 2)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align:center&quot;&gt;&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;3&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;(1, 3)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align:center&quot;&gt;4&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;(4, 3)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;!--kg-card-end: markdown--&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;merge(_ base1: AsyncSequence, _ base2: AsyncSequence)&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Merges sequences into a new one. The result is a combination of results from two sequences. Sequences must have the same &lt;code&gt;Element&lt;/code&gt; type.&lt;/p&gt;&lt;!--kg-card-begin: markdown--&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style=&quot;text-align:center&quot;&gt;Sequence 1&lt;/th&gt;
&lt;th style=&quot;text-align:center&quot;&gt;Sequence 2&lt;/th&gt;
&lt;th style=&quot;text-align:center&quot;&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align:center&quot;&gt;&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;&lt;em&gt;awaits&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align:center&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align:center&quot;&gt;&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;2&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align:center&quot;&gt;&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;3&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align:center&quot;&gt;4&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;4&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;!--kg-card-end: markdown--&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Considering that it&apos;s not defined from which sequence element will appear faster, the order of elements can be whatever&lt;/div&gt;&lt;/div&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;zip(_ base1: AsyncSequence, _ base2: AsyncSequence)&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The same as a regular &lt;code&gt;zip&lt;/code&gt; but for &lt;code&gt;AsyncSequence&lt;/code&gt;. Differs from &lt;code&gt;combineLatest&lt;/code&gt; as it waits until the second value is available and does not use the last value.&lt;/p&gt;&lt;!--kg-card-begin: markdown--&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style=&quot;text-align:center&quot;&gt;Sequence 1&lt;/th&gt;
&lt;th style=&quot;text-align:center&quot;&gt;Sequence 2&lt;/th&gt;
&lt;th style=&quot;text-align:center&quot;&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align:center&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;&lt;em&gt;awaits&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align:center&quot;&gt;&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;2&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;(1, 2)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align:center&quot;&gt;&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;3&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;&lt;em&gt;awaits&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align:center&quot;&gt;4&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;(4, 3)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;!--kg-card-end: markdown--&gt;&lt;h2 id=&quot;time-related-functions&quot;&gt;Time-related functions&lt;/h2&gt;&lt;p&gt;Sounds awesome, but Swift is not powerful enough to put &lt;code&gt;await&lt;/code&gt; before the time itself. When events can potentially happen faster than the desired consumption rate, there are ways to handle the situation. These functions allow linking &lt;code&gt;AsyncSequences&lt;/code&gt; with time. They can be applied to any &lt;code&gt;AsyncSequence&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;For both listed methods, a custom clock can be specified. By default, it&apos;s &lt;code&gt;ContinuousClock&lt;/code&gt;&lt;/p&gt;&lt;h3 id=&quot;debounce&quot;&gt;Debounce&lt;/h3&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt; public func debounce&amp;lt;C: Clock&amp;gt;(
    for interval: C.Instant.Duration, 
    tolerance: C.Instant.Duration? = nil, 
    clock: C
  ) -&amp;gt; AsyncDebounceSequence&amp;lt;Self, C&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The debounce algorithm produces elements after a particular duration has passed between events. If there are a lot of events happening, debounce will wait until at least &lt;code&gt;interval&lt;/code&gt; of time elapsed from the last event before emitting value.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;seq.debounce(for: .seconds(1))&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In this case, it transforms a potentially fast asynchronous sequence of events into one that waits for a window of 1 second &lt;strong&gt;with no events&lt;/strong&gt; to elapse before emitting a value.&lt;/p&gt;&lt;h3 id=&quot;throttle&quot;&gt;Throttle&lt;/h3&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;extension AsyncSequence {
  public func throttle&amp;lt;C: Clock, Reduced&amp;gt;(
    for interval: C.Instant.Duration, 
    clock: C, 
    reducing: @Sendable @escaping (Reduced?, Element) async -&amp;gt; Reduced
  ) -&amp;gt; AsyncThrottleSequence&amp;lt;Self, C, Reduced&amp;gt;
  
  public func throttle&amp;lt;Reduced&amp;gt;(
    for interval: Duration, 
    reducing: @Sendable @escaping (Reduced?, Element) async -&amp;gt; Reduced
  ) -&amp;gt; AsyncThrottleSequence&amp;lt;Self, ContinuousClock, Reduced&amp;gt;
  
  public func throttle&amp;lt;C: Clock&amp;gt;(
    for interval: C.Instant.Duration, 
    clock: C, 
    latest: Bool = true
  ) -&amp;gt; AsyncThrottleSequence&amp;lt;Self, C, Element&amp;gt;
  
  public func throttle(
    for interval: Duration, 
    latest: Bool = true
  ) -&amp;gt; AsyncThrottleSequence&amp;lt;Self, ContinuousClock, Element&amp;gt;
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The throttle algorithm produces elements such that at least a specific interval has elapsed between them. If values are produced by the base &lt;code&gt;AsyncSequence&lt;/code&gt; the throttle does not resume its next iterator until the period has elapsed or unless a terminal event is encountered. Similarly to &lt;code&gt;debounce&lt;/code&gt;, a custom clock can be specified.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;seq.throttle(for: .seconds(1))&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In this case, the throttle transforms a potentially fast asynchronous sequence of events into one that waits for a window of 1 second to elapse before emitting a value.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Notice that debounce, waits for a window &lt;strong&gt;with no events&lt;/strong&gt;, while throttle simply waits for a window.&lt;/div&gt;&lt;/div&gt;&lt;h2 id=&quot;final-notes&quot;&gt;Final notes&lt;/h2&gt;&lt;p&gt;It&apos;s actually frankly entertaining to watch how Swift unfolds new features and how they are developed. Definitely check the project&apos;s GitHub mentioned in references to check out the module&apos;s source code.&lt;/p&gt;&lt;p&gt;If you feel not really confident with relatively new swift concurrency features, check out my quick guide to async/await in Swift.&lt;/p&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://alexdremov.me/quick-guide-to-async-await-in-swift/&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;Quick Guide to Async Await in Swift | Alex Dremov&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;Everything you need to know about new Swift asynchronous features. Async await, main actor, task, async get, and possible use cases — all covered.&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://alexdremov.me/assets/icons/apple-touch-icon.png&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;Alex Dremov&lt;/span&gt;&lt;span class=&quot;kg-bookmark-publisher&quot;&gt;Alex Dremov&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2022/04/slide_17.jpg&quot; alt=&quot;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://github.com/apple/swift-async-algorithms?ref=alexdremov.me&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;GitHub - apple/swift-async-algorithms: Async Algorithms for Swift&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;Async Algorithms for Swift. Contribute to apple/swift-async-algorithms development by creating an account on GitHub.&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://github.com/fluidicon.png&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;GitHub&lt;/span&gt;&lt;span class=&quot;kg-bookmark-publisher&quot;&gt;apple&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://opengraph.githubassets.com/25b178985a8c49655550b061d0d0ef4bda784e300a455499592788297fd99f67/apple/swift-async-algorithms&quot; alt=&quot;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;</content:encoded></item><item><title>Treap: The Easiest Search Tree (Explained)</title><link>https://alexdremov.me/treap-algorithm-explained/</link><guid isPermaLink="true">https://alexdremov.me/treap-algorithm-explained/</guid><description>Binary search trees are hard. I&apos;m going to explain one of the easiest, yet efficient and powerful balanced binary tree — treap or cartesian tree.</description><pubDate>Mon, 25 Apr 2022 04:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;Cartesian tree or treap&lt;/strong&gt; (binary search tree + binary heap) is a fast yet simple data structure. It conforms to a core search binary tree property and binary heap property at the same time. Despite its simplicity, treap self-balances, resulting in &lt;code&gt;O(logn)&lt;/code&gt; complexity on average for all common operations.&lt;/p&gt;&lt;p&gt;Amazing, right?&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-purple&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💥&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;The algorithm uses random values. Therefore, &lt;code spellcheck=&quot;false&quot; style=&quot;white-space: pre-wrap;&quot;&gt;O(logn)&lt;/code&gt; complexity is &lt;b&gt;&lt;strong style=&quot;white-space: pre-wrap;&quot;&gt;on average&lt;/strong&gt;&lt;/b&gt;. However, with a lot of items &lt;code spellcheck=&quot;false&quot; style=&quot;white-space: pre-wrap;&quot;&gt;O(logn)&lt;/code&gt; is almost always true. So, later in this article, I will use just &lt;code spellcheck=&quot;false&quot; style=&quot;white-space: pre-wrap;&quot;&gt;O(logn)&lt;/code&gt; without &quot;on average&quot; addition.&lt;/div&gt;&lt;/div&gt;&lt;p&gt;Moreover, there is a modification (implicit treap, treap with implicit key) that lets you use treap as a usual array with &lt;code&gt;O(logn)&lt;/code&gt; &lt;strong&gt;random insertions and&lt;/strong&gt; &lt;strong&gt;random deletions&lt;/strong&gt;. Isn&apos;t it cool? In this article I&apos;ll explain how to create one and provide the implementation in Swift. Also, I will compare treap to the general &lt;code&gt;set&lt;/code&gt; from standard library. Let&apos;s start!&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;In a binary search tree, for each node, all items&apos; values in the left subtree are less than the node&apos;s value, and all items in the right subtree are greater&lt;/div&gt;&lt;/div&gt;&lt;h2 id=&quot;core-algorithm&quot;&gt;Core algorithm&lt;/h2&gt;&lt;p&gt;As I said earlier, treap combines heaps and binary search trees. Therefore, we are going to store at least two properties: &lt;code&gt;key&lt;/code&gt; (or value) and &lt;code&gt;priority&lt;/code&gt;. Key is a value for which tree is a search tree and for the priority, it is a binary heap.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;A binary heap is a binary tree where each node child&apos;s value is less than the node&apos;s value&lt;/div&gt;&lt;/div&gt;&lt;figure class=&quot;kg-card kg-image-card kg-card-hascaption&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2022/04/treap.png&quot; class=&quot;kg-image&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;1440&quot; height=&quot;900&quot; srcset=&quot;https://alexdremov.me/content/images/size/w600/2022/04/treap.png 600w, https://alexdremov.me/content/images/size/w1000/2022/04/treap.png 1000w, https://alexdremov.me/content/images/2022/04/treap.png 1440w&quot; sizes=&quot;(min-width: 720px) 720px&quot;&gt;&lt;figcaption&gt;&lt;span style=&quot;white-space: pre-wrap;&quot;&gt;Treap example&lt;/span&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;On the image above, you may notice that for every node, all child&apos;s priorities are less. On the other side, all children on the left have a key less than that in the node, and all children on the right have a larger key.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;It&apos;s also called a &lt;b&gt;&lt;strong style=&quot;white-space: pre-wrap;&quot;&gt;cartesian tree &lt;/strong&gt;&lt;/b&gt;as it can be displayed on a regular 2D grid with (key, priority) coordinate for each node. Just like in the image above.&lt;/div&gt;&lt;/div&gt;&lt;p&gt;To create a fully-functioning search tree, we need to implement:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;find&lt;/li&gt;&lt;li&gt;insert&lt;/li&gt;&lt;li&gt;remove&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;More exotic operations like &lt;code&gt;lower bound&lt;/code&gt; and &lt;code&gt;upper bound&lt;/code&gt; are also pretty simple and does not differ from those in the other search trees. And all these operations can be implemented using &lt;strong&gt;just two helper operations&lt;/strong&gt;!&lt;/p&gt;&lt;p&gt;How to do that?&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-purple&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💥&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;&lt;b&gt;&lt;strong style=&quot;white-space: pre-wrap;&quot;&gt;Split&lt;/strong&gt;&lt;/b&gt;&lt;br&gt;&lt;br&gt;Splits the tree into two trees by given &lt;code spellcheck=&quot;false&quot; style=&quot;white-space: pre-wrap;&quot;&gt;value&lt;/code&gt;. All values in the left tree are &lt;b&gt;&lt;strong style=&quot;white-space: pre-wrap;&quot;&gt;less&lt;/strong&gt;&lt;/b&gt; than the &lt;code spellcheck=&quot;false&quot; style=&quot;white-space: pre-wrap;&quot;&gt;value&lt;/code&gt; while in the right tree are &lt;b&gt;&lt;strong style=&quot;white-space: pre-wrap;&quot;&gt;greater&lt;/strong&gt;&lt;/b&gt;. And both resulting trees are correct treaps.&lt;br&gt;&lt;br&gt;We will use a special flag that decides whether to send values that are equal to the left tree or to the right tree.&lt;/div&gt;&lt;/div&gt;&lt;figure class=&quot;kg-card kg-image-card kg-width-wide kg-card-hascaption&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2022/04/split-image-1.png&quot; class=&quot;kg-image&quot; alt=&quot;Example of split function result. The equal value sent to the right&quot; loading=&quot;lazy&quot; width=&quot;1440&quot; height=&quot;734&quot; srcset=&quot;https://alexdremov.me/content/images/size/w600/2022/04/split-image-1.png 600w, https://alexdremov.me/content/images/size/w1000/2022/04/split-image-1.png 1000w, https://alexdremov.me/content/images/2022/04/split-image-1.png 1440w&quot; sizes=&quot;(min-width: 1200px) 1200px&quot;&gt;&lt;figcaption&gt;&lt;span style=&quot;white-space: pre-wrap;&quot;&gt;Example of split function result. The equal value sent to the right&lt;/span&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-purple&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💥&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;&lt;b&gt;&lt;strong style=&quot;white-space: pre-wrap;&quot;&gt;Merge&lt;/strong&gt;&lt;/b&gt;&lt;br&gt;&lt;br&gt;Merges two treaps into one big treap.&lt;br&gt;&lt;b&gt;&lt;strong style=&quot;white-space: pre-wrap;&quot;&gt;Prerequisite:&lt;/strong&gt;&lt;/b&gt; all items in the first tree are less than items in the right tree.&lt;/div&gt;&lt;/div&gt;&lt;figure class=&quot;kg-card kg-image-card kg-width-wide kg-card-hascaption&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2022/04/merge-example.png&quot; class=&quot;kg-image&quot; alt=&quot;Treap merge example&quot; loading=&quot;lazy&quot; width=&quot;1440&quot; height=&quot;900&quot; srcset=&quot;https://alexdremov.me/content/images/size/w600/2022/04/merge-example.png 600w, https://alexdremov.me/content/images/size/w1000/2022/04/merge-example.png 1000w, https://alexdremov.me/content/images/2022/04/merge-example.png 1440w&quot; sizes=&quot;(min-width: 1200px) 1200px&quot;&gt;&lt;figcaption&gt;&lt;span style=&quot;white-space: pre-wrap;&quot;&gt;Merge example&lt;/span&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;So, if we implement these two methods, implementing all other three operations would be trivial.&lt;/p&gt;&lt;h2 id=&quot;split&quot;&gt;Split&lt;/h2&gt;&lt;p&gt;Let&apos;s start thinking about code at this stage. I&apos;m going to explain this in &lt;code&gt;C++&lt;/code&gt;. Rewriting the following code in &lt;code&gt;Swift&lt;/code&gt; is actually really easy. Leave a comment bellow if you need a help.&lt;/p&gt;&lt;figure class=&quot;kg-card kg-code-card&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;template&amp;lt;typename T&amp;gt;
struct Node {
	T key;
	size_t prior;
	Node* left = nullptr, *right = nullptr;

	Node(T key, size_t prior) :
		key(std::move(key)),
		prior(prior) {
	}
};&lt;/code&gt;&lt;/pre&gt;&lt;figcaption&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre-wrap;&quot;&gt;Structure of treap&apos;s node&lt;/span&gt;&lt;/p&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;For split, we have a &lt;code&gt;head&lt;/code&gt; node and a &lt;code&gt;key&lt;/code&gt; for which split needs to be done. This method is extremely simple using recursion.&lt;/p&gt;&lt;h3 id=&quot;algorithm&quot;&gt;Algorithm&lt;/h3&gt;&lt;p&gt;Let the current head be &lt;code&gt;p&lt;/code&gt;.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;If &lt;code&gt;p-&amp;gt;key&lt;/code&gt; is &lt;strong&gt;less than&lt;/strong&gt; the &lt;code&gt;key&lt;/code&gt;, then we need to go &lt;strong&gt;right &lt;/strong&gt;and split &lt;code&gt;p-&amp;gt;right&lt;/code&gt; further. &lt;br&gt;&lt;br&gt;Also, splitting &lt;code&gt;right&lt;/code&gt; will bring two trees as well, and the first one will have nodes with keys &lt;strong&gt;less &lt;/strong&gt;than the &lt;code&gt;key&lt;/code&gt;. Yet, they are greater than the &lt;code&gt;p-&amp;gt;key&lt;/code&gt; (as they are in the second tree of the first split). &lt;br&gt;So, we set &lt;code&gt;p-&amp;gt;right&lt;/code&gt; to the &lt;strong&gt;first&lt;/strong&gt; tree of splitting &lt;code&gt;right&lt;/code&gt; result.&lt;br&gt;&lt;br&gt;&lt;strong&gt;Result:&lt;/strong&gt;  &lt;code&gt;p&lt;/code&gt;, split right&apos;s second tree&lt;/li&gt;&lt;li&gt;If the &lt;code&gt;p-&amp;gt;key&lt;/code&gt; is &lt;strong&gt;greater or equal &lt;/strong&gt;to the &lt;code&gt;key&lt;/code&gt;, then we need to go &lt;strong&gt;left &lt;/strong&gt;and split &lt;code&gt;p-&amp;gt;left&lt;/code&gt; further.&lt;br&gt;&lt;br&gt;Similarly to the case above,  we set &lt;code&gt;p-&amp;gt;left&lt;/code&gt; to the &lt;strong&gt;second&lt;/strong&gt; tree of split left.&lt;br&gt;&lt;br&gt;&lt;strong&gt;Result: &lt;/strong&gt;split left&apos;s first tree, &lt;code&gt;p&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The algorithm above leaves a node that is equal to the split value in the second tree. Symmetrically, we will use the &lt;code&gt;equalOnTheLeft&lt;/code&gt; flag to leave the node in the left tree.&lt;/p&gt;&lt;p&gt;So, the final code:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;pair&amp;lt;Node *, Node *&amp;gt; split (Node *p, const T&amp;amp; key,
				bool equalOnTheLeft=false) {
    if (!p) // reached leaf
    	return {nullptr, nullptr};
    if (p-&amp;gt;key &amp;lt; key ||
    	(equalOnTheLeft &amp;amp;&amp;amp; p-&amp;gt;key == key)) { // splitting right
        auto q = split(p-&amp;gt;right, key, equalOnTheLeft);
        
        // q.first has nodes of the right
        // subtree that are less than key
        p-&amp;gt;right = q.first; 
        
        return {p, q.second};
    } else { // splitting left
        auto q = split(p-&amp;gt;left, key, equalOnTheLeft);
        
        // q.second has nodes of the left 
        // subtree that are greater or equal
        // to the key
        p-&amp;gt;left = q.second;
        
        return {q.first, p};
	}
}&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Priorities are not used and not changed during the split procedure. The resulting trees have the right order of priorities as the initial tree had it right&lt;/div&gt;&lt;/div&gt;
&lt;!--kg-card-begin: html--&gt;
&lt;section class=&quot;custom-replace&quot; data-replace=&quot;.subscription-article&quot;&gt;
    &lt;a class=&quot;gh-portal-triggerbtn-container&quot;&gt;Subscribe and don&apos;t miss posts!&lt;/a&gt;
&lt;/section&gt;
&lt;!--kg-card-end: html--&gt;
&lt;h2 id=&quot;merge&quot;&gt;Merge&lt;/h2&gt;&lt;p&gt;Merge is similar to split, but it uses &lt;strong&gt;priorities&lt;/strong&gt; to do the work. As I mentioned before, there is a &lt;strong&gt;prerequisite&lt;/strong&gt;: all items in the first merged tree must be less than items in the second tree. If this is not true, another algorithm must be used.&lt;/p&gt;&lt;h3 id=&quot;algorithm-1&quot;&gt;Algorithm&lt;/h3&gt;&lt;p&gt;Similarly to &lt;code&gt;split&lt;/code&gt;, &lt;code&gt;merge&lt;/code&gt; is also recursive. Let us have two trees to merge: &lt;code&gt;l&lt;/code&gt; and &lt;code&gt;r&lt;/code&gt;.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;We need to choose which tree will represent the new head. That&apos;s simple — the head must have the greatest priority, so we choose &lt;code&gt;l&lt;/code&gt; or &lt;code&gt;r&lt;/code&gt; based on that.&lt;/li&gt;&lt;/ul&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Notice that the head node in &lt;code spellcheck=&quot;false&quot; style=&quot;white-space: pre-wrap;&quot;&gt;l&lt;/code&gt; has the highest priority in the whole &lt;code spellcheck=&quot;false&quot; style=&quot;white-space: pre-wrap;&quot;&gt;l&lt;/code&gt; tree as its a property of correct treap. The same applies to &lt;code spellcheck=&quot;false&quot; style=&quot;white-space: pre-wrap;&quot;&gt;r&lt;/code&gt;.&lt;/div&gt;&lt;/div&gt;&lt;ul&gt;&lt;li&gt;If &lt;code&gt;l&lt;/code&gt; has greater priority, then &lt;code&gt;l-&amp;gt;left&lt;/code&gt; subtree will remain intact as left subtree for sure less than &lt;code&gt;r&lt;/code&gt; and it has nothing to do with it. &lt;br&gt;&lt;br&gt;Then,  &lt;code&gt;l-&amp;gt;right&lt;/code&gt; subtree must be merged with &lt;code&gt;r&lt;/code&gt; and it&apos;s going to be the new &lt;code&gt;l-&amp;gt;right&lt;/code&gt; subtree.&lt;/li&gt;&lt;li&gt;If &lt;code&gt;r&lt;/code&gt; has greater priority, then, similar to the example above, &lt;code&gt;r-&amp;gt;right&lt;/code&gt; will remain intact and &lt;code&gt;r-&amp;gt;left&lt;/code&gt; must be merged with &lt;code&gt;l&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;Node* merge (Node *l, Node *r) {
    if (!l) // left is empty
    	return r;
    if (!r) // right is empty
    	return l;
        
    if (l-&amp;gt;prior &amp;gt; r-&amp;gt;prior) { // l has the new head.
        l-&amp;gt;right = merge(l-&amp;gt;right, r);
        return l;
    } else { // r has the new head.
        r-&amp;gt;left = merge(l, r-&amp;gt;left);
        return r;
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Why is it correct?&lt;/p&gt;&lt;p&gt;It seems like nothing stops us from breaking the search tree structure where all items&apos; values in the left subtree are less than the node&apos;s value, and all items in the right subtree are greater.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;&lt;b&gt;&lt;strong style=&quot;white-space: pre-wrap;&quot;&gt;Prerequisite&lt;/strong&gt;&lt;/b&gt; saves binary search tree property as items are never reordered and &lt;code spellcheck=&quot;false&quot; style=&quot;white-space: pre-wrap;&quot;&gt;l &amp;lt; r&lt;/code&gt; property is always kept the same&lt;/div&gt;&lt;/div&gt;&lt;h2 id=&quot;implementing-search-tree-methods&quot;&gt;Implementing search tree methods&lt;/h2&gt;&lt;p&gt;You believed me that all methods are easy to implement through &lt;code&gt;split&lt;/code&gt; and &lt;code&gt;merge&lt;/code&gt;. Time to prove that.&lt;/p&gt;&lt;h3 id=&quot;find&quot;&gt;Find&lt;/h3&gt;&lt;p&gt;Find is implemented just like for the general search tree. We use the fact that keys in the left subtree are greater than the value in the node.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;Node* find(Node* node, const T&amp;amp; key) {
	if (node == nullptr)
		return nullptr;
    if (node-&amp;gt;key == key)
		return node;
    return find(key &amp;gt;= node-&amp;gt;key ? node-&amp;gt;right : node-&amp;gt;left, key);
}&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;insert&quot;&gt;Insert&lt;/h3&gt;&lt;p&gt;Let&apos;s think about insert in terms of split and merge. We have one big tree and we need to insert a new &lt;code&gt;key&lt;/code&gt;. &lt;/p&gt;&lt;ul&gt;&lt;li&gt;Split the tree by &lt;code&gt;key&lt;/code&gt; to new trees: &lt;code&gt;first&lt;/code&gt; and &lt;code&gt;second&lt;/code&gt;. Then, we will have two trees: the first (which has values lower than the &lt;code&gt;key&lt;/code&gt;) and the second (which has values greater or equal to the &lt;code&gt;key&lt;/code&gt;).&lt;br&gt;&lt;br&gt;We can check that node already exists: try to find it in the right tree.&lt;/li&gt;&lt;/ul&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-purple&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💥&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Implementation requires that each item is met only &lt;b&gt;&lt;strong style=&quot;white-space: pre-wrap;&quot;&gt;once&lt;/strong&gt;&lt;/b&gt;.&lt;br&gt;&lt;br&gt;If you need to insert multiple copies of the same item, you can store an item and it&apos;s count to achieve that&lt;/div&gt;&lt;/div&gt;&lt;ul&gt;&lt;li&gt;Create a new node that will store the new &lt;code&gt;key&lt;/code&gt; — &lt;code&gt;newNode&lt;/code&gt;. Ta-da this node is a correct treap that has only one node.&lt;br&gt;&lt;br&gt;For the new node, you need to set&lt;strong&gt; a random priority&lt;/strong&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-purple&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💥&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;&lt;b&gt;&lt;strong style=&quot;white-space: pre-wrap;&quot;&gt;Random priorities &lt;/strong&gt;&lt;/b&gt;are key to the complexity. This makes the cartesian tree balance itself, making &lt;code spellcheck=&quot;false&quot; style=&quot;white-space: pre-wrap;&quot;&gt;O(logn)&lt;/code&gt; complexity for all operations&lt;/div&gt;&lt;/div&gt;&lt;ul&gt;&lt;li&gt;New head will be  &lt;code&gt;merge(first, merge(newNode, second))&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;See? It&apos;s that simple.&lt;/p&gt;&lt;figure class=&quot;kg-card kg-image-card kg-width-wide kg-card-hascaption&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2022/04/merge-example-specific.png&quot; class=&quot;kg-image&quot; alt=&quot;Insert example&quot; loading=&quot;lazy&quot; width=&quot;1440&quot; height=&quot;1048&quot; srcset=&quot;https://alexdremov.me/content/images/size/w600/2022/04/merge-example-specific.png 600w, https://alexdremov.me/content/images/size/w1000/2022/04/merge-example-specific.png 1000w, https://alexdremov.me/content/images/2022/04/merge-example-specific.png 1440w&quot; sizes=&quot;(min-width: 1200px) 1200px&quot;&gt;&lt;figcaption&gt;&lt;span style=&quot;white-space: pre-wrap;&quot;&gt;Insert example&lt;/span&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;Node* insert(Node* head, T key) {
    auto split = split(head, key);
    if (find(split.second, key) != nullptr) {
    	// Key exists already
        // Merge back
        return merge(split.first, split.second);
    }
    
    auto newNode = new Node(std::move(key), rand());
    return merge(split.first, merge(newNode, splitsplitted.second));
}&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;remove&quot;&gt;Remove&lt;/h3&gt;&lt;p&gt;It&apos;s very similar to &lt;code&gt;insert&lt;/code&gt;. However, that&apos;s where the &lt;code&gt;equalOnTheLeft&lt;/code&gt; flag is used.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Remember that the &lt;code spellcheck=&quot;false&quot; style=&quot;white-space: pre-wrap;&quot;&gt;second&lt;/code&gt; tree produced by &lt;code spellcheck=&quot;false&quot; style=&quot;white-space: pre-wrap;&quot;&gt;split&lt;/code&gt; contains items greater or &lt;b&gt;&lt;strong style=&quot;white-space: pre-wrap;&quot;&gt;equal &lt;/strong&gt;&lt;/b&gt;to the selected key&lt;/div&gt;&lt;/div&gt;&lt;p&gt;Therefore, the &lt;code&gt;second&lt;/code&gt; tree will contain the value that needs to be removed. But how to remove it from the tree? &lt;/p&gt;&lt;p&gt;Split again.&lt;/p&gt;&lt;p&gt;We can split the &lt;code&gt;second&lt;/code&gt; tree by key, setting the &lt;code&gt;equalOnTheLeft&lt;/code&gt; flag to &lt;code&gt;true&lt;/code&gt;. Thus, the node will be separated from the &lt;code&gt;second&lt;/code&gt; tree to the new tree.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;After conducting two splits and separating deleted node, unneded node is easely removed everything else is merged.&lt;/div&gt;&lt;/div&gt;&lt;figure class=&quot;kg-card kg-image-card kg-width-wide kg-card-hascaption&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2022/04/remove-example.png&quot; class=&quot;kg-image&quot; alt=&quot;Remove example&quot; loading=&quot;lazy&quot; width=&quot;1440&quot; height=&quot;1048&quot; srcset=&quot;https://alexdremov.me/content/images/size/w600/2022/04/remove-example.png 600w, https://alexdremov.me/content/images/size/w1000/2022/04/remove-example.png 1000w, https://alexdremov.me/content/images/2022/04/remove-example.png 1440w&quot; sizes=&quot;(min-width: 1200px) 1200px&quot;&gt;&lt;figcaption&gt;&lt;span style=&quot;white-space: pre-wrap;&quot;&gt;Remove example&lt;/span&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;Node *remove(Node *head, const T &amp;amp;key) {
    auto split = split(head, key);
    if (split.second) {
        auto secondSplit = split(split.second, key,
                                     /*equalOnTheLeft=*/true);
        // Key exists, so delete it and merge
        auto everythingElse = secondSplit.second;
        if (secondSplit.first == nullptr) {
            // There&apos;s no element equal to key. Merge back.
            return merge(split.first, everythingElse);
        }

        // We got node with key value in
        // secondSplit.first
        delete secondSplit.first;

        size--;
        return merge(split.first, everythingElse);
    }
    // Key is not presented. Merge back.
    return merge(split.first, split.second);
}&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;full-code&quot;&gt;Full code&lt;/h2&gt;&lt;p&gt;You can download C++ code of a little bit optimized Treap here:&lt;/p&gt;&lt;div class=&quot;kg-card kg-file-card&quot;&gt;&lt;a class=&quot;kg-file-card-container&quot; href=&quot;https://alexdremov.me/content/files/2022/04/treap.h&quot; title=&quot;Download&quot; download=&quot;&quot;&gt;&lt;div class=&quot;kg-file-card-contents&quot;&gt;&lt;div class=&quot;kg-file-card-title&quot;&gt;Treap&lt;/div&gt;&lt;div class=&quot;kg-file-card-caption&quot;&gt;C++ code allocations-optimised&lt;/div&gt;&lt;div class=&quot;kg-file-card-metadata&quot;&gt;&lt;div class=&quot;kg-file-card-filename&quot;&gt;treap.h&lt;/div&gt;&lt;div class=&quot;kg-file-card-filesize&quot;&gt;5 KB&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-file-card-icon&quot;&gt;&lt;svg viewBox=&quot;0 0 24 24&quot;&gt;&lt;defs&gt;&lt;style&gt;.a{fill:none;stroke:currentColor;stroke-linecap:round;stroke-linejoin:round;stroke-width:1.5px;}&lt;/style&gt;&lt;/defs&gt;&lt;title&gt;download-circle&lt;/title&gt;&lt;polyline class=&quot;a&quot; points=&quot;8.25 14.25 12 18 15.75 14.25&quot;&gt;&lt;/polyline&gt;&lt;line class=&quot;a&quot; x1=&quot;12&quot; y1=&quot;6.75&quot; x2=&quot;12&quot; y2=&quot;18&quot;&gt;&lt;/line&gt;&lt;circle class=&quot;a&quot; cx=&quot;12&quot; cy=&quot;12&quot; r=&quot;11.25&quot;&gt;&lt;/circle&gt;&lt;/svg&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;h2 id=&quot;comparing-to-stdset&quot;&gt;Comparing to &lt;code&gt;std::set&lt;/code&gt;&lt;/h2&gt;&lt;p&gt;First of all, the implemented version of treap utilizes &lt;code&gt;split&lt;/code&gt; and &lt;code&gt;merge&lt;/code&gt; methods. Note that there is more efficient implementation that uses rotations. However, the true power of treap is in &lt;code&gt;split&lt;/code&gt; and &lt;code&gt;merge&lt;/code&gt; methods as other search trees can&apos;t do it easily.&lt;/p&gt;&lt;h3 id=&quot;find-tests&quot;&gt;Find tests&lt;/h3&gt;&lt;figure class=&quot;kg-card kg-image-card kg-width-wide kg-card-hascaption&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2022/04/find-3.svg&quot; class=&quot;kg-image&quot; alt=&quot;Find operation test&quot; loading=&quot;lazy&quot; width=&quot;1152&quot; height=&quot;768&quot;&gt;&lt;figcaption&gt;&lt;span style=&quot;white-space: pre-wrap;&quot;&gt;Find operation test&lt;/span&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;It&apos;s visible that asymptotics is similar. Though, treap always has the greater overhead. Still, it&apos;s a good result! We&apos;re competing with an utterly optimized standard library data structure.&lt;/p&gt;&lt;h3 id=&quot;inserts&quot;&gt;Inserts&lt;/h3&gt;&lt;figure class=&quot;kg-card kg-image-card kg-width-wide kg-card-hascaption&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2022/04/insert.svg&quot; class=&quot;kg-image&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;1152&quot; height=&quot;768&quot;&gt;&lt;figcaption&gt;&lt;span style=&quot;white-space: pre-wrap;&quot;&gt;Insert operation test&lt;/span&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;Insertion has even bigger overhead. And it was expected: recursive calls of merge and split do not improve performance ;)&lt;/p&gt;&lt;div class=&quot;kg-card kg-file-card&quot;&gt;&lt;a class=&quot;kg-file-card-container&quot; href=&quot;https://alexdremov.me/content/files/2022/04/TreapProject-1.zip&quot; title=&quot;Download&quot; download=&quot;&quot;&gt;&lt;div class=&quot;kg-file-card-contents&quot;&gt;&lt;div class=&quot;kg-file-card-title&quot;&gt;TreapProject&lt;/div&gt;&lt;div class=&quot;kg-file-card-caption&quot;&gt;Comparisons tests. Outputs CSV of time measurments &lt;/div&gt;&lt;div class=&quot;kg-file-card-metadata&quot;&gt;&lt;div class=&quot;kg-file-card-filename&quot;&gt;TreapProject.zip&lt;/div&gt;&lt;div class=&quot;kg-file-card-filesize&quot;&gt;7 KB&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-file-card-icon&quot;&gt;&lt;svg viewBox=&quot;0 0 24 24&quot;&gt;&lt;defs&gt;&lt;style&gt;.a{fill:none;stroke:currentColor;stroke-linecap:round;stroke-linejoin:round;stroke-width:1.5px;}&lt;/style&gt;&lt;/defs&gt;&lt;title&gt;download-circle&lt;/title&gt;&lt;polyline class=&quot;a&quot; points=&quot;8.25 14.25 12 18 15.75 14.25&quot;&gt;&lt;/polyline&gt;&lt;line class=&quot;a&quot; x1=&quot;12&quot; y1=&quot;6.75&quot; x2=&quot;12&quot; y2=&quot;18&quot;&gt;&lt;/line&gt;&lt;circle class=&quot;a&quot; cx=&quot;12&quot; cy=&quot;12&quot; r=&quot;11.25&quot;&gt;&lt;/circle&gt;&lt;/svg&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;h3 id=&quot;comparison-conclusion&quot;&gt;Comparison conclusion&lt;/h3&gt;&lt;figure class=&quot;kg-card kg-image-card kg-width-wide&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2022/04/height50m-1.svg&quot; class=&quot;kg-image&quot; alt=&quot;Height of treap evaluation&quot; loading=&quot;lazy&quot; width=&quot;1152&quot; height=&quot;768&quot;&gt;&lt;/figure&gt;&lt;p&gt;As you see, treap has higher nodes&apos; height on average than that of very well-balanced AVL tree. &lt;/p&gt;&lt;p&gt;Yes, treap has worse performance than that of &lt;code&gt;std::set&lt;/code&gt;. Yet, the results are comparable, and with a large data size, treap gets closer and closer to &lt;code&gt;std::set&lt;/code&gt; which in fact is a red and black tree.&lt;/p&gt;&lt;p&gt;Believe me, &lt;strong&gt;you don&apos;t want to write your own RB tree&lt;/strong&gt;. It&apos;s a nightmare.&lt;/p&gt;&lt;h2 id=&quot;use-cases-and-modifications&quot;&gt;Use cases and modifications&lt;/h2&gt;&lt;p&gt;We developed this data structure not just to lose &lt;code&gt;std::set&lt;/code&gt;. There are several useful applications.&lt;/p&gt;&lt;h3 id=&quot;sum-of-numbers-in-the-interval&quot;&gt;Sum of numbers in the interval &lt;/h3&gt;&lt;p&gt;We need to modify &lt;code&gt;Node&lt;/code&gt; structure, adding &lt;code&gt;sum&lt;/code&gt; field. It will store sum of all its children and itself.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;template&amp;lt;typename T&amp;gt;
struct Node {
	T key;
	size_t prior;
	long long sum;
	Node* left = nullptr, *right = nullptr;

	Node(T key, size_t prior) :
		key(std::move(key)),
		prior(prior) {
	}
};&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It&apos;s extremely easy to update the &lt;code&gt;sum&lt;/code&gt;. Every time childs are changed, &lt;code&gt;sum = left-&amp;gt;sum + right-&amp;gt;sum&lt;/code&gt;. So, you can implement some kind of &lt;code&gt;update&lt;/code&gt; function and call it in split and merge right before returning value. That&apos;s it.&lt;/p&gt;&lt;p&gt;How to answer on request?&lt;/p&gt;&lt;p&gt;We receive interval &lt;code&gt;[l, r]&lt;/code&gt;. To calculate the sum of numbers on this interval, we can split the tree by &lt;code&gt;l&lt;/code&gt;, then split the second tree of the result by &lt;code&gt;r+1&lt;/code&gt; (or by &lt;code&gt;r&lt;/code&gt;, leaving equal elements on the left). In the end, we will have a tree containing all added numbers in the interval &lt;code&gt;[l, r]&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Complexity:&lt;/strong&gt; &lt;code&gt;O(logn)&lt;/code&gt; versus &lt;code&gt;O(n)&lt;/code&gt; naive.&lt;/p&gt;&lt;h3 id=&quot;using-a-hash-of-value-in-place-of-priority&quot;&gt;Using a hash of value in place of priority&lt;/h3&gt;&lt;p&gt;You can use a hash of value as a priority as a good hash function is pretty random. What benefits does it bring?&lt;/p&gt;&lt;p&gt;If keys and priorities are fixed, then no matter how you construct the treap or add elements, it&apos;s always going to have the same structure.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;You may think about it this way: keys fix x axis and priorities fix y axis of treap&lt;/div&gt;&lt;/div&gt;&lt;p&gt;Therefore, you can compare two sets in &lt;code&gt;O(n)&lt;/code&gt; as treaps containing the same values will have &lt;strong&gt;absolutely the same structure.&lt;/strong&gt;&lt;/p&gt;&lt;h2 id=&quot;implicit-treap&quot;&gt;Implicit treap&lt;/h2&gt;&lt;p&gt;What if we use &lt;strong&gt;the size of the left subtree&lt;/strong&gt; as a key? Then, we can use this key as an index. Wow. That means that we can represent a regular ordered array as a treap!&lt;/p&gt;&lt;p&gt;By doing this, we can:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;make insertions by random index&lt;br&gt; &lt;code&gt;O(logn)&lt;/code&gt; versus &lt;code&gt;O(n)&lt;/code&gt; naive&lt;/li&gt;&lt;li&gt;make deletions by random index&lt;br&gt; &lt;code&gt;O(logn)&lt;/code&gt; versus &lt;code&gt;O(n)&lt;/code&gt; naive&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;With great power, comes great responsibility.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-red&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;😡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Access by random index downgrades to &lt;code spellcheck=&quot;false&quot; style=&quot;white-space: pre-wrap;&quot;&gt;O(logn)&lt;/code&gt; versus &lt;code spellcheck=&quot;false&quot; style=&quot;white-space: pre-wrap;&quot;&gt;O(1)&lt;/code&gt; in the standard array.&lt;/div&gt;&lt;/div&gt;&lt;p&gt;If your algorithm requires a lot of array modifications and very few accesses/outputs, then it&apos;s the right choice. Moreover, you can convert treap into an array and back with &lt;code&gt;O(n)&lt;/code&gt; complexity.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-purple&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💥&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;I have implicit treap implemented in &lt;b&gt;&lt;strong style=&quot;white-space: pre-wrap;&quot;&gt;Swift&lt;/strong&gt;&lt;/b&gt;. It behaves just like the general array and implements a lot of optimisations. Check it out!&lt;/div&gt;&lt;/div&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://github.com/AlexRoar/swift-collections/tree/main/Sources/OrderedCollections/TreeArray?ref=alexdremov.me&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;swift-collections/Sources/OrderedCollections/TreeArray at main · AlexRoar/swift-collections&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;Commonly used data structures for Swift. Contribute to AlexRoar/swift-collections development by creating an account on GitHub.&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://github.com/fluidicon.png&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;GitHub&lt;/span&gt;&lt;span class=&quot;kg-bookmark-publisher&quot;&gt;AlexRoar&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://opengraph.githubassets.com/49005a356ceb6b3993976514f19972ef42448ba48f43e8a30af9ce7834af1d0a/AlexRoar/swift-collections&quot; alt=&quot;&quot; onerror=&quot;this.style.display = &apos;none&apos;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;h3 id=&quot;cut-paste-problem&quot;&gt;Cut-paste problem&lt;/h3&gt;&lt;p&gt;Imagine that you have a big string and you recieve requests to cut some part and to insert it somwhere. &lt;/p&gt;&lt;p&gt;This problem can be solved using treaps with implicit key. You can use splits to cut needed part and merge to insert it. &lt;/p&gt;&lt;h3 id=&quot;union-algorithm&quot;&gt;Union algorithm&lt;/h3&gt;&lt;p&gt;We can define &lt;strong&gt;Union&lt;/strong&gt; algo as a method that merges two treaps when relation between elements in trees is not known. The Union algorithm relies on a divide-and-conquer approach. It compares the roots of the two treaps, selects the node with the higher priority to be the new root, and uses a&amp;nbsp;&lt;code&gt;split&lt;/code&gt;&amp;nbsp;operation on the second treap based on the new root&apos;s key. It then recursively unites the left and right subtrees. This recursive method is significantly more efficient than the naive&amp;nbsp;&lt;code&gt;O(M log(N))&lt;/code&gt;&amp;nbsp;approach of inserting elements one by one, and it elegantly degrades to an expected&amp;nbsp;&lt;code&gt;O(n)&lt;/code&gt;&amp;nbsp;time when both treaps are of roughly equal size.&lt;/p&gt;&lt;p&gt;So, step by step:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Handle Base Cases. &lt;/strong&gt;If either treap is empty, return the other treap. If both are empty, return null.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Determine the New Root. &lt;/strong&gt;Compare the heap priority values of the roots of both treaps. Let&amp;nbsp;&lt;code&gt;T1&lt;/code&gt;&amp;nbsp;be the treap whose root has the higher priority, and&amp;nbsp;&lt;code&gt;T2&lt;/code&gt;&amp;nbsp;be the other treap. The root of&amp;nbsp;&lt;code&gt;T1&lt;/code&gt;&amp;nbsp;(let&apos;s call it&amp;nbsp;&lt;code&gt;R1&lt;/code&gt;) will become the root of the new united treap to preserve the heap property.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Split the Second Treap. &lt;/strong&gt;Perform a standard&amp;nbsp;&lt;code&gt;split&lt;/code&gt;&amp;nbsp;operation on&amp;nbsp;&lt;code&gt;T2&lt;/code&gt;&amp;nbsp;using the key of&amp;nbsp;&lt;code&gt;R1&lt;/code&gt;&amp;nbsp;as the splitting threshold. This divides&amp;nbsp;&lt;code&gt;T2&lt;/code&gt;&amp;nbsp;into two distinct sub-treaps:&lt;ul&gt;&lt;li&gt;&lt;code&gt;&lt;strong&gt;L2&lt;/strong&gt;&lt;/code&gt;: Contains all nodes from&amp;nbsp;&lt;code&gt;T2&lt;/code&gt;&amp;nbsp;with keys less than&amp;nbsp;&lt;code&gt;R1&lt;/code&gt;&apos;s key.&lt;/li&gt;&lt;li&gt;&lt;code&gt;&lt;strong&gt;R2&lt;/strong&gt;&lt;/code&gt;: Contains all nodes from&amp;nbsp;&lt;code&gt;T2&lt;/code&gt;&amp;nbsp;with keys greater than&amp;nbsp;&lt;code&gt;R1&lt;/code&gt;&apos;s key.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Recursively Unite Subtrees. &lt;/strong&gt;Recursively call the Union algorithm to construct the new left and right subtrees for&amp;nbsp;&lt;code&gt;R1&lt;/code&gt;:&lt;ul&gt;&lt;li&gt;Set&amp;nbsp;&lt;code&gt;R1&lt;/code&gt;&apos;s left child to be the result of&amp;nbsp;&lt;code&gt;Union(T1.left, L2)&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;Set&amp;nbsp;&lt;code&gt;R1&lt;/code&gt;&apos;s right child to be the result of&amp;nbsp;&lt;code&gt;Union(T1.right, R2)&lt;/code&gt;.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Return the United Treap. &lt;/strong&gt;Return&amp;nbsp;&lt;code&gt;R1&lt;/code&gt;. The resulting structure guarantees both the Binary Search Tree property (enforced by the&amp;nbsp;&lt;code&gt;split&lt;/code&gt; boundary) and the Heap property (enforced by the priority comparison).&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;faq&quot;&gt;FAQ&lt;/h2&gt;&lt;blockquote&gt;Cartesian trees are most suitable for what?&lt;/blockquote&gt;&lt;p&gt;Treap is useful when you need to collect some kind of characteristic on an interval (for example, sum) or apply some modification to the interval. Treap with implicit key is also useful when you need to apply a lot of random tree insertions/deletions with few accesses.&lt;/p&gt;&lt;blockquote&gt;Why don&apos;t we use array indices as keys for an implicit treap?&lt;/blockquote&gt;&lt;p&gt;Because in case of insertion we would need to recalculate all indeces that are higher than inserted index. Therefore, it downgrades complexity to &lt;code&gt;O(n)&lt;/code&gt;.&lt;/p&gt;&lt;blockquote&gt;Is treap a randomized tree?&lt;/blockquote&gt;&lt;p&gt;Yes, it is. But it can also use hash value in place of a random value.&lt;/p&gt;&lt;blockquote&gt;I know about implementation without split and merge. It utilizes left and right turns. Is it better?&lt;/blockquote&gt;&lt;p&gt;For example, GeeksforGeeks use such implementation, I know. But I believe that the true value of treap is in seampless splits and merges. You&apos;ve already seen by examples how it is really usefull. Why implementing treap with turns when you can build AVL that&apos;s probably going to be faster?&lt;/p&gt;&lt;blockquote&gt;If merge &lt;strong&gt;prerequisite&lt;/strong&gt; that &quot;all items in the first merged tree must be less than items in the second tree&quot; breaks, what is the complexity of merging two treaps?&lt;/blockquote&gt;&lt;p&gt;If this condition is violated, merge can no longer be used as it will lead to an invalid tree. Therefore, a &lt;strong&gt;Union&lt;/strong&gt; operation must be used. The expected time complexity of the Union operation is&amp;nbsp;&lt;code&gt;O(M log(N/M + 1))&lt;/code&gt;, where&amp;nbsp;&lt;code&gt;M&lt;/code&gt;&amp;nbsp;is the number of nodes in the smaller treap and&amp;nbsp;&lt;code&gt;N&lt;/code&gt;&amp;nbsp;is the number of nodes in the larger one.&lt;/p&gt;&lt;blockquote&gt;What is complexity of merging N treaps?&lt;/blockquote&gt;&lt;p&gt;If the &lt;code&gt;N&lt;/code&gt; treaps have strictly ordered keys, you can apply the standard merge operation. Let &lt;code&gt;V&lt;/code&gt; represent the total number of nodes across all &lt;code&gt;N&lt;/code&gt; treaps. Because a single merge takes &lt;code&gt;O(log V)&lt;/code&gt;, sequentially merging all &lt;code&gt;N&lt;/code&gt; treaps will take&amp;nbsp;&lt;code&gt;O(N log V)&lt;/code&gt;&amp;nbsp;expected time overall.&lt;/p&gt;&lt;p&gt;If the keys across the &lt;code&gt;N&lt;/code&gt; treaps overlap or interleave, the standard merge is invalid and you must use a &lt;strong&gt;Union&lt;/strong&gt; approach. By placing all treaps into a queue and repeatedly uniting pairs until only one remains, you do&amp;nbsp;&lt;code&gt;O(V)&lt;/code&gt;&amp;nbsp;work at each of the&amp;nbsp;&lt;code&gt;log N&lt;/code&gt;&amp;nbsp;levels of the union tree. This results in an expected time complexity of&amp;nbsp;&lt;code&gt;O(V log N)&lt;/code&gt;&amp;nbsp;to combine all &lt;code&gt;N&lt;/code&gt; overlapping treaps.&lt;/p&gt;&lt;h2 id=&quot;love-data-structures&quot;&gt;Love data structures?&lt;/h2&gt;&lt;p&gt;Check out my article on the amazing Skip List! While a lot of people never heard about it, Skip List is &lt;strong&gt;beautiful&lt;/strong&gt; and can solve, for example, the problem of finding the n-th maximum or the rolling median problem in the most efficient way.&lt;/p&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://alexdremov.me/skip-list-indexation-and-kth-maximum/&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;Skip List Indexation and kth Maximum | Alex Dremov&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;Skip List is a nice structure that lets you to perform insertions, searches, and finding n-th maximum. In this post I fokus on skip list indexation&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://alexdremov.me/assets/icons/apple-touch-icon.png&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;Alex Dremov&lt;/span&gt;&lt;span class=&quot;kg-bookmark-publisher&quot;&gt;Alex Dremov&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2022/04/--------------2020-11-06---01.51.30.png&quot; alt=&quot;&quot; onerror=&quot;this.style.display = &apos;none&apos;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;p&gt;Also, you can check the whole algorithms section of my blog&lt;/p&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://alexdremov.me/tag/algorithms/&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;Alex Dremov | Algorithms&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;Those are hard! In this section I discuss algorithms that I encountered during work or my college assignments&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://alexdremov.me/assets/icons/apple-touch-icon.png&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;Alex Dremov&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://images.unsplash.com/photo-1580777361964-27e9cdd2f838?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=max&amp;amp;fm=jpg&amp;amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDZ8fGFsZ29yaXRobXxlbnwwfHx8fDE2NDk1MDYwMDM&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;w=2000&quot; alt=&quot;&quot; onerror=&quot;this.style.display = &apos;none&apos;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://books.google.de/books?id=NLngYyWFl_YC&amp;pg=PA298&amp;lpg=PA298&amp;dq=treap+algorithm&amp;source=bl&amp;ots=BASmGA8mBd&amp;sig=ACfU3U17YFycVO2ztnR-zjL5yLbhEfv3VQ&amp;hl=en&amp;sa=X&amp;ved=2ahUKEwjxqr_r0qf3AhXD0qQKHcWWDjcQ6AF6BAgyEAM#v=onepage&amp;q=treap%20algorithm&amp;f=false&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;Introduction To Algorithms&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;The first edition won the award for Best 1990 Professional and Scholarly Book in Computer Science and Data Processing by the Association of American Publishers.There are books on algorithms that are rigorous but incomplete and others that cover masses of material but lack rigor. Introduction to Algo…&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;Google Books&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://books.google.de/books/content?id=NLngYyWFl_YC&amp;amp;printsec=frontcover&amp;amp;img=1&amp;amp;zoom=1&amp;amp;edge=curl&amp;amp;imgtk=AFLRE73PtOLetmXuVivAcv-TRLkC8fjpuL48GXZzQ576K23NJLUElL93yxbTDC9ES8rz_-HbjeVd9GkteTzhsSnzKtt9jLIQ-vYsdZyiETYa-lk1uLKXz-jXTl2sR8t2kpHJy_777gys&quot; alt=&quot;&quot; onerror=&quot;this.style.display = &apos;none&apos;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://algorithmica.org/ru/treap?ref=alexdremov.me&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;Декартово дерево - Алгоритмика&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;data:image/x-icon;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAQAAADZc7J/AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QA/4ePzL8AAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfjBggQFxAS4ilBAAACgElEQVRIx52VS0iUYRSGn/8fzbxMIYoRKSZKi2wGwo2FxRQZIlGbaGUF0b52LWbTLlx0W7VyEZiBWkE5mraRRrJFRUwolQw6zQTeuoiU423eFuk0M//nhJ7dd+Z7n/8932HOscgIgU0lR/Cxn3LKgGlijDJIkAgJiywhZMujVo0qrsyIa1St8shWFnmp/IooW0TkV6mMYuRVQCv6X6woIK+QQ16vkPP2M/WZICHVpyGEvCb5vE7olH6bEV6l1R4w3RpQkYoVNJcSWHsLIUt+rTpvLOuSELqihAmwKr8sgZBHE2aX5UKoRmNmDxPyCBtoodLU1m5yaADC9Jr7XkkLoCqNmPAx1eqqnqhAqEHfzB5GVGXTQLUJ/5xZznGcOuAtQbOHahpsfOQ5f5mng8McZAdnsVigkyUTIA8fGjZ561OpuiRJY6oR2qU35iKGbSqc4GU6qObYmstmYIrH5iIq0JIT+17lupU8BVUsVKsvJgdLtgnbxXZOJ091HAU+0W+0YCnGnvRUlCa+42OdbRHiA3CSbtyZ+q85RDMBfcxxjZKUf5uPO4wwzGsaMwFR1JZe1Jx8uuB4mBtC6LJzXLTZDLKYihziM+fJzfjQGfYC/XxMTy8yaDNEOLWBDznAIcdT7aMRiPI0PR1myGacnn+Zd7ygiUIHwEUjuUAnsdR0D+M20E7k7/knt5ki39guN9uAEPeIr6citIPrOsxQiC9hveIuLylkkgXyKUsR/2CAB8yyEzdhJiliN3aCmzwCCwSl3FfzDL9wYZFAuClJAcSZJoELALFKAWVYvVxkdm3NbDRUs0TqUM021jeU15s2w9YXSxKy9dWWRGx6uVpOyObW+x/B+LEV0hF3cAAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxOS0wNi0wOFQxNjoyMzoxNiswMjowMLEfSBUAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMTktMDYtMDhUMTY6MjM6MTYrMDI6MDDAQvCpAAAAV3pUWHRSYXcgcHJvZmlsZSB0eXBlIGlwdGMAAHic4/IMCHFWKCjKT8vMSeVSAAMjCy5jCxMjE0uTFAMTIESANMNkAyOzVCDL2NTIxMzEHMQHy4BIoEouAOoXEXTyQjWVAAAAAElFTkSuQmCC&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;Алгоритмика&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://hsto.org/storage/habraeffect/a1/0a/a10a744def8f325a1019502ecc175ef6.png&quot; alt=&quot;&quot; onerror=&quot;this.style.display = &apos;none&apos;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;p&gt;&lt;a href=&quot;https://www.cs.cmu.edu/~scandal/papers/treaps-spaa98.pdf?ref=alexdremov.me&quot;&gt;https://www.cs.cmu.edu/~scandal/papers/treaps-spaa98.pdf&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title>Type Placeholders: New Swift 5.6 Feature</title><link>https://alexdremov.me/swift-type-placeholders/</link><guid isPermaLink="true">https://alexdremov.me/swift-type-placeholders/</guid><description>Type placeholders were recently introduced in Swift 5.6. Get in touch with new useful Swift feature.</description><pubDate>Thu, 21 Apr 2022 04:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Type placeholders were recently introduced in Swift 5.6. And yes, they are a nice add-on to powerful Swift type inference system. If you are familiar with C++, you must know about an &lt;code&gt;auto&lt;/code&gt; keyword. Type placeholders are &lt;em&gt;almost&lt;/em&gt; the same.&lt;/p&gt;&lt;h2 id=&quot;generics-and-type-placeholder&quot;&gt;Generics and type placeholder&lt;/h2&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;let number: _ = 42 // Type placeholder
let anotherNumber = 42&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Yes, Swift can infer variable&apos;s type, but type placeholders mean to be used for a type with &lt;strong&gt;multiple types in it&lt;/strong&gt;. Generics. That&apos;s where they really shine.&lt;/p&gt;&lt;p&gt;Consider regular &lt;code&gt;Result&lt;/code&gt; enum&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;enum Result&amp;lt;Success, Failure&amp;gt; where Failure : Error {
    case success(Success)
    case failure(Failure)
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And what if we have some kind of complex object&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;var ohMy = [1: [3: (1, 2, 3, &quot;That&apos;s a long tuple&quot;)]]&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If you will try to create a &lt;code&gt;Result&lt;/code&gt;  from &lt;code&gt;ohMy&lt;/code&gt;, you&apos;ll see compilation error.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;let result = Result.success(ohMy)&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-red&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;😡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Generic parameter &lt;code&gt;Failure&lt;/code&gt; could not be inferred&lt;/div&gt;&lt;/div&gt;&lt;p&gt;Bruh. So I need to write...&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;let result = Result&amp;lt;[Int : [Int : (Int, Int, Int, String)]], Error&amp;gt;.success(ohMy)&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Use type placeholders to omit type that Swift can infer&lt;/div&gt;&lt;/div&gt;&lt;p&gt;Thanks to type placeholders, no. Swift can infer object&apos;s type by itself. So, we need to provide &lt;code&gt;Failure&lt;/code&gt; type only.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;let result = Result&amp;lt;_, Error&amp;gt;.success(ohMy) // Nice&lt;/code&gt;&lt;/pre&gt;&lt;!--kg-card-begin: html--&gt;&lt;section class=&quot;custom-replace&quot; data-replace=&quot;.subscription-article&quot;&gt;
    &lt;a class=&quot;gh-portal-triggerbtn-container&quot;&gt;Subscribe and don&apos;t miss posts!&lt;/a&gt;
&lt;/section&gt;&lt;!--kg-card-end: html--&gt;&lt;h2 id=&quot;collections-and-type-placeholder&quot;&gt;Collections and type placeholder&lt;/h2&gt;&lt;p&gt;This feature also useful with collections. What if we need a dictionary with enum keys?&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;enum Foo {
	case bizz
	case bonk
}

let results = [
	.bizz: ohMy,
	.bonk: ohMy
]&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-red&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;😡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Reference to member &lt;code&gt;bizz&lt;/code&gt; cannot be resolved without a contextual type&lt;/div&gt;&lt;/div&gt;&lt;p&gt;So, let&apos;s provide this &lt;em&gt;contextual type, &lt;/em&gt;but you remember how &lt;code&gt;ohMy&lt;/code&gt;&apos;s type is bad-looking? Let&apos;s use type placeholder.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;// 🚫
let results:[Foo: [Int : [Int : (Int, Int, Int, String)]]] = [
	.bizz: ohMy,
	.bonk: ohMy
]

// ✅
let results:[Foo: _] = [
	.bizz: ohMy,
	.bonk: ohMy
]&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;more-examples&quot;&gt;More examples&lt;/h2&gt;&lt;p&gt;Examples of types containing placeholders are:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;Array&amp;lt;_&amp;gt; // array with placeholder element type
[Int: _] // dictionary with placeholder value type
(_) -&amp;gt; Int // function type accepting a single type placeholder argument and returning &apos;Int&apos;
(_, Double) // tuple type of placeholder and &apos;Double&apos;
_? // optional wrapping a type placeholder&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;final-notes&quot;&gt;Final notes&lt;/h2&gt;&lt;p&gt;That&apos;s a great feature and broadens Swift’s type inference capabilities. For now, it&apos;s some kind of less-known, but I think it will be more used in the future.&lt;/p&gt;&lt;p&gt;You can check out other less-known Swift features in my previous post:&lt;/p&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://alexdremov.me/top-7-subtle-swift-features/&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;Top 7 Subtle Swift Features | Alex Dremov&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;Here, I collected Swift features that are less known and can be useful when you prepare for interviews or want to deepen your Swift knowledge.&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://alexdremov.me/assets/icons/apple-touch-icon.png&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;Alex Dremov&lt;/span&gt;&lt;span class=&quot;kg-bookmark-publisher&quot;&gt;Alex Dremov&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2022/04/Artboard-1-1.png&quot; alt=&quot;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://github.com/apple/swift-evolution/blob/main/proposals/0315-placeholder-types.md?ref=alexdremov.me&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;swift-evolution/0315-placeholder-types.md at main · apple/swift-evolution&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;This maintains proposals for changes and user-visible enhancements to the Swift Programming Language. - swift-evolution/0315-placeholder-types.md at main · apple/swift-evolution&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://github.com/fluidicon.png&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;GitHub&lt;/span&gt;&lt;span class=&quot;kg-bookmark-publisher&quot;&gt;apple&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://opengraph.githubassets.com/c17082dded0015da2efcc475a488d2710ae6bf2f831faac7614e539d5739e9a2/apple/swift-evolution&quot; alt=&quot;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;</content:encoded></item><item><title>Quick Guide to Async Await in Swift</title><link>https://alexdremov.me/quick-guide-to-async-await-in-swift/</link><guid isPermaLink="true">https://alexdremov.me/quick-guide-to-async-await-in-swift/</guid><description>Everything you need to know about new Swift asynchronous features. Async await, main actor, task, async get, and possible use cases — all covered</description><pubDate>Sat, 16 Apr 2022 08:50:00 GMT</pubDate><content:encoded>&lt;p&gt;How to create asynchronous functions, run code in parallel, who is MainActor, what is the closures pyramid and how to get rid of it? Let&apos;s start.&lt;/p&gt;
&lt;!--kg-card-begin: html--&gt;
&lt;h2 id=&quot;straight-to-the-point&quot;&gt;Straight to the point&lt;/h2&gt;
&lt;!--kg-card-end: html--&gt;
&lt;p&gt;Swift 5.5 introduced built-in support for writing asynchronous and parallel code in a structured way. &lt;em&gt;Asynchronous code&lt;/em&gt; can be suspended and resumed later, although only one piece of the program executes at a time.&lt;/p&gt;&lt;p&gt;Keyword &lt;code&gt;async&lt;/code&gt; is used to mark function as asynchronous. That&apos;s it.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;func downloadNames(fromServer name: String) async -&amp;gt; [String] {
    ... // some other tasks
    return data
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;But what does it really mean?&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-purple&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💥&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;The async function can be suspended in the middle of the execution when it’s waiting for something.&lt;/div&gt;&lt;/div&gt;&lt;p&gt;Here&apos;s how &lt;code&gt;async&lt;/code&gt; functions can be called&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;let namesMain = await downloadNames(fromServer: &quot;main&quot;)
let secondary = await downloadNames(fromServer: &quot;secondary&quot;)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;When you type &lt;code&gt;await&lt;/code&gt;, the current execution is suspended, until an asynchronous call is finished.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Suspension is never implicit or preemptive — &lt;b&gt;&lt;strong style=&quot;white-space: pre-wrap;&quot;&gt;each &lt;/strong&gt;&lt;/b&gt;such place is marked with the &lt;code spellcheck=&quot;false&quot; style=&quot;white-space: pre-wrap;&quot;&gt;await&lt;/code&gt; keyword.&lt;/div&gt;&lt;/div&gt;
&lt;!--kg-card-begin: html--&gt;
&lt;h2 id=&quot;where-to-call-async-functions&quot;&gt;Where to call async functions&lt;/h2&gt;
&lt;!--kg-card-end: html--&gt;
&lt;p&gt;As I said before, &lt;code&gt;await&lt;/code&gt; suspends current execution. But there must be a structure underneath that can be suspended. You can&apos;t suspend a raw thread or the main thread, for example.&lt;/p&gt;&lt;p&gt;You opened Playgrounds, right?&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Do not use Swift Playgrounds to test new concurrency features as they are not fully supported yet&lt;/div&gt;&lt;/div&gt;&lt;p&gt;If you try to call an async function in an inappropriate place, you will see this error&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-red&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;😡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;&lt;code spellcheck=&quot;false&quot; style=&quot;white-space: pre-wrap;&quot;&gt;async&lt;/code&gt; call in a function that does not support concurrency&lt;/div&gt;&lt;/div&gt;&lt;p&gt;That&apos;s because an asynchronous function can be called only in:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Code in the body of an asynchronous function, method, or property.&lt;/li&gt;&lt;li&gt;Code in the static &lt;code&gt;main()&lt;/code&gt; method of a structure, class, or enumeration that’s marked with &lt;code&gt;@main&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;Code in an unstructured child task&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;That&apos;s a lot of words.&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;For most developers, only the first and the last points make sense. Most of the places in your code do not support &lt;code&gt;await&lt;/code&gt;. How to deal with that?&lt;/p&gt;
&lt;!--kg-card-begin: html--&gt;
&lt;h2 id=&quot;tasks-and-tasks&quot;&gt;Tasks and TaskGroup&lt;/h2&gt;
&lt;!--kg-card-end: html--&gt;
&lt;h3 id=&quot;task&quot;&gt;Task&lt;/h3&gt;&lt;p&gt;To call an asynchronous function in a place that does not support concurrency, you need to create a concurrent task. You can use &lt;code&gt;Task&lt;/code&gt; and &lt;code&gt;TaskGroup&lt;/code&gt; to achieve that.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;Task {
    let names = await downloadNames(fromServer: &quot;main&quot;)
    ... // futher work
    ... // take over the world (asynchronously)

}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;When you create an instance of &lt;code&gt;Task&lt;/code&gt;, you provide a closure that contains the work for that task to perform. Tasks can start running immediately after creation and may not. You can create a task in another &lt;code&gt;Task&lt;/code&gt; or other concurrent environments.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;let handle = Task { // Creates asynchronous task
	let names = await downloadNames(fromServer: &quot;main&quot;)
    
	Task { // Creates asynchronous task
		await save(names: names)
	}
    
	for name in names {
		print(name)
	}
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;After creating a task, you use the instance to interact with it — for example, to wait for it to complete or to cancel it. Tasks run independently from their handles.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;To cancel a task, you can &lt;b&gt;&lt;strong style=&quot;white-space: pre-wrap;&quot;&gt;throw an error, return nil, or return partially completed work.&lt;/strong&gt;&lt;/b&gt;Use &lt;code spellcheck=&quot;false&quot; style=&quot;white-space: pre-wrap;&quot;&gt;Task.isCancelled&lt;/code&gt; to check if the current task was cancelled.&lt;/div&gt;&lt;/div&gt;&lt;h3 id=&quot;taskgroup-to-group-the-tasks&quot;&gt;TaskGroup to group the tasks&lt;/h3&gt;&lt;p&gt;&lt;code&gt;TaskGroup&lt;/code&gt; lets you launch several tasks and wait for the completion of all of them. The order in which these tasks are completed is not defined.&lt;/p&gt;&lt;p&gt;How to create it?&lt;/p&gt;&lt;p&gt;&lt;code&gt;TaskGroup&lt;/code&gt; is created through &lt;code&gt;withTaskGroup(of:)&lt;/code&gt;. You provide closure in which you spawn new tasks and perform operations on returned data.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;let calculations = await withTaskGroup(of: Int.self) { group -&amp;gt; Int in
	group.addTask { 1 * 2 } // () -&amp;gt; Int
	group.addTask { 2 * 3 }
	group.addTask { 3 * 4 }
	group.addTask { 4 * 5 }
	group.addTask { 5 * 6 }

	var collected = [Int]()

	for await value in group {
		collected.append(value)
	}

	return collected
}&lt;/code&gt;&lt;/pre&gt;&lt;figure class=&quot;kg-card kg-image-card kg-card-hascaption&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2022/04/gyAFz.jpg&quot; class=&quot;kg-image&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;400&quot; height=&quot;400&quot;&gt;&lt;figcaption&gt;&lt;span style=&quot;white-space: pre-wrap;&quot;&gt;http://i.imgur.com/gyAFz.jpg&lt;/span&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;The &lt;code&gt;group&lt;/code&gt; object inside closure conforms to &lt;code&gt;AsyncSequence&lt;/code&gt;. It&apos;s just like a general sequence, but elements are generated asynchronously. To iterate over it you can use &lt;code&gt;.next()&lt;/code&gt; method or &lt;code&gt;for await ... in sequence&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;It can be used to parallelize &lt;code&gt;for&lt;/code&gt; loops, for example.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;let calculations = await withTaskGroup(of: Int.self) {[works] group -&amp;gt; [Int] in
	for work in works {
		group.addTask { work() }
	}

	var collected = [Int]()

	for await value in group {
		collected.append(value)
	}

	return collected
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;That&apos;s great, but how to perform unrelated tasks concurrently without TaskGroup?&lt;/p&gt;
&lt;!--kg-card-begin: html--&gt;
&lt;h2 id=&quot;async-let&quot;&gt;Async let, async get, concurrent execution&lt;/h2&gt;
&lt;!--kg-card-end: html--&gt;
&lt;p&gt;These features seem like a real power to me. &lt;/p&gt;&lt;p&gt;Imagine you need to load an article, and data stored on different services or URLs: &lt;/p&gt;&lt;ul&gt;&lt;li&gt;Article thumbnail&lt;/li&gt;&lt;li&gt;Article text&lt;/li&gt;&lt;li&gt;Related articles&lt;/li&gt;&lt;li&gt;Comments&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;And the most obvious way to load all data is to write such code&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;let thumbnail = await loadThumbnail(forPost: post)
let text = await loadArticleText(forPost: post)
let related = await loadRelatedArticles(forPost: post)
let comments = await loadComments(forPost: post)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And this is mighty concurrent code that will load needed information the fastest way. Right? Not really.&lt;/p&gt;&lt;figure class=&quot;kg-card kg-image-card&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2022/04/suspension-await-async.png&quot; class=&quot;kg-image&quot; alt=&quot;Execution of async await code visualisation&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;800&quot; srcset=&quot;https://alexdremov.me/content/images/size/w600/2022/04/suspension-await-async.png 600w, https://alexdremov.me/content/images/size/w1000/2022/04/suspension-await-async.png 1000w, https://alexdremov.me/content/images/2022/04/suspension-await-async.png 1280w&quot; sizes=&quot;(min-width: 720px) 720px&quot;&gt;&lt;/figure&gt;&lt;p&gt;Code is still executed serially and assets are not loaded in parallel. Each step waits until data is loaded. You can spawn a task for every step, sure. But is it really a nice solution?&lt;/p&gt;&lt;!--members-only--&gt;&lt;p&gt;That&apos;s where &lt;code&gt;async let&lt;/code&gt; and &lt;code&gt;async get&lt;/code&gt; come in handy.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Use &lt;code spellcheck=&quot;false&quot; style=&quot;white-space: pre-wrap;&quot;&gt;async let&lt;/code&gt; to start asynchronous tasks in parallel and to wait for their completion only when data is actually needed.&lt;/div&gt;&lt;/div&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;async let thumbnail = loadThumbnail(forPost: post)
async let text = loadArticleText(forPost: post)
async let related = loadRelatedArticles(forPost: post)
async let comments = loadComments(forPost: post)

// take over the world (synchronously)

let postInformation = await Post(thumbnail, text, related, comments)&lt;/code&gt;&lt;/pre&gt;&lt;figure class=&quot;kg-card kg-image-card&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2022/04/async-let-await.png&quot; class=&quot;kg-image&quot; alt=&quot;async let asynchronous explanation&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;800&quot; srcset=&quot;https://alexdremov.me/content/images/size/w600/2022/04/async-let-await.png 600w, https://alexdremov.me/content/images/size/w1000/2022/04/async-let-await.png 1000w, https://alexdremov.me/content/images/2022/04/async-let-await.png 1280w&quot; sizes=&quot;(min-width: 720px) 720px&quot;&gt;&lt;/figure&gt;&lt;p&gt;Wow! Four tasks run in parallel.&lt;/p&gt;&lt;p&gt;As you see, all four tasks launch and start to run in the background. Then, &lt;code&gt;Post&lt;/code&gt; object is created only when all four functions return. As you remember, suspension can happen only when you use &lt;code&gt;await&lt;/code&gt; keyword. &lt;/p&gt;&lt;p&gt;Computed properties also can be async with &lt;code&gt;async get&lt;/code&gt;&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Use &lt;code spellcheck=&quot;false&quot; style=&quot;white-space: pre-wrap;&quot;&gt;async&lt;/code&gt; properties to load the object&apos;s computed information concurrently&lt;/div&gt;&lt;/div&gt;&lt;p&gt;Let our &lt;code&gt;Post&lt;/code&gt; provide the ability to load full JSON data&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;class Post {
    ...
    
    var fullJsonData: String {
        get async throws {
            let (data, _) = try await URLSession.shared.data(from: jsonUrl)
            return String(bytes: data, encoding: String.Encoding.utf8)
        }
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And to efficiently load JSON data for several posts&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;async let jsones = [
	currentPost.fullJsonData,
    nextPost.fullJsonData,
    previousPost.fullJsonData
]

save(jsonData: await jsones)&lt;/code&gt;&lt;/pre&gt;
&lt;!--kg-card-begin: html--&gt;
&lt;h2 id=&quot;usecases&quot;&gt;Use cases and examples&lt;/h2&gt;
&lt;!--kg-card-end: html--&gt;
&lt;h3 id=&quot;closures&quot;&gt;Closures&lt;/h3&gt;&lt;p&gt;First of all, If you have ever written concurrent code, you know that most APIs are closure-based. If you needed to write some complex networking code, you probably already constructed some kind of &lt;strong&gt;closure pyramid.&lt;/strong&gt;&lt;/p&gt;&lt;figure class=&quot;kg-card kg-gallery-card kg-width-wide kg-card-hascaption&quot;&gt;&lt;div class=&quot;kg-gallery-container&quot;&gt;&lt;div class=&quot;kg-gallery-row&quot;&gt;&lt;div class=&quot;kg-gallery-image&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2022/04/1-Bv8Ol0xIyHtkoYfl1Wv9Og.png&quot; width=&quot;694&quot; height=&quot;394&quot; loading=&quot;lazy&quot; alt=&quot;&quot; srcset=&quot;https://alexdremov.me/content/images/size/w600/2022/04/1-Bv8Ol0xIyHtkoYfl1Wv9Og.png 600w, https://alexdremov.me/content/images/2022/04/1-Bv8Ol0xIyHtkoYfl1Wv9Og.png 694w&quot;&gt;&lt;/div&gt;&lt;div class=&quot;kg-gallery-image&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2022/04/1-YoTPCR_l1ApgGGfMp6ZzmQ.png&quot; width=&quot;962&quot; height=&quot;674&quot; loading=&quot;lazy&quot; alt=&quot;&quot; srcset=&quot;https://alexdremov.me/content/images/size/w600/2022/04/1-YoTPCR_l1ApgGGfMp6ZzmQ.png 600w, https://alexdremov.me/content/images/2022/04/1-YoTPCR_l1ApgGGfMp6ZzmQ.png 962w&quot; sizes=&quot;(min-width: 720px) 720px&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;figcaption&gt;&lt;p&gt;&lt;span style=&quot;white-space: pre-wrap;&quot;&gt;Closure pyramide in old asynchronous code&lt;/span&gt;&lt;/p&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;Now, you don&apos;t need to pass a closure to catch data after the completion of an asynchronous task. You can wait for it with &lt;code&gt;await&lt;/code&gt; keyword and your code will no longer look like a pyramid.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-purple&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💥&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Also, you can just forget to call a callback and nothing will remind you of that&lt;/div&gt;&lt;/div&gt;&lt;h3 id=&quot;networking&quot;&gt;Networking&lt;/h3&gt;&lt;p&gt;&lt;code&gt;URLSession&lt;/code&gt; now supports &lt;code&gt;async&lt;/code&gt;/ &lt;code&gt;await&lt;/code&gt;! &lt;/p&gt;&lt;p&gt;Therefore, networking code now is so much easier to read&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;let (content, _) = try await URLSession.shared.data(from: url)
// some work
let (text, _) = try await URLSession.shared.data(from: url)
// some work
let (image, _) = try await URLSession.shared.data(from: url)&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;ui-updates-and-mainactor&quot;&gt;UI updates and MainActor&lt;/h3&gt;&lt;p&gt;The first thing you discover when trying to search for asynchronous tasks in UI is that UI &lt;strong&gt;must&lt;/strong&gt; be updated from the main thread. And this resulted in this kind of code&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;func someUIUpdatingFunction() {
	// generating ui for taking over the world
    DispatchQueue.main.async {
    	// updates
    }
    // adding emoji
    DispatchQueue.main.async {
    	// updates
    }
}

...

DispatchQueue.global().async{
	someUIUpdatingFunction()
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;That looks chunky and async / await code can do better.&lt;/p&gt;&lt;p&gt;Swift introduced &lt;code&gt;@MainActor&lt;/code&gt;. It can be used on classes, functions, structs, properties, computed properties, closures, etc.&lt;/p&gt;&lt;p&gt;What it does is tells swift that operations marked with &lt;code&gt;@MainActor&lt;/code&gt; must be executed on the main thread.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;@MainActor
func someUIUpdatingFunction() {
	// generating ui for taking over the world
    // updates
    // adding emoji
    // updates
}

...

Task {
	await someUIUpdatingFunction()
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Or, if used for classes or structs, it makes all method calls and property accesses be executed on the main thread.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;@MainActor
class PostDisplay {

	func updateView() async {
    	// executed on the main thread
    }
    
}&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Use &lt;code spellcheck=&quot;false&quot; style=&quot;white-space: pre-wrap;&quot;&gt;@MainActor&lt;/code&gt; in classes wisely. If only several methods require running on the main thread, then mark only them and not the whole structure.&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-purple&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💥&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Find out more about actors in a new post!&lt;/div&gt;&lt;/div&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://alexdremov.me/conquer-data-races-with-swift-actors/&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;Conquer Data Races with Swift Actors&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;Unleash the power of Swift concurrency with Actors! Get all the information you need in this comprehensive article&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://alexdremov.me/content/images/icon/icon-192x192-f19dea29-0764-48bf-b5d6-66b2fd3c1977.png&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;Alex Dremov&lt;/span&gt;&lt;span class=&quot;kg-bookmark-publisher&quot;&gt;Alex Dremov&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/thumbnail/photo-1532800783378-1bed60adaf58-bf824424-ed56-4872-b9e4-3ef521ebe867&quot; alt=&quot;&quot; onerror=&quot;this.style.display = &apos;none&apos;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;h2 id=&quot;final-notes&quot;&gt;Final notes&lt;/h2&gt;&lt;p&gt;That&apos;s the end of the quick guide. Note that Swift&apos;s API is &lt;strong&gt;massive &lt;/strong&gt;and there&apos;s still a lot to cover and elaborate on. Check references to get a better understanding of some topics.&lt;/p&gt;&lt;p&gt;Also, check out an article on Swift&apos;s subtle features if you are unfamiliar with less-known Swift functionality!&lt;/p&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://alexdremov.me/top-7-subtle-swift-features/&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;Top 7 Subtle Swift Features&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;I collected Swift features that are less known and can be useful when you prepare for interviews or want to deepen your Swift knowledge.&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://alexdremov.me/content/images/icon/icon-192x192-47a8b545-2a60-442d-beb0-c2fac8d0a837.png&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;Alex Dremov&lt;/span&gt;&lt;span class=&quot;kg-bookmark-publisher&quot;&gt;Alex Dremov&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/thumbnail/Artboard-1-1-c72248eb-59d7-4da4-8e83-1f3050c48234.png&quot; alt=&quot;&quot; onerror=&quot;this.style.display = &apos;none&apos;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://docs.swift.org/swift-book/documentation/the-swift-programming-language/concurrency/?ref=alexdremov.me&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;Documentation&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://alexdremov.me/content/images/icon/favicon-770f181f-14ae-42f5-8ba6-519da5a245d1.ico&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;Swift.org&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://www.raywenderlich.com/books/modern-concurrency-in-swift/v1.0/chapters/2-getting-started-with-async-await?ref=alexdremov.me&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;Modern Concurrency in Swift, Chapter 2: Getting Started With async/await&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;Go into more detail about how the async/await syntax and the cooperative asynchronous execution work. Additionally, it introduces the usage of “async let” to design concurrent code and the “Task” type which encapsulates asynchronous execution in the modern concurrency model.&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://www.raywenderlich.com/apple-touch-icon.png&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;raywenderlich.com&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://assets.alexandria.raywenderlich.com/books/5686df272ebe17522460d1e9df428b11e20e4b9082093262998ce29c90d9c99c/images/eada75decf8ab02b0ae1e352d5cf7692/original.png&quot; alt=&quot;&quot; onerror=&quot;this.style.display = &apos;none&apos;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://github.com/apple/swift-evolution/blob/main/proposals/0296-async-await.md?ref=alexdremov.me#reasync&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;swift-evolution/0296-async-await.md at main · apple/swift-evolution&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;This maintains proposals for changes and user-visible enhancements to the Swift Programming Language. - swift-evolution/0296-async-await.md at main · apple/swift-evolution&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://github.com/fluidicon.png&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;GitHub&lt;/span&gt;&lt;span class=&quot;kg-bookmark-publisher&quot;&gt;apple&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://opengraph.githubassets.com/55c1070ec657c45c2e03864767e01a6eac0df785a897a8f5d9efec3cf1881a15/apple/swift-evolution&quot; alt=&quot;&quot; onerror=&quot;this.style.display = &apos;none&apos;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://github.com/apple/swift-evolution/blob/main/proposals/0306-actors.md?ref=alexdremov.me&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;swift-evolution/0306-actors.md at main · apple/swift-evolution&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;This maintains proposals for changes and user-visible enhancements to the Swift Programming Language. - swift-evolution/0306-actors.md at main · apple/swift-evolution&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://github.com/fluidicon.png&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;GitHub&lt;/span&gt;&lt;span class=&quot;kg-bookmark-publisher&quot;&gt;apple&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://opengraph.githubassets.com/55c1070ec657c45c2e03864767e01a6eac0df785a897a8f5d9efec3cf1881a15/apple/swift-evolution&quot; alt=&quot;&quot; onerror=&quot;this.style.display = &apos;none&apos;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://developer.apple.com/documentation/swift/taskgroup?ref=alexdremov.me&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;TaskGroup | Apple Developer Documentation&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;A group that contains dynamically created child tasks.&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://alexdremov.me/content/images/icon/favicon-2b0fc38c-6eff-47c9-b0af-ad919776d865.ico&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;Apple Developer Documentation&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/thumbnail/developer-og-dd6ae72d-4719-4515-983c-18b58d3a09f0.jpg&quot; alt=&quot;&quot; onerror=&quot;this.style.display = &apos;none&apos;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;h2 id=&quot;structure&quot;&gt;Structure&lt;/h2&gt;&lt;ol&gt;&lt;li&gt;&lt;a href=&quot;#straight-to-the-point&quot;&gt;To the point: how to write async functions&lt;/a&gt; &lt;/li&gt;&lt;li&gt;&lt;a href=&quot;#where-to-call-async-functions&quot;&gt;Where you can call async functions&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;#tasks-and-tasks&quot;&gt;Tasks and TaskGroup&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;#async-let&quot;&gt;Async let, async get&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;#usecases&quot;&gt;Use cases and examples&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;</content:encoded></item><item><title>Top 7 Subtle Swift Features</title><link>https://alexdremov.me/top-7-subtle-swift-features/</link><guid isPermaLink="true">https://alexdremov.me/top-7-subtle-swift-features/</guid><description>I collected Swift features that are less known and can be useful when you prepare for interviews or want to deepen your Swift knowledge.</description><pubDate>Sat, 09 Apr 2022 10:41:05 GMT</pubDate><content:encoded>&lt;h2 id=&quot;1-keyword-indirect&quot;&gt;1. Keyword &lt;code&gt;indirect&lt;/code&gt;&lt;/h2&gt;&lt;p&gt;It’s used with enums only. As you know, enums are &lt;strong&gt;value type&lt;/strong&gt; and stored on the stack. Therefore, the compiler needs to know how much memory each enum takes. As only one option is possible at any moment, the enum occupies the memory of the largest case plus some operational information.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;// Just a general enum, nothing fancy
enum Foo {
    case bizz(String)
    case fizz(Int)
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;But what if we make enum dependant on itself?&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;// Infinite size??
enum Foo {
    case bizz(Foo)
    case fizz
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This definition generates a compiler error.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-red&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;😡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Recursive enum &lt;code&gt;Foo&lt;/code&gt; is not marked &lt;code&gt;indirect&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;The error makes sense: the compiler can’t calculate &lt;code&gt;Foo&lt;/code&gt; size as it tends to infinity. Here comes the &lt;code&gt;indirect&lt;/code&gt; keyword.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;// Oh, fine
enum Foo {
    indirect case bizz(Foo)
    case fizz
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Simple:&lt;/strong&gt; it modifies the enum memory structure to solve the recursion problem. &lt;strong&gt;Detailed:&lt;/strong&gt; &lt;code&gt;.bizz(Foo)&lt;/code&gt; is no longer stored inline in memory. Actually, with the &lt;code&gt;indirect&lt;/code&gt; modifier data is now stored behind a pointer (indirectly).&lt;/p&gt;&lt;p&gt;Problem solved! Also, we can modify the whole enum as indirect&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;// Every case is indirect now
indirect enum Foo {
    case bizz(Foo?)
    case fizz(Foo?)
}&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;&lt;h2 id=&quot;2-attribute-autoclosure&quot;&gt;2. Attribute &lt;code&gt;@autoclosure&lt;/code&gt;&lt;/h2&gt;&lt;p&gt;Swift’s &lt;code&gt;@autoclosure&lt;/code&gt; attribute enables you to define an argument that automatically gets wrapped in a closure. It’s mostly used to defer the execution of an expression to when it’s actually needed.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;func calculate(_ expression: @autoclosure () -&amp;gt; Int,
               zero: Bool) -&amp;gt; Int {
    guard !zero else {
        return 0
    }

    return expression()
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Then, calculate can be called like this:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;calculate(1 + 2, zero: false) // 3

calculate([Int](repeating: 5, count: 10000000).reduce(0, +),
                zero: false) // 50000000

calculate([Int](repeating: 5, count: 1000).reduce(0, +),
                zero: true) // 0&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;So, in this case, when &lt;code&gt;zero: true&lt;/code&gt;, the call of &lt;code&gt;calculate&lt;/code&gt; does not calculate the expression at all, improving code performance.&lt;/p&gt;&lt;!--kg-card-begin: html--&gt;&lt;section class=&quot;custom-replace&quot; data-replace=&quot;.subscription-article&quot;&gt;
    &lt;a href=&quot;https://alexdremov.me/#/portal/signup&quot;&gt;Subscribe and don&apos;t miss posts!&lt;/a&gt;
&lt;/section&gt;&lt;!--kg-card-end: html--&gt;&lt;hr&gt;&lt;h2 id=&quot;3-lazy&quot;&gt;3. Lazy&lt;/h2&gt;&lt;p&gt;A &lt;code&gt;lazy&lt;/code&gt; stored property is a property whose initial value isn’t calculated until the first time it’s used. Lazy properties must always be declared as a variable. Note that if you use &lt;code&gt;lazy&lt;/code&gt; in &lt;code&gt;struct&lt;/code&gt;, then the function that uses it must be marked as &lt;code&gt;mutating&lt;/code&gt;.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;class Foo {
    lazy var bonk = DBConnection()
    
    func send() {
        bonk.sendMessage()
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We already covered &lt;code&gt;@autoclosure&lt;/code&gt; which also can help to defer expression evaluation. That can be used with &lt;code&gt;lazy&lt;/code&gt;! Consider this common case of dependency injection.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;class Foo {
    let bonkProvider: () -&amp;gt; DBConnection
    lazy var bonk: DBConnection = bonkProvider()
    
    init(_ expression: @escaping @autoclosure () -&amp;gt; DBConnection) {
        self.bonkProvider = expression
    }
    
    func send() {
    	// Here bonkProvider() is called
        // only for the first call of send()
        bonk.sendMessage()
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;&lt;h2 id=&quot;4-enums-as-namespaces&quot;&gt;4. Enums as namespaces&lt;/h2&gt;&lt;p&gt;Swift does not have namespaces, which may be a problem in big projects. This is easily solved with enums.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;enum API {}

extension API {
    static let token = &quot;…&quot;

    struct CatsCounter {
        …
    }
}

let a = API.CatsCounter()
print(API.token)&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;&lt;h2 id=&quot;5-dynamic-member-lookup&quot;&gt;5. Dynamic member lookup&lt;/h2&gt;&lt;p&gt;This section describes the &lt;code&gt;@dynamicMemberLookup&lt;/code&gt; attribute. It can be used with structs and classes.&lt;/p&gt;&lt;p&gt;Just adding &lt;code&gt;@dynamicMemberLookup&lt;/code&gt; to the definition generates an error &lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-red&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;😡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;&lt;code&gt;@dynamicMemberLookup&lt;/code&gt; attribute requires &lt;code&gt;Foo&lt;/code&gt; to have a &lt;code&gt;subscript(dynamicMember:)&lt;/code&gt; method that accepts either &lt;code&gt;ExpressibleByStringLiteral&lt;/code&gt; or a &lt;code&gt;key path&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;Therefore, such subscript needs to be defined&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;@dynamicMemberLookup
class Foo {
    subscript(dynamicMember string: String) -&amp;gt; String {
        return string
    }
}

let a = Foo()
print(a.helloWorld)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In &lt;code&gt;subscript&lt;/code&gt; you can implement much more complex logic to retrieve data. But you can see how this implementation is limited to strings only and not really safe. This can be modified with a &lt;code&gt;key path&lt;/code&gt;.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;class Bob {
    let age = 22
    let name = &quot;Bob&quot;
}

@dynamicMemberLookup
class Foo {
    let himself = Bob()
    
    subscript&amp;lt;T&amp;gt;(dynamicMember keyPath: KeyPath&amp;lt;Bob, T&amp;gt;) -&amp;gt; T {
        return himself[keyPath: keyPath]
    }
}

let a = Foo()
print(a.age)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Even though you know about this feature does not mean that it should be used everywhere. It’s up to you what is more readable and expressive: &lt;code&gt;a.himself.age&lt;/code&gt; or &lt;code&gt;a.age&lt;/code&gt;.&lt;/p&gt;&lt;hr&gt;&lt;h2 id=&quot;6-dynamically-callable&quot;&gt;6. Dynamically callable&lt;/h2&gt;&lt;p&gt;Also, a compiler feature that allows you to call objects. Can be applied to &lt;code&gt;struct&lt;/code&gt;, &lt;code&gt;enum&lt;/code&gt;, and &lt;code&gt;class&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;After adding the attribute, the error is generated:&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-red&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;😡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;&lt;code&gt;@dynamicCallable&lt;/code&gt; attribute requires &lt;code&gt;RangeGenerator&lt;/code&gt; to have either a valid &lt;code&gt;dynamicallyCall(withArguments:)&lt;/code&gt; method or &lt;code&gt;dynamicallyCall(withKeywordArguments:)&lt;/code&gt; method&lt;/div&gt;&lt;/div&gt;&lt;p&gt;The method signature is similar to that of &lt;code&gt;@dynamicMemberLookup&lt;/code&gt;.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;@dynamicCallable
struct RangeGenerator {
    var range: Range&amp;lt;Int&amp;gt;
    
    func dynamicallyCall(withKeywordArguments args: KeyValuePairs&amp;lt;String, Int&amp;gt;) -&amp;gt; [Int] {
        if args.count &amp;gt; 1 || args.first?.key != &quot;count&quot; {
            fatalError(&quot;Unknown arguments \(args)&quot;)
        }
        let count = args.first!.value
        return (0..&amp;lt;count).map{ _ in Int.random(in: range) }
    }
}

let gen = RangeGenerator(range: 0..&amp;lt;100)
print(gen(count: 13))
// [2, 89, 4, 17, 65, 26, 73, 86, 93, 13, 25, 96, 96]&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;&lt;h2 id=&quot;7-inlining&quot;&gt;7. Inlining&lt;/h2&gt;&lt;p&gt;Sometimes you want to give additional information about optimisations the compiler can use. Inlining code is one of the most important optimization features. So, how to use &lt;code&gt;‌@inlinable&lt;/code&gt;, &lt;code&gt;@inline(__always)&lt;/code&gt;, &lt;code&gt;@usableFromInline&lt;/code&gt;?&lt;/p&gt;&lt;p&gt;The &lt;code&gt;@inlinable&lt;/code&gt; attribute exports the body of a function as part of a module&apos;s interface, making it available to the optimizer when referenced from other modules.&lt;/p&gt;&lt;p&gt;As a result, &lt;code&gt;@inlinable&lt;/code&gt; makes the implementation of the method public and able to be inlined into the caller. Secondly, it forces you to make everything it calls &lt;code&gt;@usableFromInline&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;&lt;code&gt;@inline(__always)&lt;/code&gt; tells the compiler to ignore inlining heuristics and always (almost) inline the function.&lt;/p&gt;&lt;p&gt;A function that is &lt;code&gt;@inline(__always)&lt;/code&gt;, but not &lt;code&gt;@inlinable&lt;/code&gt;, will not be available for inlining outside its module, because the function&apos;s code is not available.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-blue&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💥&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;&lt;code&gt;@inline(__always)&lt;/code&gt; can be beneficial for performance, but it can also have catastrophic effects on macro performance due to code size increase.&lt;/div&gt;&lt;/div&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;struct Foo {
    @inlinable
    @inline(__always)
    func simpleComputation(_ a: Int, _ b: Int) -&amp;gt; Int {
        duplicate(a) + duplicate(b)
    }
    
    @usableFromInline
    func duplicate(_ c: Int) -&amp;gt; Int {
        c * 2
    }
    
    func general() {
        print(&quot;Hello world&quot;)
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This has more effects on implementation, check the discussion on this &lt;a href=&quot;https://forums.swift.org/t/when-should-both-inlinable-and-inline-always-be-used/37375?ref=alexdremov.me&quot;&gt;forum&lt;/a&gt; if you want to understand this in-depth&lt;/p&gt;&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;&lt;ol&gt;&lt;li&gt;&lt;a href=&quot;https://www.swiftbysundell.com/articles/using-autoclosure-when-designing-swift-apis/?ref=alexdremov.me&quot;&gt;https://www.swiftbysundell.com/articles/using-autoclosure-when-designing-swift-apis/&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.swiftbysundell.com/articles/powerful-ways-to-use-swift-enums/?ref=alexdremov.me&quot;&gt;https://www.swiftbysundell.com/articles/powerful-ways-to-use-swift-enums/&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.hackingwithswift.com/articles/134/how-to-use-dynamiccallable-in-swift?ref=alexdremov.me&quot;&gt;https://www.hackingwithswift.com/articles/134/how-to-use-dynamiccallable-in-swift&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.hackingwithswift.com/example-code/language/what-are-lazy-variables?ref=alexdremov.me&quot;&gt;https://www.hackingwithswift.com/example-code/language/what-are-lazy-variables&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://forums.swift.org/t/who-benefits-from-the-indirect-keyword/20167?ref=alexdremov.me&quot;&gt;https://forums.swift.org/t/who-benefits-from-the-indirect-keyword/20167&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.tothenew.com/blog/recursive-enumerations-in-swift/?ref=alexdremov.me&quot;&gt;https://www.tothenew.com/blog/recursive-enumerations-in-swift/&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.avanderlee.com/swift/dynamic-member-lookup/?ref=alexdremov.me&quot;&gt;https://www.avanderlee.com/swift/dynamic-member-lookup/&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;</content:encoded></item><item><title>Note-taking apps</title><link>https://alexdremov.me/note-taking-apps/</link><guid isPermaLink="true">https://alexdremov.me/note-taking-apps/</guid><description>Here I cover note-taking apps for productivuty and creating your own knowledge database</description><pubDate>Wed, 18 Aug 2021 16:51:00 GMT</pubDate><content:encoded>&lt;p&gt;You know that feeling in the start of a college or a school year? You say to yourself “I’ll be as productive as possible” and you feel like you can climb a mountain.&lt;/p&gt;&lt;p&gt;At least, this was my case. The first thing that I wanted to determine is a note-taking app. I wanted to have a written outline of every lecture organized in the best possible manner. So, I started to search for beautiful, powerful, and optimized for my developer-oriented mind apps for my college workflow.&lt;/p&gt;&lt;h2 id=&quot;evernote-apple-notes-onenote-joplin-notable&quot;&gt;Evernote, Apple Notes, OneNote, Joplin, Notable&lt;/h2&gt;&lt;p&gt;… and many other conventional note-taking apps. The biggest no-no for me was the inability to organize content efficiently. The best option was to work with folders and tags, but it gets messy really quick. Who uses tags? That is due to the fact that these apps were not developed to design some kind of knowledge database but rather for quick note-taking. Also, they don’t suffice my developer needs for code embeddings and markdown. Speaking about OneNote, it’s just ugly and over-complicated.&lt;/p&gt;&lt;h3 id=&quot;links&quot;&gt;Links&lt;/h3&gt;&lt;ol&gt;&lt;li&gt;&lt;a href=&quot;https://evernote.com/?ref=alexdremov.me&quot; rel=&quot;noreferrer noopener&quot;&gt;Evernote&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.microsoft.com/en-us/microsoft-365/onenote/digital-note-taking-app?ref=alexdremov.me&quot; rel=&quot;noreferrer noopener&quot;&gt;OneNote&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/laurent22/joplin?ref=alexdremov.me&quot; rel=&quot;noreferrer noopener&quot;&gt;Joplin&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/notable/notable?ref=alexdremov.me&quot; rel=&quot;noreferrer noopener&quot;&gt;Notable&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;&lt;h2 id=&quot;notion-boost-note&quot;&gt;Notion, Boost Note&lt;/h2&gt;&lt;p&gt;These are good! Even though they have hierarchical structuring, it’s supplemented with emoji icons and title pages. These additions help to navigate through data quicker. Notion’s workspaces and page linking helps to structure data efficiently. So, what’s wrong? Online service only. These apps are web-based apps and having a lagging app on some kind of fast-going lecture is not what I am looking for.&lt;/p&gt;&lt;h3 id=&quot;links-1&quot;&gt;Links&lt;/h3&gt;&lt;ol&gt;&lt;li&gt;&lt;a href=&quot;https://www.notion.so/?ref=alexdremov.me&quot; rel=&quot;noreferrer noopener&quot;&gt;Notion&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://boostnote.io/?ref=alexdremov.me&quot; rel=&quot;noreferrer noopener&quot;&gt;Boost Note&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;&lt;h2 id=&quot;ia-writer&quot;&gt;IA Writer&lt;/h2&gt;&lt;p&gt;My all-time best app for writing. Minimalistic tool with markdown support. Simply said, best for writing. However, not really suitable for structuring data and poor on linking, image embedding, and code highlighting.&lt;/p&gt;&lt;h3 id=&quot;links-2&quot;&gt;Links&lt;/h3&gt;&lt;ol&gt;&lt;li&gt;&lt;a href=&quot;https://ia.net/writer?ref=alexdremov.me&quot; rel=&quot;noreferrer noopener&quot;&gt;I&lt;/a&gt;&lt;a href=&quot;https://ia.net/writer?ref=alexdremov.me&quot; rel=&quot;noreferrer noopener&quot;&gt;A&lt;/a&gt;&lt;a href=&quot;https://ia.net/writer?ref=alexdremov.me&quot; rel=&quot;noreferrer noopener&quot;&gt; Writer&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;&lt;h2 id=&quot;logseq&quot;&gt;Logseq&lt;/h2&gt;&lt;p&gt;Weird at the first glance, genius if you dive deeply. Graph-based organization system bemuses at first. “What do you mean there is no folders?” But then you realize that folders or hierarchical structuring is logical but not natural. When you write some content, new concepts flow not in hierarchical order but rather like connections or links. In Logseq, pages are created as they are needed. The whole workspace is graph-organized. Moreover, it supports lots of block types and this satisfies my developer’s needs with overhead. Thus, I started considering this app as a primary one for use.&lt;/p&gt;&lt;p&gt;UPD: after almost a year use of logseq, my knowledge base looks like that&lt;/p&gt;&lt;figure class=&quot;kg-card kg-image-card&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2022/10/Screenshot-2022-10-22-at-15.12.16.png&quot; class=&quot;kg-image&quot; alt loading=&quot;lazy&quot; width=&quot;2000&quot; height=&quot;1540&quot; srcset=&quot;https://alexdremov.me/content/images/size/w600/2022/10/Screenshot-2022-10-22-at-15.12.16.png 600w, https://alexdremov.me/content/images/size/w1000/2022/10/Screenshot-2022-10-22-at-15.12.16.png 1000w, https://alexdremov.me/content/images/size/w1600/2022/10/Screenshot-2022-10-22-at-15.12.16.png 1600w, https://alexdremov.me/content/images/2022/10/Screenshot-2022-10-22-at-15.12.16.png 2000w&quot; sizes=&quot;(min-width: 720px) 720px&quot;&gt;&lt;/figure&gt;&lt;p&gt;And it’s cool, but I have to say that this graph view is really of low use, unfortunately. Or I just have not used the app extensively enough.&lt;/p&gt;&lt;h3 id=&quot;links-3&quot;&gt;Links&lt;/h3&gt;&lt;ol&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/logseq/logseq?ref=alexdremov.me&quot; rel=&quot;noreferrer noopener&quot;&gt;Logseq&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;&lt;h2 id=&quot;athens&quot;&gt;Athens&lt;/h2&gt;&lt;p&gt;Looks like Logseq and has a very similar functionality. However, I found Athens more pleasant-looking and less complicated. Here it is. Minimalistic app with beautiful design and striking structuring system. This is my top-1 of all note-taking apps that I was reviewing for a couple of days.&lt;/p&gt;&lt;p&gt;However, the project is brand new and has some bugs, so maybe I will be using Logseq for reliablity.&lt;/p&gt;&lt;h3 id=&quot;links-4&quot;&gt;Links&lt;/h3&gt;&lt;ol&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/athensresearch/athens?ref=alexdremov.me&quot; rel=&quot;noreferrer noopener&quot;&gt;Athens&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;</content:encoded></item><item><title>The Mystery of Mach-O Object Structure</title><link>https://alexdremov.me/mystery-of-mach-o-object-file-builders/</link><guid isPermaLink="true">https://alexdremov.me/mystery-of-mach-o-object-file-builders/</guid><description>I’m going to tell you about the internals of the Mach-O file and give an introduction to the simple relocatable object file structure</description><pubDate>Thu, 29 Apr 2021 19:59:09 GMT</pubDate><content:encoded>&lt;p&gt;During the development of the final project for “the assembly language and low-level architecture” MIPT freshman course, we were developing a compilable programming language. I wanted to make it compilable to the standard object file but encountered the mystery of almost no information about its structure. What’s more important, there were little to no examples on this topic. In this article, I’m going to tell you about the internals of the Mach-O file and give an introduction to the simple relocatable object file structure.&lt;/p&gt;&lt;h2 id=&quot;general-structure&quot;&gt;General Structure&lt;/h2&gt;&lt;p&gt;Mach-O file can be divided into three main parts:&lt;/p&gt;&lt;figure class=&quot;kg-card kg-image-card&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2022/10/6XLCD.gif&quot; class=&quot;kg-image&quot; alt=&quot;image of the structure&quot; loading=&quot;lazy&quot; width=&quot;261&quot; height=&quot;285&quot;&gt;&lt;/figure&gt;&lt;ul&gt;&lt;li&gt;Header&lt;/li&gt;&lt;li&gt;Load commands&lt;/li&gt;&lt;li&gt;Data&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The&lt;strong&gt; header&lt;/strong&gt; contains general information and identifies the file as a Mach-O file. The header also contains other basic file type information, indicates the target architecture, and contains flags specifying options that affect the interpretation of the rest of the file.&lt;/p&gt;&lt;p&gt;Directly after the header is series of variable-size &lt;strong&gt;load commands&lt;/strong&gt; that specify the layout and linkage characteristics of the file. This is the core that defines the file characteristics.&lt;/p&gt;&lt;p&gt;Following the load commands, all Mach-O files contain &lt;strong&gt;segment data&lt;/strong&gt;. Each segment has zero or more sections. Each segment defines a region of virtual memory that the dynamic linker maps into the address space of the process. Apart from segment data, other data also can be placed here. For example, symbol table, relocations, etc.&lt;/p&gt;&lt;h2 id=&quot;object-specific-structure&quot;&gt;Object-specific structure&lt;/h2&gt;&lt;p&gt;As this article focuses on object files, I will not go into details about general executable files. Even though their format is the same, load commands and data differ.&lt;/p&gt;&lt;p&gt;To make a workable object file, we need to define these elements. I ordered them in the order they will be placed in the file.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Header&lt;/li&gt;&lt;li&gt;Load commands&lt;/li&gt;&lt;li&gt;Segment (__TEXT)&lt;/li&gt;&lt;li&gt;Text section (__text)&lt;/li&gt;&lt;li&gt;Data section (__data)&lt;/li&gt;&lt;li&gt;Symbols table (SYMTAB)&lt;/li&gt;&lt;li&gt;Dynamic symbols table (DYSYMTAB)&lt;/li&gt;&lt;li&gt;Data&lt;/li&gt;&lt;li&gt;Text section data&lt;/li&gt;&lt;li&gt;Data section data&lt;/li&gt;&lt;li&gt;Relocations&lt;/li&gt;&lt;li&gt;Symbol table data&lt;/li&gt;&lt;li&gt;String table&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;header&quot;&gt;Header&lt;/h2&gt;&lt;p&gt;Header is defined by this structure:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;struct mach_header_64 {
    uint32_t       magic;      /* mach magic number identifier */
    cpu_type_t     cputype;    /* cpu specifier */
    cpu_subtype_t  cpusubtype; /* machine specifier */
    uint32_t       filetype;   /* type of file */
    uint32_t       ncmds;      /* number of load commands */
    uint32_t       sizeofcmds; /* the size of all the load commands */
    uint32_t       flags;      /* flags */
    uint32_t       reserved;   /* reserved */
};&lt;/code&gt;&lt;/pre&gt;&lt;ol&gt;&lt;li&gt;&lt;strong&gt;magic&lt;/strong&gt; – it’s exactly what the name says. It simply contains the magic number that helps to identify the file as Mach-O. It holds &lt;code&gt;MH_MAGIC_64&lt;/code&gt; (0xfeedfacf) constant.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;cputype,&lt;/strong&gt; &lt;strong&gt;cpusubtype&lt;/strong&gt; – defines CPU information. For most cases, &lt;code&gt;CPU_TYPE_X86_64&lt;/code&gt; and &lt;code&gt;CPU_SUBTYPE_X86_64_ALL&lt;/code&gt; can be used.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;filetype&lt;/strong&gt; – as Mach-O file can be used for multiple purposes, it is needed to know the file type. As we build an object file, &lt;code&gt;MH_OBJECT&lt;/code&gt; must be used.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;ncmds&lt;/strong&gt; – number of load commands followed by the header.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;sizeofcmds&lt;/strong&gt; – the size of load commands (in bytes).&lt;/li&gt;&lt;li&gt;&lt;strong&gt;flags&lt;/strong&gt; – special flags, can be found &lt;a href=&quot;https://github.com/aidansteele/osx-abi-macho-file-format-reference?ref=alexdremov.me#mach_header&quot; rel=&quot;noreferrer noopener&quot;&gt;here&lt;/a&gt;. For the object file, we will be using &lt;code&gt;MH_SUBSECTIONS_VIA_SYMBOLS&lt;/code&gt; which means that the sections of the object file can be divided into individual blocks. These blocks are dead-stripped if they are not used by other codes.&lt;/li&gt;&lt;/ol&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;MH_NOUNDEFS&lt;/code&gt; — The object file contained no undefined references when it was built.&lt;/li&gt;&lt;li&gt;&lt;code&gt;MH_INCRLINK&lt;/code&gt; — The object file is the output of an incremental link against a base file and cannot be linked again.&lt;/li&gt;&lt;li&gt;&lt;code&gt;MH_DYLDLINK&lt;/code&gt; — The file is input for the dynamic linker and cannot be statically linked again.&lt;/li&gt;&lt;li&gt;&lt;code&gt;MH_TWOLEVEL&lt;/code&gt; — The image is using two-level namespace bindings.&lt;/li&gt;&lt;li&gt;&lt;code&gt;MH_BINDATLOAD&lt;/code&gt; — The dynamic linker should bind the undefined references when the file is loaded.&lt;/li&gt;&lt;li&gt;&lt;code&gt;MH_PREBOUND&lt;/code&gt; — The file’s undefined references are prebound.&lt;/li&gt;&lt;li&gt;&lt;code&gt;MH_PREBINDABLE&lt;/code&gt; — This file is not prebound but can have its prebinding redone. Used only when &lt;code&gt;MH_PREBEOUND&lt;/code&gt; is not set.&lt;/li&gt;&lt;li&gt;&lt;code&gt;MH_NOFIXPREBINDING&lt;/code&gt; — The dynamic linker doesn’t notify the prebinding agent about this executable.&lt;/li&gt;&lt;li&gt;&lt;code&gt;MH_ALLMODSBOUND&lt;/code&gt; — Indicates that this binary binds to all two-level namespace modules of its dependent libraries. Used only when &lt;code&gt;MH_PREBINDABLE&lt;/code&gt; and &lt;code&gt;MH_TWOLEVEL&lt;/code&gt; are set.&lt;/li&gt;&lt;li&gt;&lt;code&gt;MH_CANONICAL&lt;/code&gt; — This file has been canonicalized by unprebinding—clearing prebinding information from the file. See the &lt;code&gt;redo_prebinding&lt;/code&gt; man page for details.&lt;/li&gt;&lt;li&gt;&lt;code&gt;MH_SPLIT_SEGS&lt;/code&gt; — The file has its read-only and read-write segments split.&lt;/li&gt;&lt;li&gt;&lt;code&gt;MH_FORCE_FLAT&lt;/code&gt; — The executable is forcing all images to use flat namespace bindings.&lt;/li&gt;&lt;li&gt;&lt;code&gt;MH_SUBSECTIONS_VIA_SYMBOLS&lt;/code&gt; — The sections of the object file can be divided into individual blocks. These blocks are dead-stripped if they are not used by other codes. See “Linking” for details.&lt;/li&gt;&lt;li&gt;&lt;code&gt;MH_NOMULTIDEFS&lt;/code&gt; — This umbrella guarantees there are no multiple definitions of symbols in its subimages. As a result, the two-level namespace hints can always be used.&lt;/li&gt;&lt;/ul&gt;&lt;ol&gt;&lt;li&gt;&lt;strong&gt;reserved&lt;/strong&gt; – reserved bytes, not used.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Summing up, here is the code for initializing header for object file.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;“To be modified”&lt;/strong&gt; means that it is not possible to determine the value before constructing the file. Therefore, it will be changed afterwards.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;mach_header_64 header = {};
header.magic          = MH_MAGIC_64;
header.cputype        = CPU_TYPE_X86_64;
header.cpusubtype     = CPU_SUBTYPE_X86_64_ALL;
header.filetype       = MH_OBJECT;
header.ncmds          = 0; /* to be modified */
header.sizeofcmds     = 0; /* to be modified */
header.flags          = MH_SUBSECTIONS_VIA_SYMBOLS;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;load-commands&quot;&gt;Load commands&lt;/h2&gt;&lt;p&gt;The load command structures are located directly after the header of the object file, and they specify both the logical structure of the file and the layout of the file in virtual memory.&lt;/p&gt;&lt;p&gt;For an object file, several load commands are needed: segment section, symtab, dysymtab. Every load command has two the same fields in the beginning: &lt;code&gt;uint32_t cmd&lt;/code&gt; and &lt;code&gt;uint32_t cmdsize&lt;/code&gt;, but the following content differs.&lt;/p&gt;&lt;h3 id=&quot;segment_command_64&quot;&gt;segment_command_64&lt;/h3&gt;&lt;p&gt;Specifies the range of bytes in a 64-bit Mach-O file that make up a segment. Those bytes are mapped by the loader into the address space of a program. Segment structure is:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;struct segment_command_64 {  /* for 64-bit architectures */
   uint32_t   cmd;           /* LC_SEGMENT_64 */
   uint32_t   cmdsize;       /* includes sizeof section_64 structs */
   char       segname[16];   /* segment name */
   uint64_t   vmaddr;        /* memory address of this segment */
   uint64_t   vmsize;        /* memory size of this segment */
   uint64_t   fileoff;       /* file offset of this segment */
   uint64_t   filesize;      /* amount to map from the file */
   vm_prot_t  maxprot;       /* maximum VM protection */
   vm_prot_t  initprot;      /* initial VM protection */
   uint32_t   nsects;        /* number of sections in segment */
   uint32_t   flags;         /* flags */
};&lt;/code&gt;&lt;/pre&gt;&lt;ol&gt;&lt;li&gt;&lt;strong&gt;segname&lt;/strong&gt; – the name of the segment. There are no requirements, but it is common to start the name with a double underline (__) and use uppercase. For example, &lt;code&gt;SEG_TEXT&lt;/code&gt; (“__TEXT”), &lt;code&gt;SEG_DATA&lt;/code&gt; (“__DATA”).&lt;/li&gt;&lt;li&gt;&lt;strong&gt;vmaddr&lt;/strong&gt; – the start of this segment in virtual memory.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;vmsize&lt;/strong&gt; – the size of this segment in memory. For executables, this value must be divisible by page. In object files, this is not needed as this requirement is fulfilled on the linking stage.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;fileoff&lt;/strong&gt; – offset of this segment in the file. This offset points to some areas after load commands. The image below helps&lt;/li&gt;&lt;li&gt;&lt;strong&gt;filesize&lt;/strong&gt; – the amount of file from &lt;strong&gt;fileoff&lt;/strong&gt; to be mapped.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;maxprot&lt;/strong&gt; – maximum virtual memory protection. For TEXT segment, usually, &lt;code&gt;VM_PROT_READ | VM_PROT_EXECUTE | VM_PROT_WRITE&lt;/code&gt; .&lt;/li&gt;&lt;li&gt;&lt;strong&gt;initprot&lt;/strong&gt; – memory protection during initialization.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;nsect&lt;/strong&gt; – number of sections directly followed by this segment.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;flags&lt;/strong&gt; – can be found &lt;a href=&quot;https://github.com/aidansteele/osx-abi-macho-file-format-reference?ref=alexdremov.me#mach_header&quot; rel=&quot;noreferrer noopener&quot;&gt;here&lt;/a&gt;. For the object file, no flags are needed.&lt;/li&gt;&lt;/ol&gt;&lt;figure class=&quot;kg-card kg-image-card&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2022/10/vmmap.png&quot; class=&quot;kg-image&quot; alt loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;343&quot; srcset=&quot;https://alexdremov.me/content/images/size/w600/2022/10/vmmap.png 600w, https://alexdremov.me/content/images/size/w1000/2022/10/vmmap.png 1000w, https://alexdremov.me/content/images/2022/10/vmmap.png 1200w&quot; sizes=&quot;(min-width: 720px) 720px&quot;&gt;&lt;/figure&gt;&lt;h3 id=&quot;section_64&quot;&gt;section_64&lt;/h3&gt;&lt;p&gt;Segment load command is directly followed by sections defined in it.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;struct section_64 {          /* for 64-bit architectures */
   char       sectname[16];  /* name of this section */
   char       segname[16];   /* segment this section goes in */
   uint64_t   addr;          /* memory address of this section */
   uint64_t   size;          /* size in bytes of this section */
   uint32_t   offset;        /* file offset of this section */
   uint32_t   align;         /* section alignment (power of 2) */
   uint32_t   reloff;        /* file offset of relocation entries */
   uint32_t   nreloc;        /* number of relocation entries */
   uint32_t   flags;         /* flags (section type and attributes)*/
   uint32_t   reserved1;     /* reserved (for offset or index) */
   uint32_t   reserved2;     /* reserved (for count or sizeof) */
   uint32_t   reserved3;     /* reserved */
};&lt;/code&gt;&lt;/pre&gt;&lt;ol&gt;&lt;li&gt;&lt;strong&gt;sectname&lt;/strong&gt; – the name of the section. There are no requirements, but it is common to start the name with a double underline (__) and use lowercase. For example, &lt;code&gt;SECT_TEXT&lt;/code&gt; (“__text”), &lt;code&gt;SECT_DATA&lt;/code&gt; (“__data”).&lt;/li&gt;&lt;li&gt;&lt;strong&gt;segname&lt;/strong&gt; – the name of the segment this section goes in.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;addr&lt;/strong&gt; – memory address of this section. For example, if segment vaddress is 0x10000, then first section address is also 0x10000.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;size&lt;/strong&gt; – the size in bytes of this section in the file.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;offset&lt;/strong&gt; – the offset of the file section from the start of the file.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;align&lt;/strong&gt; – alignment of the section as a power of 2. For example, 1 means 2 bytes alignment, 2 means 4 bytes alignment. Specifies the alignment of the section in memory.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;reloff&lt;/strong&gt; – the offset of relocations array from the file beginning.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;nreloc&lt;/strong&gt; – number of relocations.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;flags&lt;/strong&gt; – specify information about data contained in the section. For example, for code &lt;code&gt;S_REGULAR | S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SOME_INSTRUCTIONS&lt;/code&gt;. For the data section, &lt;code&gt;S_REGULAR&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;reserved1, reserved2, reserved3&lt;/strong&gt; – unused in our case.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Segment load command and sections are the most important part of the file. Object file has only one segment and one or several sections.&lt;/p&gt;&lt;p&gt;Now, we can define a segment and sections associated with it.&lt;/p&gt;&lt;p&gt;__TEXT segment – the only segment in the object file&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;segment_command_64 segment = {};
/*
 * Usually, as there is only one segment in the object file,
 * placing name is omitted. 
 * strcpy(segment.segname, SEG_TEXT);
 */
segment.cmd                = LC_SEGMENT_64;
segment.cmdsize            = sizeof(segment) + 2 * sizeof(section_64);
segment.vmaddr             = 0;
segment.vmsize             = 0; /* to be modified */
segment.fileoff            = 0; /* to be modified */
segment.filesize           = 0; /* to be modified */
segment.maxprot            = VM_PROT_READ | VM_PROT_EXECUTE;
segment.initprot           = VM_PROT_READ | VM_PROT_EXECUTE;
segment.nsects             = 2; /* code and data sections */&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;__text section&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;section_64 sectionText     = {};
strcpy(sectionText.segname,  SEG_TEXT ); /* segname  &amp;lt;- __TEXT */
strcpy(sectionText.sectname, SECT_TEXT); /* sectname &amp;lt;- __text */
sectionText.addr           = 0;
sectionText.size           = 0;          /* to be modified */
sectionText.offset         = 0;          /* to be modified */
sectionText.align          = 4;          /* 2^4 code alignment */
sectionText.reloff         = 0;          /* to be modified */
sectionText.nreloc         = 0;          /* to be modified */
sectionText.flags          = S_REGULAR |
                             S_ATTR_PURE_INSTRUCTIONS |
                             S_ATTR_SOME_INSTRUCTIONS;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;__data section&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;section_64 sectionData     = {};
strcpy(sectionData.segname,  SEG_DATA ); /* segname  &amp;lt;- __DATA */
strcpy(sectionData.sectname, SECT_DATA); /* sectname &amp;lt;- __data */
sectionData.addr           = 0;          /* = sectionText.size */
sectionData.size           = 0;          /* to be modified */
sectionData.offset         = 0;          /* = sectionText.offset */
                                         /*   + sectionText.size */
sectionData.align          = 1;          /* 2^1 code alignment */
sectionData.reloff         = 0;          /* no relocations in data section */
sectionData.nreloc         = 0;          
sectionData.flags          = S_REGULAR;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;At this point, simple object file structure is almost ready, but SYMTAB and DYSYMTAB load commands are steel needed to be defined even if there is no relocations at all.&lt;/p&gt;&lt;h2 id=&quot;symtab&quot;&gt;Symtab&lt;/h2&gt;&lt;p&gt;Describes the size and location of the symbol table data structures. Its structure is:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;struct symtab_command {
   uint32_t   cmd;       /* LC_SYMTAB */
   uint32_t   cmdsize;   /* sizeof(struct symtab_command) */
   uint32_t   symoff;    /* symbol table offset */
   uint32_t   nsyms;     /* number of symbol table entries */
   uint32_t   stroff;    /* string table offset */
   uint32_t   strsize;   /* string table size in bytes */
};&lt;/code&gt;&lt;/pre&gt;&lt;ol&gt;&lt;li&gt;&lt;strong&gt;symoff&lt;/strong&gt; – offset to the symbol table – located after load commands somewhere further in the file.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;nsyms&lt;/strong&gt; – number of symbols in symbols table.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;stroff&lt;/strong&gt; – string table offset.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;strsize&lt;/strong&gt; – the size of the string table in bytes.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;The most straightforward description so far. It is convenient to describe a symbol table and string table here.&lt;/p&gt;&lt;h3 id=&quot;string-table&quot;&gt;String table&lt;/h3&gt;&lt;p&gt;The string table is the most straightforward structure of all listed here. It is simply strings separated by zeros.&lt;/p&gt;&lt;figure class=&quot;kg-card kg-image-card&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2022/10/Screenshot-2021-04-30-at-22.38.27.png&quot; class=&quot;kg-image&quot; alt loading=&quot;lazy&quot; width=&quot;992&quot; height=&quot;288&quot; srcset=&quot;https://alexdremov.me/content/images/size/w600/2022/10/Screenshot-2021-04-30-at-22.38.27.png 600w, https://alexdremov.me/content/images/2022/10/Screenshot-2021-04-30-at-22.38.27.png 992w&quot; sizes=&quot;(min-width: 720px) 720px&quot;&gt;&lt;/figure&gt;&lt;h3 id=&quot;symbol-table&quot;&gt;Symbol table&lt;/h3&gt;&lt;p&gt;Symbol table consists of equally sized entries. They must be grouped by their type – local symbols (further grouped by the module they are from), defined external symbols (further grouped by the module they are from), and undefined symbols. The order of groups is not important.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;struct nlist_64 {
    union {
        uint32_t  n_strx;  /* index into the string table */
    } n_un;
    uint8_t n_type;        /* type flag, see below */
    uint8_t n_sect;        /* section number or NO_SECT */
    uint16_t n_desc;       /* see &amp;lt;mach-o/stab.h&amp;gt; */
    uint64_t n_value;      /* value of this symbol (or stab offset) */
};&lt;/code&gt;&lt;/pre&gt;&lt;ol&gt;&lt;li&gt;&lt;strong&gt;n_strx&lt;/strong&gt; – index of the string in the string table. For example, the index of “_print” in the string table above is 1. The index of _giveYouUp0 is 8; it is the position of the first letter from the start of the string table.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;n_type&lt;/strong&gt; – a type of symbol. Defines the meaning of the symbol. There are essential values:&lt;/li&gt;&lt;li&gt;&lt;code&gt;N_TYPE&lt;/code&gt; (0x0e) – These bits define the type of the symbol.&lt;/li&gt;&lt;li&gt;&lt;code&gt;N_UNDF&lt;/code&gt; (0x0) – The symbol is undefined. Undefined symbols are symbols referenced in this module but defined in a different module. The &lt;code&gt;n_sect&lt;/code&gt; field is set to &lt;code&gt;NO_SECT&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;&lt;code&gt;N_ABS&lt;/code&gt; (0x2) – The symbol is absolute. The linker does not change the value of an absolute symbol. The &lt;code&gt;n_sect&lt;/code&gt; field is set to &lt;code&gt;NO_SECT&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;&lt;code&gt;N_SECT&lt;/code&gt; (0xe) – The symbol is defined in the section number given in &lt;code&gt;n_sect&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;&lt;code&gt;N_PBUD&lt;/code&gt; (0xc) – The symbol is undefined and the image is using a prebound value for the symbol. The &lt;code&gt;n_sect&lt;/code&gt; field is set to &lt;code&gt;NO_SECT&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;&lt;code&gt;N_INDR&lt;/code&gt; ( 0xa) – The symbol is defined to be the same as another symbol. The &lt;code&gt;n_value&lt;/code&gt; field is an index into the string table specifying the name of the other symbol. When that symbol is linked, both this and the other symbol have the same defined type and value.&lt;/li&gt;&lt;li&gt;&lt;code&gt;N_EXT&lt;/code&gt;  (0x01) – If this bit is on, this symbol is external, a symbol that is either &lt;strong&gt;defined outside this file&lt;/strong&gt; or that is defined in this file but can be referenced by other files.&lt;/li&gt;&lt;li&gt;&lt;code&gt;N_STAB&lt;/code&gt; (0xe0) – If any of these 3 bits are set, the symbol is a symbolic debugging table (&lt;code&gt;stab&lt;/code&gt;) entry. In that case, the entire &lt;code&gt;n_type&lt;/code&gt; field is interpreted as a &lt;code&gt;stab&lt;/code&gt;value.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;n_sect&lt;/strong&gt; – an integer specifying the number of the section that this symbol can be found in, or &lt;code&gt;NO_SECT&lt;/code&gt; if the symbol is not to be found in any section.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;n_desc&lt;/strong&gt; – provides additional information about the nature of this symbol for non-stab symbols (not &lt;code&gt;N_STAB&lt;/code&gt;). The reference flags can be accessed using the &lt;code&gt;REFERENCE_TYPE&lt;/code&gt; mask (0xF). Usually, &lt;code&gt;REFERENCE_FLAG_UNDEFINED_NON_LAZY&lt;/code&gt; used for external symbols. If the symbol is defined in the section (&lt;code&gt;N_SECT&lt;/code&gt;), use &lt;code&gt;REFERENCE_FLAG_DEFINED&lt;/code&gt; + &lt;code&gt;N_EXT&lt;/code&gt; if you want to make it available from other files or &lt;code&gt;REFERENCE_FLAG_PRIVATE_DEFINED&lt;/code&gt; without specifying &lt;code&gt;N_EXT&lt;/code&gt; if not. The most used values are:&lt;/li&gt;&lt;li&gt;&lt;code&gt;REFERENCE_FLAG_UNDEFINED_NON_LAZY&lt;/code&gt; (0x0)—This symbol is a reference to an external non-lazy (data) symbol.&lt;/li&gt;&lt;li&gt;&lt;code&gt;REFERENCE_FLAG_UNDEFINED_LAZY&lt;/code&gt; (0x1)—This symbol is a reference to an external lazy symbol—that is, to a function call.&lt;/li&gt;&lt;li&gt;&lt;code&gt;REFERENCE_FLAG_DEFINED&lt;/code&gt; (0x2)—This symbol is defined in this module.&lt;/li&gt;&lt;li&gt;&lt;code&gt;REFERENCE_FLAG_PRIVATE_DEFINED&lt;/code&gt; (0x3)—This symbol is defined in this module and is visible only to modules within this shared library.&lt;/li&gt;&lt;li&gt;&lt;code&gt;REFERENCE_FLAG_PRIVATE_UNDEFINED_NON_LAZY&lt;/code&gt; (0x4)—This symbol is defined in another module in this file, is a non-lazy (data) symbol, and is visible only to modules within this shared library.&lt;/li&gt;&lt;li&gt;&lt;code&gt;REFERENCE_FLAG_PRIVATE_UNDEFINED_LAZY&lt;/code&gt; (0x5)—This symbol is defined in another module in this file, is a lazy (function) symbol, and is visible only to modules within this shared library.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;n_value&lt;/strong&gt; – information about this symbol. The format of this value is different for each type of symbol table entry (as specified by the &lt;code&gt;n_type&lt;/code&gt; field). For the &lt;code&gt;N_SECT&lt;/code&gt; symbol type, &lt;code&gt;n_value&lt;/code&gt; is the address of the symbol – offset from the start of the &lt;strong&gt;segment&lt;/strong&gt;. For &lt;code&gt;N_UNDF | N_EXT&lt;/code&gt; it is not used.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;This structure is one of the hardest to understand and use. Therefore, there are examples. Notice that symbols are grouped. It will be used later in DYSYMTAB.&lt;/p&gt;&lt;figure class=&quot;kg-card kg-image-card&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2022/10/Screenshot-2021-05-01-at-01.58.43.png&quot; class=&quot;kg-image&quot; alt loading=&quot;lazy&quot; width=&quot;1426&quot; height=&quot;1372&quot; srcset=&quot;https://alexdremov.me/content/images/size/w600/2022/10/Screenshot-2021-05-01-at-01.58.43.png 600w, https://alexdremov.me/content/images/size/w1000/2022/10/Screenshot-2021-05-01-at-01.58.43.png 1000w, https://alexdremov.me/content/images/2022/10/Screenshot-2021-05-01-at-01.58.43.png 1426w&quot; sizes=&quot;(min-width: 720px) 720px&quot;&gt;&lt;/figure&gt;&lt;p&gt;On the image above, there are four symbols in total. Two of them are locally defined, two of them undefined in the current file. There are descriptions of two of these symbols:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;#0th symbol&lt;/li&gt;&lt;/ul&gt;&lt;ol&gt;&lt;li&gt;&lt;strong&gt;n_strx&lt;/strong&gt; = 34 – index of naming’s first symbol in the string table.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;n_type&lt;/strong&gt; = &lt;code&gt;N_SECT | N_EXT&lt;/code&gt; – symbol defined in some section of the current file and available externally.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;n_sect&lt;/strong&gt; = 1 – symbol defined in the first (counting from 1) section.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;n_desc&lt;/strong&gt; = &lt;code&gt;REFERENCE_FLAG_DEFINED&lt;/code&gt; – symbol defined in the file. This information is redundant as it is already known from &lt;code&gt;N_SECT&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;value&lt;/strong&gt; = 0 – symbol definition locates at the very beginning of the segment (zero offset).&lt;/li&gt;&lt;/ol&gt;&lt;ul&gt;&lt;li&gt;#2nd symbol&lt;/li&gt;&lt;/ul&gt;&lt;ol&gt;&lt;li&gt;&lt;strong&gt;n_strx&lt;/strong&gt; = 1 – index of naming’s first symbol in the string table.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;n_type&lt;/strong&gt; = &lt;code&gt;N_UNDF | N_EXT&lt;/code&gt; – symbol is not defined in the current file, must be defined externally.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;n_sect&lt;/strong&gt; = &lt;code&gt;NO_SECT&lt;/code&gt; – no associated section.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;n_desc&lt;/strong&gt; = &lt;code&gt;REFERENCE_FLAG_UNDEFINED_NON_LAZY &lt;/code&gt;– this symbol is a reference to an external non-lazy (data) symbol.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;value&lt;/strong&gt; = 0 – unused.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;These two symbols can be constructed like this:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;nlist_64 symbols[2] = {
    {34, N_SECT  | N_EXT, 1      , REFERENCE_FLAG_DEFINED           , 0},
    {1 , N_UNDF | N_EXT, NO_SECT, REFERENCE_FLAG_UNDEFINED_NON_LAZY, 0}
};&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;dysymtab&quot;&gt;Dysymtab&lt;/h2&gt;&lt;p&gt;It describes the sizes and locations of the parts of the symbol table used for dynamic linking. As I already noticed, symtab entries must be grouped by their type. Here, this requirment is used.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;struct dysymtab_command {
    uint32_t cmd;            /* LC_DYSYMTAB */
    uint32_t cmdsize;        /* sizeof(struct dysymtab_command) */
    uint32_t ilocalsym;      /* index to local symbols */
    uint32_t nlocalsym;      /* number of local symbols */

    uint32_t iextdefsym;     /* index to externally defined symbols */
    uint32_t nextdefsym;     /* number of externally defined symbols */

    uint32_t iundefsym;      /* index to undefined symbols */
    uint32_t nundefsym;      /* number of undefined symbols */

    uint32_t tocoff;         /* file offset to table of contents */
    uint32_t ntoc;           /* number of entries in table of contents */

    uint32_t modtaboff;      /* file offset to module table */
    uint32_t nmodtab;        /* number of module table entries */


    uint32_t extrefsymoff;   /* offset to referenced symbol table */
    uint32_t nextrefsyms;    /* number of referenced symbol table entries */


    uint32_t indirectsymoff; /* file offset to the indirect symbol table */
    uint32_t nindirectsyms;  /* number of indirect symbol table entries */


    uint32_t extreloff;      /* offset to external relocation entries */
    uint32_t nextrel;        /* number of external relocation entries */

    uint32_t locreloff;      /* offset to local relocation entries */
    uint32_t nlocrel;        /* number of local relocation entries */

}; &lt;/code&gt;&lt;/pre&gt;&lt;p&gt;There are a lot of fields, but only several of them are needed for object files.&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;strong&gt;ilocalsym + nlocalsym&lt;/strong&gt; – local symbols are used only for debugging.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;iextdefsym + nextdefsym&lt;/strong&gt; – external symbols.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;iundefsym + nundefsym&lt;/strong&gt; – undefined symbols.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Fields with i* prefix indicate index of the first entry in the symbol table, while n* holds the number of such symbols.&lt;/p&gt;&lt;figure class=&quot;kg-card kg-image-card&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2022/10/board.png&quot; class=&quot;kg-image&quot; alt loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;800&quot; srcset=&quot;https://alexdremov.me/content/images/size/w600/2022/10/board.png 600w, https://alexdremov.me/content/images/size/w1000/2022/10/board.png 1000w, https://alexdremov.me/content/images/2022/10/board.png 1280w&quot; sizes=&quot;(min-width: 720px) 720px&quot;&gt;&lt;/figure&gt;&lt;h2 id=&quot;relocations&quot;&gt;Relocations&lt;/h2&gt;&lt;p&gt;Finally, all this structures were needed just to be able to do relocations. But why we even need them? Consider this assembly code:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;call     ...   ; call function – external or internal
mov      rax, [rip + ...] ; load global variable&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In both of these cases address or offset is not known until the linking stage as segments will be rearranged, combined, and placed back in some order. Linker will substitute address or offset by the relevant one. Relocations information specifies where address must be changed, how it must be changed and for what symbol.&lt;/p&gt;&lt;p&gt;Relocations entry is defined as:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;struct relocation_info {
   int32_t  r_address;        /* offset in the section to */
                              /* what is being relocated */
   uint32_t r_symbolnum:24,   /* symbol index if r_extern == 1 or
                              /* section ordinal if r_extern == 0 */
            r_pcrel:1,        /* was relocated pc relative already */
            r_length:2,       /* 0=byte, 1=word, 2=long, 3=quad */
            r_extern:1,       /* does not include value of sym referenced */
            r_type:4;         /* if not 0, machine specific relocation type */
};&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Do you remember that each section may have relocations and they are specified in corresponding field of section dtructure? Here are relocations themselves.&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;strong&gt;r_address&lt;/strong&gt; – offset of value that is needed to be relocated from the start of the section.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;r_symbolnum&lt;/strong&gt; – as symbol index in symbol table if r_extern == 1 or section ordinal (number) if r_extern == 0.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;r_pcrel&lt;/strong&gt; – (1/0) Indicates whether the item containing the address to be relocated is part of a CPU instruction that uses PC-relative addressing. For addresses contained in PC-relative instructions, the CPU adds the address of the instruction to the address contained in the instruction.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;r_length&lt;/strong&gt; – Indicates the length of item containing the address to be relocated. A value of zero indicates a single byte; a value of 1 indicates a 2-byte address, and a value of 2 indicates a 4-byte address.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;r_extern&lt;/strong&gt; – (1/0) Indicates whether the r_symbolnum field is an index into the symbol table (1) or a section number (zero).&lt;/li&gt;&lt;li&gt;&lt;strong&gt;r_type&lt;/strong&gt; – Indicates the type of relocation to be performed. Possible values for this field are shared between this structure and the &lt;code&gt;&lt;a href=&quot;http://mirror.informatimago.com/next/developer.apple.com/documentation/DeveloperTools/Conceptual/MachORuntime/8rt_file_format/chapter_10_section_31.html?ref=alexdremov.me#//apple_ref/doc/uid/20001298/scattered_relocation_entry&quot;&gt;scattered_relocation_info&lt;/a&gt;&lt;/code&gt; data structure; see the description of the r_type field in the &lt;code&gt;&lt;a href=&quot;http://mirror.informatimago.com/next/developer.apple.com/documentation/DeveloperTools/Conceptual/MachORuntime/8rt_file_format/chapter_10_section_31.html?ref=alexdremov.me#//apple_ref/doc/uid/20001298/scattered_relocation_entry&quot;&gt;scattered_relocation_info&lt;/a&gt;&lt;/code&gt; data structure for more details. There are two most used values:&lt;/li&gt;&lt;li&gt;&lt;code&gt;GENERIC_RELOC_SECTDIFF&lt;/code&gt; – used for relative call addresses.&lt;/li&gt;&lt;li&gt;&lt;code&gt;GENERIC_RELOC_PAIR&lt;/code&gt; – used for global variable rip relative offset.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Here’s an example of common relocation:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;relocation_info relocation = {};
relocation.r_address = ...           /* some offset to the beginning */
                                     /* of relocatable address */
relocation.r_symbolnum = 0;          /* first symbol in symtab */
relocation.r_pcrel = 1;              /* let it be call instruction that */
                                     /* is PC-relative */
relocation.r_length = 2;             /* 4-bytes address */
relocation.r_extern = 1;             /* external symbol */
relocation.r_type   = GENERIC_RELOC_SECTDIFF;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;cumulative-example&quot;&gt;Cumulative example&lt;/h2&gt;&lt;p&gt;Here, I provide a code of constructing complete Mach-O object file with call to external function and call to internal function.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;mach_header_64 header = {};
header.magic          = MH_MAGIC_64;
header.cputype        = CPU_TYPE_X86_64;
header.cpusubtype     = CPU_SUBTYPE_X86_64_ALL;
header.filetype       = MH_OBJECT;
header.ncmds          = 0; /* to be modified */
header.sizeofcmds     = 0; /* to be modified */
header.flags          = MH_SUBSECTIONS_VIA_SYMBOLS;

segment_command_64 segment = {};
segment.cmd                = LC_SEGMENT_64;
segment.cmdsize            = sizeof(segment) + sizeof(section_64);
segment.vmaddr             = 0;
segment.vmsize             = 0; /* to be modified */
segment.fileoff            = 0; /* to be modified */
segment.filesize           = 0; /* to be modified */
segment.maxprot            = VM_PROT_READ | VM_PROT_EXECUTE;
segment.initprot           = VM_PROT_READ | VM_PROT_EXECUTE;
segment.nsects             = 0; /* to be modified */

section_64 sectionText     = {};
strcpy(sectionText.segname,  SEG_TEXT ); /* segname  &amp;lt;- __TEXT */
strcpy(sectionText.sectname, SECT_TEXT); /* sectname &amp;lt;- __text */
sectionText.addr           = 0;
sectionText.size           = 0;          /* to be modified */
sectionText.offset         = 0;          /* to be modified */
sectionText.align          = 4;          /* 2^4 code alignment */
sectionText.reloff         = 0;          /* to be modified */
sectionText.nreloc         = 0;          /* to be modified */
sectionText.flags          = S_REGULAR |
                             S_ATTR_PURE_INSTRUCTIONS |
                             S_ATTR_SOME_INSTRUCTIONS;

const unsigned char code[] = {
        0xE8, 0x00, 0x00, 0x00, 0x00,      // call &amp;lt;address&amp;gt; - someFuncExternal
        0xE8, 0x00, 0x00, 0x00, 0x00,      // call &amp;lt;address&amp;gt; - someFunc
        0xB8, 0x01, 0x00, 0x00, 0x02,      // mov     rax, 0x2000001 ; exit
        0xBF, 0x00, 0x00, 0x00, 0x00,      // mov     rdi, 0
        0x0F, 0x05,                        // syscall
        // someFunc:
        0x48, 0x31, 0xC0,                  // xor rax, rax
        0xC3                               // ret
};

symtab_command symtabCommand    = {};
symtabCommand.cmd               = LC_SYMTAB;
symtabCommand.cmdsize           = sizeof(symtab_command);
symtabCommand.symoff            = 0;       /* to be modified */
symtabCommand.nsyms             = 0;       /* to be modified */
symtabCommand.stroff            = 0;       /* to be modified */
symtabCommand.strsize           = 0;       /* to be modified */

const char stringTable[]        = &quot;\0_someFunc0\0_someFuncExternal0\0&quot;;

nlist_64 symbols[2] = {
        {
            1,                      // first index in string table
            N_SECT | N_EXT,         // defined in the file, available externally
            1,                      // first section
            REFERENCE_FLAG_DEFINED, // defined in the file
            4 * 5 + 2               // offset of this symbol in the section
        },
        {
            12,                      // second string in string table
            N_UNDF  | N_EXT,         // undefined in the file,
                                     // must be defined externally
            NO_SECT,                 // no section specified
            REFERENCE_FLAG_UNDEFINED_NON_LAZY, // external non-lazy symbol
            0                        // unused
        }
};

dysymtab_command dysymtabCommand      = {};
dysymtabCommand.cmd                   = LC_DYSYMTAB;
dysymtabCommand.cmdsize               = sizeof(dysymtabCommand);
dysymtabCommand.ilocalsym             = 0; // first symbol in symbol table
dysymtabCommand.nlocalsym             = 1; // only one locally defined symbol
dysymtabCommand.iextdefsym            = 1; // second symbol in symbol table
dysymtabCommand.nextdefsym            = 1; // only one externally defined symbol

relocation_info relocations[] = {
        {
            1,      // after first byte address to someFuncExternal
            1,      // second symbol
            1,      // relative call, PC counted
            2,      // 4 bytes
            1,      // external
            GENERIC_RELOC_SECTDIFF
        },
        {
            6,      // second call address
            0,      // first symbol
            1,      // relative call, PC counted
            2,      // 4 bytes
            1,      // external
            GENERIC_RELOC_SECTDIFF
        },
};

size_t offsetCounter = 0;
FILE* binary = fopen(&quot;object.o&quot;, &quot;wb&quot;);

// Write header;
header.ncmds = 3; // segment + symtab + dysymtab
header.sizeofcmds = sizeof(segment) + sizeof(sectionText) + sizeof(symtabCommand) + sizeof(dysymtabCommand);
fwrite(&amp;amp;header, 1, sizeof(header), binary);
offsetCounter += sizeof(header);

// Write segment
segment.vmsize  = segment.filesize = sizeof(code);
segment.fileoff = header.sizeofcmds + sizeof(header); // we&apos;ll place code just after all load commands.
segment.nsects  = 1;
fwrite(&amp;amp;segment, 1, sizeof(segment), binary);
offsetCounter += sizeof(segment);

// Write section
sectionText.size   = segment.filesize;
sectionText.offset = segment.fileoff;
sectionText.reloff = segment.fileoff + segment.filesize; // just after the code
sectionText.nreloc = sizeof(relocations) / sizeof(relocations[0]); // two calls
fwrite(&amp;amp;sectionText, 1, sizeof(sectionText), binary);
offsetCounter += sizeof(sectionText);

// Write symtab
symtabCommand.symoff = sectionText.reloff +
                        sectionText.nreloc * sizeof(relocation_info); // just after relocations
symtabCommand.nsyms = 2; // two functions
symtabCommand.stroff = symtabCommand.symoff +
                        symtabCommand.nsyms * sizeof(nlist_64); // just after symbol table
symtabCommand.strsize = sizeof(stringTable);
fwrite(&amp;amp;symtabCommand, 1, sizeof(symtabCommand), binary);
offsetCounter += sizeof(symtabCommand);

// Write dysymtab
fwrite(&amp;amp;dysymtabCommand, 1, sizeof(dysymtabCommand), binary);
offsetCounter += sizeof(dysymtabCommand);

// Write code
fwrite(&amp;amp;code, 1, sizeof(code), binary);

// Write relocations
fwrite(&amp;amp;relocations, 1, sizeof(relocations), binary);

// Write symbol table
fwrite(&amp;amp;symbols, 1, sizeof(symbols), binary);

// Write string table
fwrite(&amp;amp;stringTable, 1, sizeof(stringTable), binary);

fclose(binary);&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;&lt;ol&gt;&lt;li&gt;&lt;a href=&quot;http://mirror.informatimago.com/next/developer.apple.com/documentation/DeveloperTools/Conceptual/MachORuntime/8rt_file_format/chapter_10_section_30.html?ref=alexdremov.me&quot; rel=&quot;noreferrer noopener&quot;&gt;Developer collection – relocation_info&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/aidansteele/osx-abi-macho-file-format-reference?ref=alexdremov.me&quot; rel=&quot;noreferrer noopener&quot;&gt;Mach-O format reference OSX-ABI&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/zhongjianfeipqy/MachOView?ref=alexdremov.me&quot;&gt;MachOViewer – check out your file structure&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;</content:encoded></item><item><title>Skip List Indexation and kth Maximum</title><link>https://alexdremov.me/skip-list-indexation-and-kth-maximum/</link><guid isPermaLink="true">https://alexdremov.me/skip-list-indexation-and-kth-maximum/</guid><description>Skip list is a nice structure that lets you to perform insertions, searches, finding n-th maximum. In this post I fokus on skip list indexation</description><pubDate>Thu, 05 Nov 2020 22:49:32 GMT</pubDate><content:encoded>&lt;p&gt;Skip List is a nice structure that lets you to perform &lt;code&gt;O(logn)&lt;/code&gt; insertions into sorted list,  &lt;code&gt;O(logn)&lt;/code&gt; searches and &lt;code&gt;O(logn)&lt;/code&gt; for finding n-th — second, third, fourth, ... — maximum or even calculating the rolling median. In this article I focus on indexation of skip list (indexable skip list).&lt;/p&gt;&lt;p&gt;The best guide I found was &lt;a href=&quot;https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.17.524&amp;rep=rep1&amp;type=pdf&amp;ref=alexdremov.me&quot; rel=&quot;noreferrer noopener&quot;&gt;“a skip list cookbook”&lt;/a&gt; last revised in 1990. It slightly touched the problem of finding &lt;strong&gt;the kth element&lt;/strong&gt;, but the provided algorithm is extremely vague and refers to unknown quantities without giving the information on how to find these quantities or update (ex. &lt;code&gt;fDistance[i]&lt;/code&gt;). Wikipedia also talks about indexing, but an algorithm for calculating skip distances is not provided.&lt;/p&gt;&lt;figure class=&quot;kg-card kg-image-card kg-card-hascaption&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2022/04/--------------2020-11-05---22.44.47.png&quot; class=&quot;kg-image&quot; alt=&quot;Skip list algo from old book&quot; loading=&quot;lazy&quot; width=&quot;1052&quot; height=&quot;802&quot; srcset=&quot;https://alexdremov.me/content/images/size/w600/2022/04/--------------2020-11-05---22.44.47.png 600w, https://alexdremov.me/content/images/size/w1000/2022/04/--------------2020-11-05---22.44.47.png 1000w, https://alexdremov.me/content/images/2022/04/--------------2020-11-05---22.44.47.png 1052w&quot; sizes=&quot;(min-width: 720px) 720px&quot;&gt;&lt;figcaption&gt;&lt;span style=&quot;white-space: pre-wrap;&quot;&gt;Cookbook searchByPosition algorithm&lt;/span&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;Therefore, I decided to create this post and provide an algorithm for indexing skip lists. Here, I’m going to give the code as well.&lt;/p&gt;&lt;h2 id=&quot;about-skip-list&quot;&gt;About skip list&lt;/h2&gt;&lt;p&gt;A skip list is a one-way linked list that has &lt;strong&gt;“express lanes”&lt;/strong&gt; for reaching distant members. It is a probabilistic data structure: selecting the &quot;height&quot; of each node relies on random numbers. As a result, it provides &lt;code&gt;O(logn)&lt;/code&gt; insert and search complexity.&lt;/p&gt;&lt;figure class=&quot;kg-card kg-image-card&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2022/04/800px-Skip_list.svg_-768x180.png&quot; class=&quot;kg-image&quot; alt=&quot;Skip list&quot; loading=&quot;lazy&quot; width=&quot;768&quot; height=&quot;180&quot; srcset=&quot;https://alexdremov.me/content/images/size/w600/2022/04/800px-Skip_list.svg_-768x180.png 600w, https://alexdremov.me/content/images/2022/04/800px-Skip_list.svg_-768x180.png 768w&quot; sizes=&quot;(min-width: 720px) 720px&quot;&gt;&lt;/figure&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Fast lanes change complexity of search, insert, and indexation from &lt;code spellcheck=&quot;false&quot; style=&quot;white-space: pre-wrap;&quot;&gt;O(n)&lt;/code&gt; to &lt;code spellcheck=&quot;false&quot; style=&quot;white-space: pre-wrap;&quot;&gt;O(logn)&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;Each node has a link to the right node on the same level and a link to the bottom node that has the same value, but one level lower. The first layer doesn’t have a bottom link. Some nodes don’t have the right node. We consider the null right node as \(+\infty\) and head as \(-\infty\).&lt;/p&gt;&lt;p&gt;To search for an element, we start at the left top corner and move: right if the right element is lower or equals to the needed element or move down if it is bigger than the required element.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;If the required element is not presented in the list, we end up in the potential position for the insertion.&lt;/div&gt;&lt;/div&gt;&lt;p&gt;Indexing skip list allows us to calculate the rolling median of set in &lt;code&gt;O(logn)&lt;/code&gt; and to find n-th minimum or maximum in &lt;code&gt;O(logn)&lt;/code&gt; also!&lt;/p&gt;&lt;h2 id=&quot;defining-the-node&quot;&gt;Defining the node&lt;/h2&gt;&lt;p&gt;Each node is going to be:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;template&amp;lt;typename T&amp;gt;
struct TreeNode {
    T             key;
    unsigned      level     = 1;
    bool          headNode  = false;
    bool          deleted   = false;
    size_t        skipDist  = 0;
    TreeNode&amp;lt;T&amp;gt;*  right     = null;
    TreeNode&amp;lt;T&amp;gt;*  down      = null;
}&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;key&lt;/code&gt; – stored value&lt;/li&gt;&lt;li&gt;&lt;code&gt;level&lt;/code&gt; – the level of the node&lt;/li&gt;&lt;li&gt;&lt;code&gt;headNode&lt;/code&gt; – is this node is the head node&lt;/li&gt;&lt;li&gt;&lt;code&gt;deleted&lt;/code&gt; – the node is marked as deleted&lt;/li&gt;&lt;li&gt;&lt;code&gt;skipDist&lt;/code&gt; – distance skipped&lt;/li&gt;&lt;li&gt;&lt;code&gt;right&lt;/code&gt; – right node&lt;/li&gt;&lt;li&gt;&lt;code&gt;down&lt;/code&gt; – down node&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;We need to define the &lt;code&gt;deleted&lt;/code&gt; mark as we can’t delete the node immediately due to the fact that the list is one-way linked. We just can’t update the left to the deleted one’s member. On the other hand, such a feature is useful in multi-threaded projects.&lt;/p&gt;&lt;p&gt;On this figure you can see what &lt;code&gt;skipDist&lt;/code&gt; means:&lt;/p&gt;&lt;figure class=&quot;kg-card kg-image-card&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2022/04/800px-Skip_list.svg_-------768x180.png&quot; class=&quot;kg-image&quot; alt=&quot;Skip list with skip distances&quot; loading=&quot;lazy&quot; width=&quot;768&quot; height=&quot;180&quot; srcset=&quot;https://alexdremov.me/content/images/size/w600/2022/04/800px-Skip_list.svg_-------768x180.png 600w, https://alexdremov.me/content/images/2022/04/800px-Skip_list.svg_-------768x180.png 768w&quot; sizes=&quot;(min-width: 720px) 720px&quot;&gt;&lt;/figure&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;Basically, &lt;code spellcheck=&quot;false&quot; style=&quot;white-space: pre-wrap;&quot;&gt;skipDist&lt;/code&gt; counts how many nodes will be skipped if you travel by the according &lt;i&gt;&lt;em class=&quot;italic&quot; style=&quot;white-space: pre-wrap;&quot;&gt;fast lane&lt;/em&gt;&lt;/i&gt;&lt;/div&gt;&lt;/div&gt;&lt;h2 id=&quot;defining-skip-list&quot;&gt;Defining skip list&lt;/h2&gt;&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;template&amp;lt;typename T&amp;gt;
class SkipList {
    unsigned     maxLevels;
    TreeNode&amp;lt;T&amp;gt;* head;
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Pretty much self-explanatory structure.&lt;/p&gt;&lt;p&gt;On initialisation, we create &lt;code&gt;maxLevels&lt;/code&gt; number of head nodes:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;this-&amp;gt;head = new TreeNode&amp;lt;T&amp;gt;(0, this-&amp;gt;maxLevels);
this-&amp;gt;head-&amp;gt;headNode = true;

TreeNode&amp;lt;T&amp;gt;* pos = this-&amp;gt;head;
for(unsigned i = 1; i &amp;lt; maxLevels; ++i) {
    TreeNode&amp;lt;T&amp;gt;* newNode = new TreeNode&amp;lt;T&amp;gt;(0, this-&amp;gt;maxLevels - i);
    newNode-&amp;gt;headNode = true;
    
    pos-&amp;gt;down = newNode;
    pos = newNode;
}&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;insert&quot;&gt;Insert&lt;/h2&gt;&lt;p&gt;The hardest part of the insert algorithm is to update skip distances and to create upper-level nodes if random coin said so.&lt;/p&gt;&lt;p&gt;As I discussed previously, we do not delete elements, but rather mark them as deleted. therefore, before processing, we need to perform deletions. Let it be some function &lt;code&gt;processDeletions(node)&lt;/code&gt;. It finally deletes the element right to the node if it was marked as deleted.&lt;/p&gt;&lt;p&gt;Also, to check for cases when the right node is null, I created a function that compares node value to the key value.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;// key &amp;lt; node
int compareWithNode(T key, TreeNode&amp;lt;T&amp;gt;* node){
    if (node == nullptr)
        return -1;
    if (key == node-&amp;gt;key)
    	return 0;
    return key &amp;lt; node-&amp;gt;key ? -1 : 1;
}&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;&lt;code spellcheck=&quot;false&quot; style=&quot;white-space: pre-wrap;&quot;&gt;compareWithNode&lt;/code&gt; returns &lt;b&gt;&lt;strong style=&quot;white-space: pre-wrap;&quot;&gt;0&lt;/strong&gt;&lt;/b&gt; if values are equal, &lt;b&gt;&lt;strong style=&quot;white-space: pre-wrap;&quot;&gt;-1&lt;/strong&gt;&lt;/b&gt; if the key is lower than the node value, and &lt;b&gt;&lt;strong style=&quot;white-space: pre-wrap;&quot;&gt;1&lt;/strong&gt;&lt;/b&gt; if the key value is higher than the node value&lt;/div&gt;&lt;/div&gt;&lt;p&gt;As discussed before, if we encounter a null node, then we consider it as +inf⁡.&lt;/p&gt;&lt;p&gt;To perform all desired operations, the insert function is going to accept the current node, desired key, a pointer to the inserted node (if any), current position. We need to have a pointer to the inserted node for two reasons: to know on the higher levels whether the node was inserted at all, and we need the link to the bottom if we generate a “fast lane” node.&lt;/p&gt;&lt;p&gt;Also, let the function return bool value: whether the node was inserted on the previous level. If it was inserted, then we can&lt;strong&gt; flip the coin again&lt;/strong&gt; and insert the “fast lane” node again on the current level.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;As I said, the algorithm relies on randomness. Decision whether new fast lane will be created is based on random coin.&lt;/div&gt;&lt;/div&gt;&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;bool insertRecursive(TreeNode&amp;lt;T&amp;gt;* node,
                     T key,
                     TreeNode&amp;lt;T&amp;gt;** insertedOne,
                     unsigned* pos) {
    this-&amp;gt;processDeletions(node);
    
    int compareRight = compareWithNode(key, node-&amp;gt;right);
    // save position at the current recursion level
    unsigned posHere = *pos; 
    
    if (compareRight == 0 ||
       (node-&amp;gt;key == key &amp;amp;&amp;amp; node-&amp;gt;headNode != true))
        return false;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In the case, if the right node value is equal to the desired or the current node value is equal to the desired and this node is not the head node, the function returns with false as no insertions were needed.&lt;/p&gt;&lt;p&gt;Finally, if the right node’s value is lower than the desired, the function just increases the pos counter for the right node’s skip distance + 1 and dives deeper.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;if (compareRight == 1) { // right elem is lower
    *pos += node-&amp;gt;right-&amp;gt;skipDist + 1;
    return insertRecursive(node-&amp;gt;right,
                           key,
                           insertedOne,
                           pos);
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Interesting things happen if we go down. First of all, if we need to go lower and it’s the very first level, then we simply insert the node and return true as the node was inserted.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;else { // (compareRight == -1) // want go down
    if (node-&amp;gt;level == 1) {
        *(insertedOne) = new TreeNode&amp;lt;T&amp;gt;(key, 1, ++(this-&amp;gt;ids));
        (*(insertedOne))-&amp;gt;right = node-&amp;gt;right;
        node-&amp;gt;right = *(insertedOne);
        return true;
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If we can go down, then some cases are needed to be considered.&lt;/p&gt;&lt;figure class=&quot;kg-card kg-image-card&quot;&gt;&lt;img src=&quot;https://media.tenor.com/images/0370865dc28ad806626731f7f7dbdf09/tenor.gif&quot; class=&quot;kg-image&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;498&quot; height=&quot;280&quot;&gt;&lt;/figure&gt;&lt;p&gt;We need to go deeper. That means that on the current level the right node’s value is higher than the desired, so the insertion is going to occur before the right node. That means that the right node’s &lt;strong&gt;skip distance is going to be increased by 1.&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Also, if on the current level we insert a fast lane node, then the right node’s skip distance is shortened by the skip distance of the inserted node.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;else {
    // whether there was an insertion on the deeper level
    bool possibleLevelInsert = insertRecursive(node-&amp;gt;down,
                               key, insertedOne, pos);
                               
    if (!possibleLevelInsert ) {
        // if insertion of fast lane is impossible
        if (node-&amp;gt;right != nullptr &amp;amp;&amp;amp; *insertedOne != nullptr) {
        
        // if right node on the current level is presented
        // and we inserted the node (*insertedOne != nullptr)
            node-&amp;gt;right-&amp;gt;skipDist++;
            
         }
         
     return false; // insert of further fast lanes is impossible
     }
     &lt;/code&gt;&lt;/pre&gt;&lt;p&gt;At this point, we know that we can insert a fast lane node on the current level. Let’s spin a coin and decide.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;if(node-&amp;gt;level == 1) // trivial case
    return true;
    
bool insertNow = this-&amp;gt;spinACoin();
if (!insertNow){
	// no fast lane insertion -&amp;gt; increase the next
    // fast lane skip distance as the node was inserted somewhere between.
    if (node-&amp;gt;right != nullptr){
         node-&amp;gt;right-&amp;gt;skipDist++;
    }
    return false;
}
// Can insert the fast lane node

TreeNode&amp;lt;T&amp;gt;* newNode = new TreeNode&amp;lt;T&amp;gt;(key, node-&amp;gt;level);
newNode-&amp;gt;down = *(insertedOne);
newNode-&amp;gt;right = node-&amp;gt;right;
newNode-&amp;gt;skipDist = (*pos - posHere);

// *pos stopped updating at the insertion position.
// At the beginning, we saved temporary pos at the current recursion level.
if (node-&amp;gt;right != nullptr) {
   // shrink right node skip distance as we inserted new fast lane node
   node-&amp;gt;right-&amp;gt;skipDist -= newNode-&amp;gt;skipDist;
}
node-&amp;gt;right = newNode;
*(insertedOne) = newNode;
return true;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This was massive code. Final insert function:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;bool insertRecursive(TreeNode&amp;lt;T&amp;gt;* node, T key, TreeNode&amp;lt;T&amp;gt;** insertedOne, unsigned* pos){
    this-&amp;gt;processDeletions(node);
    int compareRight = compareWithNode(key, node-&amp;gt;right);
    unsigned posHere = *pos;
    if (compareRight == 0 || (node-&amp;gt;key == key &amp;amp;&amp;amp; node-&amp;gt;headNode != true))
        return false;
    if (compareRight == 1){ // right elem is lower
        *pos += node-&amp;gt;right-&amp;gt;skipDist + 1;
        return insertRecursive(node-&amp;gt;right, key, insertedOne, pos);
    } else {// (compareRight == -1) // want go down
        if (node-&amp;gt;level == 1){
            *(insertedOne) = new TreeNode&amp;lt;T&amp;gt;(key, 1);
            (*(insertedOne))-&amp;gt;right = node-&amp;gt;right;
            node-&amp;gt;right = *(insertedOne);
            return true;
        } else {
            bool possibleLevelInsert = insertRecursive(node-&amp;gt;down, key, insertedOne, pos);
            if (!possibleLevelInsert ){
                if (node-&amp;gt;right != nullptr &amp;amp;&amp;amp; *insertedOne != nullptr){
                    node-&amp;gt;right-&amp;gt;skipDist++;
                }
                return false;
            }
            if(node-&amp;gt;level == 1)
                return true;
            bool insertNow = this-&amp;gt;spinACoin();
            if (!insertNow){
                if (node-&amp;gt;right != nullptr){
                    node-&amp;gt;right-&amp;gt;skipDist++;
                }
                return false;
            }
            TreeNode&amp;lt;T&amp;gt;* newNode = new TreeNode&amp;lt;T&amp;gt;(key, node-&amp;gt;level);
            newNode-&amp;gt;down = *(insertedOne);
            newNode-&amp;gt;right = node-&amp;gt;right;
            newNode-&amp;gt;skipDist = (*pos - posHere);
            if (node-&amp;gt;right != nullptr) {
                node-&amp;gt;right-&amp;gt;skipDist -= newNode-&amp;gt;skipDist;
            }
            node-&amp;gt;right = newNode;
            *(insertedOne) = newNode;
            return true;
        }
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;process-deletions&quot;&gt;Process deletions&lt;/h2&gt;&lt;p&gt;The only thing that left is to define the &lt;code&gt;processDeletions(node)&lt;/code&gt; function. If the right node is marked as deleted, then we need to update &lt;strong&gt;right to the right node&lt;/strong&gt; skip distance. Also, at first, it’s needed to go to the deepest level of recursion and perform alterations from the end to the start.&lt;/p&gt;&lt;p&gt;Finally, delete the node by setting &lt;code&gt;node-&amp;gt;right = node-&amp;gt;right-&amp;gt;right&lt;/code&gt;.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;void processDeletions(TreeNode&amp;lt;T&amp;gt;* node){
    if (node == nullptr)
        return;
    if (node-&amp;gt;right == nullptr)
        return;
    if (node-&amp;gt;right-&amp;gt;deleted == false)
        return;
    processDeletions(node-&amp;gt;right);
    if (node-&amp;gt;right-&amp;gt;deleted){
        if (node-&amp;gt;right-&amp;gt;right != nullptr){
            node-&amp;gt;right-&amp;gt;right-&amp;gt;skipDist += node-&amp;gt;right-&amp;gt;skipDist;
        }
        node-&amp;gt;right = node-&amp;gt;right-&amp;gt;right;
    }&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;remove-value&quot;&gt;Remove value&lt;/h2&gt;&lt;p&gt;A similar approach is applied during value removal. The only difference is the way skip distances are updated. In the beginning, all general checks and operations. The function is going to accept the node, deletion key, and return the result of the removal.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;bool removeRecursive(TreeNode&amp;lt;T&amp;gt;* node, T key) {
    this-&amp;gt;processDeletions(node);
    if (node == nullptr)
        return false;
    int compareRight = compareWithNode(key, node-&amp;gt;right);&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If the value was found on the right, we know that we found it on the most upper level where this value presented, so we go down and mark all fast lane nodes as deleted too. Notably, we can’t find value in the current node as we could have been found it one step before as the right value.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;if (compareRight == 0){
    node = node-&amp;gt;right;
    while (node != nullptr){
        node-&amp;gt;deleted = true;
        node = node-&amp;gt;down;
    }
    return true;
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Firstly, if the right element is lower than the needed, the function simply steps right.&lt;/p&gt;&lt;p&gt;Else, if it’s needed to go down, recursion goes deeper but collects information on whether the deletion was performed on the bottom levels. In case of the successfull deletion, it’s needed to update skip distances of the right element.&lt;/p&gt;&lt;p&gt;In case if the right fast lane node is deleted, then the right to the fast lane node’s skip distance is increased by the skip distance of the fast lane.&lt;/p&gt;&lt;p&gt;If the right element is some other element, then its skip distance is decreased by one as we deleted an element before it.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;bool res = removeRecursive(node-&amp;gt;down, key);
if (res &amp;amp;&amp;amp; node-&amp;gt;right != nullptr){
    if (node-&amp;gt;right-&amp;gt;key == key){
        if (node-&amp;gt;right-&amp;gt;right != nullptr)
            node-&amp;gt;right-&amp;gt;right-&amp;gt;skipDist += node-&amp;gt;right-&amp;gt;skipDist;
        node-&amp;gt;right = node-&amp;gt;right-&amp;gt;right;
    } else {
        if (node-&amp;gt;right-&amp;gt;skipDist != 0)
            node-&amp;gt;right-&amp;gt;skipDist--;
    }
}
return res;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Final removal code:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;bool removeRecursive(TreeNode&amp;lt;T&amp;gt;* node, T key){
    processDeletions(node);
    if (node == nullptr)
        return false;
    int compareRight = compareWithNode(key, node-&amp;gt;right);
    if (compareRight == 0){
        if (compareRight == 0)
            node = node-&amp;gt;right;
        while (node != nullptr){
            node-&amp;gt;deleted = true;
            node = node-&amp;gt;down;
        }
        return true;
    }
    if (compareRight == 1){ // right elem is lower
        return removeRecursive(node-&amp;gt;right, key);
    } else {// (compareRight == -1) // want go down
        bool res = removeRecursive(node-&amp;gt;down, key);
        if (res &amp;amp;&amp;amp; node-&amp;gt;right != nullptr){
            if (node-&amp;gt;right-&amp;gt;key == key){
                if (node-&amp;gt;right-&amp;gt;right != nullptr)
                    node-&amp;gt;right-&amp;gt;right-&amp;gt;skipDist += node-&amp;gt;right-&amp;gt;skipDist;
                node-&amp;gt;right = node-&amp;gt;right-&amp;gt;right;
            } else {
                if (node-&amp;gt;right-&amp;gt;skipDist != 0)
                    node-&amp;gt;right-&amp;gt;skipDist--;
            }
        }
        return res;
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;finally&quot;&gt;Finally&lt;/h2&gt;&lt;p&gt;At this point, removal and insertion functions are implemented and we have correctly updated skip distances. Therefore, we can index the skip list.&lt;/p&gt;&lt;p&gt;The code for finding the kth skip list element is trivial. We consider the right node as a possible jump only if k–skipDistance≥0&lt;em&gt;k&lt;/em&gt;–&lt;em&gt;skipDistance&lt;/em&gt;≥0. If not, we go down.&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;The hardest part was updating skip distances. After they are evaluted, indexing skip list is extremely easy&lt;/div&gt;&lt;/div&gt;&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;TreeNode&amp;lt;T&amp;gt;* deepWalkForKth(TreeNode&amp;lt;T&amp;gt;* node, int k) {
    processDeletions(node);
    if (node == nullptr)
        return nullptr;
    if (k == 0)
        return node;
    if (node-&amp;gt;right != nullptr){
        if (k - (int)node-&amp;gt;right-&amp;gt;skipDist &amp;gt; 0)
            return deepWalkForKth(node-&amp;gt;right, k - node-&amp;gt;right-&amp;gt;skipDist - 1);
    }
    return deepWalkForKth(node-&amp;gt;down, k);
}&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;final-notes&quot;&gt;Final notes&lt;/h2&gt;&lt;p&gt;As you see, implementing skip list indexation is not too hard. What’s complicated is to consider all cases of skip distances updates. Here, I hope that I discussed all cases. If you have any questions, do not hesitate to ask them in the comments.&lt;/p&gt;&lt;h2 id=&quot;love-algorithms&quot;&gt;Love algorithms?&lt;/h2&gt;&lt;p&gt;Check out my other posts on algorithms! I explain complex yet beautiful data structures.&lt;/p&gt;&lt;figure class=&quot;kg-card kg-bookmark-card&quot;&gt;&lt;a class=&quot;kg-bookmark-container&quot; href=&quot;https://alexdremov.me/tag/algorithms/&quot;&gt;&lt;div class=&quot;kg-bookmark-content&quot;&gt;&lt;div class=&quot;kg-bookmark-title&quot;&gt;Alex Dremov | Algorithms&lt;/div&gt;&lt;div class=&quot;kg-bookmark-description&quot;&gt;Those are hard! In this section I discuss algorithms that I encountered during work or my college assignments&lt;/div&gt;&lt;div class=&quot;kg-bookmark-metadata&quot;&gt;&lt;img class=&quot;kg-bookmark-icon&quot; src=&quot;https://alexdremov.me/assets/icons/apple-touch-icon.png&quot; alt=&quot;&quot;&gt;&lt;span class=&quot;kg-bookmark-author&quot;&gt;Alex Dremov&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-bookmark-thumbnail&quot;&gt;&lt;img src=&quot;https://images.unsplash.com/photo-1580777361964-27e9cdd2f838?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=max&amp;amp;fm=jpg&amp;amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDZ8fGFsZ29yaXRobXxlbnwwfHx8fDE2NDk1MDYwMDM&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;w=2000&quot; alt=&quot;&quot; onerror=&quot;this.style.display = &apos;none&apos;&quot;&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;h2 id=&quot;project-code&quot;&gt;Project code&lt;/h2&gt;&lt;p&gt;You can download indexed skip list here&lt;/p&gt;&lt;div class=&quot;kg-card kg-file-card&quot;&gt;&lt;a class=&quot;kg-file-card-container&quot; href=&quot;https://alexdremov.me/content/files/2022/04/skiplist-1.cpp&quot; title=&quot;Download&quot; download=&quot;&quot;&gt;&lt;div class=&quot;kg-file-card-contents&quot;&gt;&lt;div class=&quot;kg-file-card-title&quot;&gt;Skiplist&lt;/div&gt;&lt;div class=&quot;kg-file-card-caption&quot;&gt;Full indexable skip list code&lt;/div&gt;&lt;div class=&quot;kg-file-card-metadata&quot;&gt;&lt;div class=&quot;kg-file-card-filename&quot;&gt;skiplist.cpp&lt;/div&gt;&lt;div class=&quot;kg-file-card-filesize&quot;&gt;8 KB&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;kg-file-card-icon&quot;&gt;&lt;svg viewBox=&quot;0 0 24 24&quot;&gt;&lt;defs&gt;&lt;style&gt;.a{fill:none;stroke:currentColor;stroke-linecap:round;stroke-linejoin:round;stroke-width:1.5px;}&lt;/style&gt;&lt;/defs&gt;&lt;title&gt;download-circle&lt;/title&gt;&lt;polyline class=&quot;a&quot; points=&quot;8.25 14.25 12 18 15.75 14.25&quot;&gt;&lt;/polyline&gt;&lt;line class=&quot;a&quot; x1=&quot;12&quot; y1=&quot;6.75&quot; x2=&quot;12&quot; y2=&quot;18&quot;&gt;&lt;/line&gt;&lt;circle class=&quot;a&quot; cx=&quot;12&quot; cy=&quot;12&quot; r=&quot;11.25&quot;&gt;&lt;/circle&gt;&lt;/svg&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;</content:encoded></item><item><title>How Deep Neural Networks Work</title><link>https://alexdremov.me/how-deep-neural-networks-train/</link><guid isPermaLink="true">https://alexdremov.me/how-deep-neural-networks-train/</guid><description>Here, I combine the explanation of Neural Nets with coding. By the end, we will develop a basic neural network and try to solve usual problems</description><pubDate>Fri, 08 May 2020 02:25:40 GMT</pubDate><content:encoded>&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;/h2&gt;&lt;p&gt;Today, when such beautiful frameworks as &lt;strong&gt;Keras&lt;/strong&gt;, &lt;strong&gt;Tensorflow&lt;/strong&gt;, &lt;strong&gt;SkLearn&lt;/strong&gt; exist, many people are not worried about how Neural Network models work and train. However, when interested people start to dig and search for explanations, they usually face unreasonably significant amounts of linear algebra thrown right into the face without any practical information.&lt;/p&gt;&lt;p&gt;At least, it was my case. Decided to understand Neural Networks, I enrolled in a local university online course. I watched lecture after lecture, noted everything necessary, and from the bottom of my heart waited for practical information and possible algorithms implementation.&lt;/p&gt;&lt;p&gt;The course ended, and I was left with a thick notebook of linear algebra, calculus, and no understanding of what can I do with all this information.&lt;/p&gt;&lt;p&gt;However, I don’t want somebody else to walk on the same road as me, so I decided to write this article. e.g. “Hello, world” in Neural Nets.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Side note:&lt;/strong&gt; in this guide, I will not explore deeply program architecture and good Python practices as it is not the primary purpose of the article.&lt;/p&gt;&lt;h2 id=&quot;single-neurone-what-is-it&quot;&gt;Single neurone: what is it?&lt;/h2&gt;&lt;figure class=&quot;kg-card kg-image-card&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2022/10/Unknown.png&quot; class=&quot;kg-image&quot; alt=&quot;Linear regression&quot; loading=&quot;lazy&quot; width=&quot;598&quot; height=&quot;579&quot;&gt;&lt;/figure&gt;&lt;p&gt;We find a line that approximates our points the best. The line can be described by the following equation:&lt;/p&gt;&lt;p&gt;\[ y(x) = wx + b \]&lt;/p&gt;&lt;p&gt;By adjusting \(w\) and \(b\) we can make our line the best fit for the current points distribution.&lt;/p&gt;&lt;p&gt;And that’s actually what every single neuron in basic Neural Net does. The big difference is that line of best fit presented on the image is in 2D. In the real world, algorithms solve problems in multidimensional space.&lt;/p&gt;&lt;p&gt;For example, if you would like to predict who survives after the Titanic tragedy, you could take into account such parameters as age, fare, sex, number of siblings, etc. See that we already have 4 dimensions to work with. However, you should not be scared of that. A lot of concepts that work in 3D or 2D can also be applied to multidimensional space.&lt;/p&gt;&lt;h2 id=&quot;classification-problem&quot;&gt;Classification problem&lt;/h2&gt;&lt;p&gt;Let’s continue to work on the Titanic survival chance problem and imagine that our neuron already knows the line of best fit. The problem of binary dependent variable classification (survived/did not survive) names Logistic Regression.&lt;/p&gt;&lt;p&gt;The problem is that line is not limited, but probability can’t be lower than zero and higher than one. Here comes a sigmoid function.&lt;/p&gt;&lt;figure class=&quot;kg-card kg-image-card&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2022/10/Unknown-1.png&quot; class=&quot;kg-image&quot; alt=&quot;Sigmoid function&quot; loading=&quot;lazy&quot; width=&quot;885&quot; height=&quot;575&quot; srcset=&quot;https://alexdremov.me/content/images/size/w600/2022/10/Unknown-1.png 600w, https://alexdremov.me/content/images/2022/10/Unknown-1.png 885w&quot; sizes=&quot;(min-width: 720px) 720px&quot;&gt;&lt;/figure&gt;&lt;p&gt;\[ y(z) = \frac{1}{1 + e^{-z}} \]&lt;/p&gt;&lt;p&gt;As you see, the function is limited by 0 and 1. So, we will use it to adjust the neuron output. The name of the function that sets neuron output basing on a linear part is an “&lt;strong&gt;activation&lt;/strong&gt; function”.&lt;/p&gt;&lt;h2 id=&quot;how-this-works-with-multidimensions&quot;&gt;How this works with multidimensions &lt;/h2&gt;&lt;p&gt;The same problem is a little bit different when \(x\) has multiple dimensions – vector. Then, every component has a different effect on the final result, so \(w\) also should be a vector.&lt;/p&gt;&lt;p&gt;Formula in vector form:&lt;/p&gt;&lt;p&gt;\[ z = w^{T}x + b\]&lt;/p&gt;&lt;p&gt;We set \(w\) as column-vector and \(x\) as column-vector. Therefore, to get scalar, we transpose the \(w\) vector. How it works:&lt;/p&gt;&lt;p&gt;\[ x = \begin{bmatrix} x_1\\ x_2\\ \ldots \\ x_n\\ \end{bmatrix} w = \begin{bmatrix} w_1\\ w_2\\ \ldots;\\ w_n\\ \end{bmatrix} \] \[ w^{T}*x = \begin{bmatrix} w_1, w_2, \ldots, w_n \end{bmatrix} * \begin{bmatrix} x_1\\ x_2\\ \ldots;\\ x_n\\ \end{bmatrix} =\] \[ w_1x_1 + w_2x_2 + \ldots\]&lt;/p&gt;&lt;p&gt;If you feel a little bit uncomfortable with the expression above, repeat basic matrix multiplication.&lt;/p&gt;&lt;p&gt;That&apos;s it. This is how a single basic neuron works. It takes input, multiplies it by \(w\), adds \(b\) (just a number), applies activation function, and sends computed value further. This process is named forward propagation. Now, let&apos;s implement this in code.&lt;/p&gt;&lt;h2 id=&quot;forward-propagation-in-code&quot;&gt;Forward propagation in code&lt;/h2&gt;&lt;p&gt;I will use NumPy for basic operations. Of course, you can implement matrix multiplication, addition, etc. by yourself, but NumPy does it more effectively and faster as it’s already compiled.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;import numpy as np&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Sigmoid function:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;def sigmoid(z):
    return 1 / (1 + np.exp(-z))&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Forward propagation function:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;def forward_propagation(w, b, x):
    z = np.dot(w.T, x) + b # np.dot(..., ...) — matrix multiplication
    return sigmoid(z)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Great. Now we can calculate forward propagation of a single neuron. But how we figure out \(w\) and \(b\) values?&lt;/p&gt;&lt;h2 id=&quot;loss-function&quot;&gt;Loss function&lt;/h2&gt;&lt;p&gt;To understand how well our algorithm performs, we need to define a loss function. For purposes of binary classification &amp;lt;strong&amp;gt;logarithmic loss&amp;lt;/strong&amp;gt; performs well. So, we will use it.&lt;/p&gt;&lt;p&gt;\[ L(\widehat{y}, y) = -(y ln(\widehat{y}) + (1-y)ln(1-\widehat{y}) \]&lt;/p&gt;&lt;p&gt;That&apos;s how it looks:&lt;/p&gt;&lt;figure class=&quot;kg-card kg-image-card&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2022/10/XUYY3761.gif&quot; class=&quot;kg-image&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;750&quot; height=&quot;580&quot; srcset=&quot;https://alexdremov.me/content/images/size/w600/2022/10/XUYY3761.gif 600w, https://alexdremov.me/content/images/2022/10/XUYY3761.gif 750w&quot; sizes=&quot;(min-width: 720px) 720px&quot;&gt;&lt;/figure&gt;&lt;p&gt;\( \hat{y} \) represents computed value, \(y\) – actual&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;def loss(A, Y):
    return -(Y * np.log(A) + (1 - Y) * np.log(1 - A))&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;As you see, loss tends to infinity when \(\hat{y}\) and \(y\) are different, but it&apos;s 0 when they are exactly the same.&lt;/p&gt;&lt;p&gt;To train model, we use labeled information: pairs of \(x , y\).  \(x\) represents input vector and \(y\) – desired output for this vector. If we stack all available \(x\) into a single matrix, we&apos;ll create \(X\) – a matrix that contains all training data. We can do the same thing for \(Y\)&lt;/p&gt;&lt;p&gt;\[ X = \begin{bmatrix} | &amp;amp;&amp;amp; | &amp;amp;&amp;amp;  &amp;amp;&amp;amp; |\\  x_1 &amp;amp;&amp;amp; x_2 &amp;amp;&amp;amp; \ldots &amp;amp;&amp;amp; x_m\\ | &amp;amp;&amp;amp; | &amp;amp;&amp;amp;  &amp;amp;&amp;amp; | \end{bmatrix}  \]&lt;/p&gt;&lt;p&gt;\[ Y = \begin{bmatrix} | &amp;amp;&amp;amp; | &amp;amp;&amp;amp;  &amp;amp;&amp;amp; |\\  y_1 &amp;amp;&amp;amp; y_2 &amp;amp;&amp;amp; \ldots &amp;amp;&amp;amp; y_m\\ | &amp;amp;&amp;amp; | &amp;amp;&amp;amp;  &amp;amp;&amp;amp; | \end{bmatrix}  \]&lt;/p&gt;&lt;p&gt;If we have \(m\) samples and every x vector is \(n\)-dimensional, then we can calculate the cost of the algorithm with selected \(w\) and \(b\).&lt;/p&gt;&lt;p&gt;\[ J(w,b) = \frac{1}{m} \sum_{i=1}^{m} L(\hat{y}^{(i)}, y^{(i)}) \]&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;def cost(A, Y, m):
    return 1 / m * np.sum(loss(A, Y))&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;vectorization&quot;&gt;Vectorization&lt;/h2&gt;&lt;p&gt;Currently, we can calculate forward propagation for single (x, y) set and we need to calculate values for all \(m\) available training pairs. The most obvious answer is to start the for loop and calculate iteratively. But this is not the optimal case. We can use \(X\) and \(Y\) matrices to calculate forward propagation for the entire training set. That&apos;s how it works:&lt;/p&gt;&lt;p&gt;\[ X^{T}w + b = \begin{bmatrix} – &amp;amp;&amp;amp; x_1 &amp;amp;&amp;amp; – \\ – &amp;amp;&amp;amp; x_2 &amp;amp;&amp;amp; – \\ &amp;amp;&amp;amp; … &amp;amp;&amp;amp; \\ – &amp;amp;&amp;amp; x_m &amp;amp;&amp;amp; –\end{bmatrix} * \begin{bmatrix} w_1\\ w_2\\ …\\ w_n\\ \end{bmatrix} + b = \]&lt;br&gt;\[ = \begin{bmatrix} x_1^{T}w + b \\ x_2^{T}w + b \\ … \\ x_m^{T}w+ b\\ \end{bmatrix} \]&lt;/p&gt;&lt;p&gt;As you see, every row represents forward propagation for every training set.&lt;/p&gt;&lt;p&gt;This approach optimizes code and speeds up calculations. Whenever possible, vectorize code. The same technique can be applied during backpropagation.&lt;/p&gt;&lt;h2 id=&quot;in-the-core-of-learning-backpropagation&quot;&gt;In the core of learning: Backpropagation&lt;/h2&gt;&lt;p&gt;At this moment we can calculate neuron output and estimate how close it to the actual value. But how we can figure out \(w\) and \(b\) values? Here comes a Gradient Descent concept.&lt;/p&gt;&lt;p&gt;The best explanation of GradDescent I ever heard:&lt;/p&gt;&lt;blockquote class=&quot;kg-blockquote-alt&quot;&gt;It’s like you are trying to find a door in a completely dark room and you can only “feel” in what direction to move&lt;/blockquote&gt;&lt;p&gt;Imagine that you have some function, but you do not know it’s expression. And shape. And you are in multidimensional space. Then, you randomly placed at some point of this function and asked to find its minimum. Not the most pleasant situation, right? However, you know how your position was calculated, so you can find a derivative. But what derivative gives? Let’s take a look at this function.&lt;/p&gt;&lt;figure class=&quot;kg-card kg-image-card&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2022/10/Unknown-3.png&quot; class=&quot;kg-image&quot; alt=&quot;Gradient descent visual&quot; loading=&quot;lazy&quot; width=&quot;597&quot; height=&quot;575&quot;&gt;&lt;/figure&gt;&lt;p&gt;Using derivative we can find direction to the function’s minimum. That’s how it looks animated:&lt;/p&gt;&lt;figure class=&quot;kg-card kg-image-card&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2022/10/1-1.gif&quot; class=&quot;kg-image&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;450&quot; srcset=&quot;https://alexdremov.me/content/images/2022/10/1-1.gif 600w&quot;&gt;&lt;/figure&gt;&lt;p&gt;We can take steps in an outlined direction and finally reach a minimum loss point. That’s how we can implement this to adjust \(w\) and \(b\)&lt;/p&gt;&lt;p&gt;\[w^{new} = w^{old} – \alpha \cdot \frac{\partial{J(w, b, x)}}{\partial{w}}\]&lt;/p&gt;&lt;p&gt;\[b^{new} = b^{old} – \alpha \cdot \frac{\partial{J(w, b, x)}}{\partial{b}}\]&lt;/p&gt;&lt;p&gt;Where \(\alpha\) is a learning rate. You can view derivative calculation in the spoiler, here are final expressions:&lt;/p&gt;&lt;p&gt;\[ \frac{\partial{J}}{\partial{z}} = \hat{Y} – Y \] \[ \frac{\partial{J(w, b, x)}}{\partial{w}} = \frac{1}{m} X*(\frac{dJ}{dz})^{T} = \frac{1}{m} X*(\hat{Y}-Y)^{T} \] \[ \frac{\partial{J(w, b, x)}}{\partial{b}} = \frac{1}{m} \sum_{i}^{n}{\sum_{j}^{m}{(\frac{dJ}{dz})_{ij}}} =\] \[ \frac{1}{m} \sum_{i}^{n}{\sum_{j}^{m}{(\hat{Y}-Y)_{ij}}} \]&lt;/p&gt;&lt;p&gt;Do not worry. It all looks a lot better in code. Further, I will use a different notation: \(\frac{\partial{J(w, b, x)}}{\partial{w}}\) as \(dw\), \(\frac{\partial{J(w, b, x)}}{\partial{b}}\) as \(db\), etc. Also, it’s common to name \(\hat{Y}\) as \(A\) because it represents activation function value.&lt;/p&gt;&lt;p&gt;\[\frac{\partial{L}}{\partial{A}} = \frac{-Y}{A} + \frac{1-Y}{1-A}\]&lt;/p&gt;&lt;p&gt;\[\frac{dA}{dZ} = \frac{-e^{-Z}}{(1+e^{-Z})^{2}} = \sigma(Z)(1-\sigma(Z))\]&lt;/p&gt;&lt;p&gt;\[A =  \sigma(Z)\]&lt;/p&gt;&lt;p&gt;\[\frac{\partial{L}}{\partial{Z}} = \frac{\partial{L}}{\partial{A}} * \frac{\partial{A}}{\partial{Z}} =\]&lt;/p&gt;&lt;p&gt;\[=  (1 – A)(-Y) + (1 – Y)A = A – Y\]&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;dZ = A - Y
db = 1 / m * np.sum(dZ)
dw = 1 / m * np.dot(X, dZ.T)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Then, single back propagation step can be represented in this function:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;def backpropagation(w, b, X, A, Y, learning_rate, m):
    dZ = A - Y
    db = 1 / m * np.sum(dZ)
    dw = 1 / m * np.dot(X, dZ.T)
    assert(dw.shape == w.shape)
    w = w - learning_rate * dw
    b = b - learning_rate * db
    return w, b&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;initialization&quot;&gt;Initialization&lt;/h2&gt;&lt;p&gt;To adjust \(w\) and \(b\), we need to have starting point. We are going to initialise \(w\) and \(b\) with zeros&lt;/p&gt;&lt;div class=&quot;kg-card kg-callout-card kg-callout-card-yellow&quot;&gt;&lt;div class=&quot;kg-callout-emoji&quot;&gt;💡&lt;/div&gt;&lt;div class=&quot;kg-callout-text&quot;&gt;We can initialise parameters with zeros when we have just one neuron. This approach does not work if there are several neurons and layers. If we initialize them with 0, then all neurons will develop in the same way and the whole network becomes almost useless.&lt;/div&gt;&lt;/div&gt;&lt;p&gt;Initialisation:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;w = np.zeros((n, 1))
b = np.zeros((n, 1))&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Finally, we can write full neuron learning code.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;def model(X, Y, learning_rate=0.1, n_iter=2000, costIter = [[],[]]):
    m = X.shape[1]
    n = X.shape[0]
    w = np.zeros((n, 1))
    b = 0
    for i in range(n_iter):
        A = forwardpropagation(w, b, X)
        c = cost(A, Y, m)
        if i % 5 == 0:
            print(&quot;Iteration %s: %s&quot; % (i, c))
        costIter[0].append(i)
        costIter[1].append(c)
        w, b = backpropagation(w, b, X, A, Y, learning_rate, m)
    return w, b&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In this code, we combine all previous steps:&lt;/p&gt;
&lt;!--kg-card-begin: html--&gt;
&lt;ul&gt;&lt;li&gt;Initialize all parameters&lt;/li&gt;&lt;li&gt;Start a loop&lt;/li&gt;&lt;li&gt;Perform forward propagation&lt;/li&gt;&lt;li&gt;Calculate cost&lt;/li&gt;&lt;li&gt;Print some data / save into array&lt;/li&gt;&lt;li&gt;Perform backpropagation step and update parameters&lt;/li&gt;&lt;/ul&gt;

&lt;!--kg-card-end: html--&gt;
&lt;p&gt;Finally, the model returns optimal \(w\) and \(b\) values so that we can use them to predict answers for new values.&lt;/p&gt;&lt;h2 id=&quot;testing&quot;&gt;Testing&lt;/h2&gt;&lt;p&gt;For testing, I selected a line&lt;/p&gt;&lt;p&gt;\[y = 1.23x + 3.23\]&lt;/p&gt;&lt;p&gt;Let points above the line be blue and ones that below – red. Here is the set that I gave to the model for training.&lt;/p&gt;&lt;figure class=&quot;kg-card kg-image-card&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2022/10/Unknown-4.png&quot; class=&quot;kg-image&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;370&quot; height=&quot;248&quot;&gt;&lt;/figure&gt;&lt;p&gt;Training:&lt;/p&gt;&lt;figure class=&quot;kg-card kg-image-card&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2022/10/Unknown-5.png&quot; class=&quot;kg-image&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;372&quot; height=&quot;248&quot;&gt;&lt;/figure&gt;&lt;p&gt;As we see, the cost minimizes overtime. That means that backpropagation works correctly and our \(w\) and \(b\) are adjusted right.&lt;/p&gt;&lt;p&gt;To check how well the algorithm performs, I randomly generated 2000 points and requested neuron to classify them.&lt;/p&gt;&lt;figure class=&quot;kg-card kg-image-card&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2022/10/Unknown-6.png&quot; class=&quot;kg-image&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;377&quot; height=&quot;248&quot;&gt;&lt;/figure&gt;&lt;p&gt;That’s how the algorithm performed. The green line represents the actual line.&lt;/p&gt;&lt;figure class=&quot;kg-card kg-image-card&quot;&gt;&lt;img src=&quot;https://alexdremov.me/content/images/2022/10/Unknown-7.png&quot; class=&quot;kg-image&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;377&quot; height=&quot;248&quot;&gt;&lt;/figure&gt;&lt;p&gt;The accuracy is around 99%. I suppose it misclassified ~1% due to the points that lie directly on the line.&lt;/p&gt;&lt;h2 id=&quot;what%E2%80%99s-special-about-this-classifier&quot;&gt;What’s special about this classifier?&lt;/h2&gt;&lt;p&gt;So one neuron approximates some linear function. How can it distinct cats from dogs, survived from not survived?&lt;/p&gt;&lt;p&gt;By combining neuron in stacks and in layers, we form complicated linear functions compositions, and then we can approximate sophisticated multidimensional functions that find subtle dependencies and relations during training.&lt;/p&gt;&lt;p&gt;But single neuron and backpropagation concept lie in the heart of the whole process.&lt;/p&gt;</content:encoded></item></channel></rss>