{"id":3068,"date":"2022-03-24T14:46:03","date_gmt":"2022-03-24T14:46:03","guid":{"rendered":"https:\/\/dlang.org\/blog\/?p=3068"},"modified":"2023-07-11T16:48:22","modified_gmt":"2023-07-11T16:48:22","slug":"reducing-template-compile-times","status":"publish","type":"post","link":"https:\/\/dlang.org\/blog\/2022\/03\/24\/reducing-template-compile-times\/","title":{"rendered":"Reducing Template Compile Times"},"content":{"rendered":"<p><img loading=\"lazy\" src=\"https:\/\/dlang.org\/blog\/wp-content\/uploads\/2019\/08\/cropped-rocket-200x200.png\" alt=\"\" width=\"200\" height=\"200\" class=\"alignleft size-thumbnail wp-image-2921\" srcset=\"https:\/\/dlang.org\/blog\/wp-content\/uploads\/2019\/08\/cropped-rocket-200x200.png 200w, https:\/\/dlang.org\/blog\/wp-content\/uploads\/2019\/08\/cropped-rocket-300x300.png 300w, https:\/\/dlang.org\/blog\/wp-content\/uploads\/2019\/08\/cropped-rocket-270x270.png 270w, https:\/\/dlang.org\/blog\/wp-content\/uploads\/2019\/08\/cropped-rocket-192x192.png 192w, https:\/\/dlang.org\/blog\/wp-content\/uploads\/2019\/08\/cropped-rocket-180x180.png 180w, https:\/\/dlang.org\/blog\/wp-content\/uploads\/2019\/08\/cropped-rocket-32x32.png 32w, https:\/\/dlang.org\/blog\/wp-content\/uploads\/2019\/08\/cropped-rocket.png 512w\" sizes=\"(max-width: 200px) 100vw, 200px\" \/><\/p>\n<p>Templates have been enormously profitable for the D programming language. They allow the programmer to generate efficient and correct code at compile time. Long gone are the days of preprocessor macros or handwritten, per-type data structures. D templates, though designed in the shadow of C++ templates, were not made in their image. D makes templates cleaner and more expressive, and also enables patterns like &#8220;<a href=\"https:\/\/www.youtube.com\/watch?v=HdzwvY8Mo-w\">Design by Introspection<\/a>&#8221;.<\/p>\n<p>Here is a simple example of a template that would require the use of preprocessor macros in C or C++:<\/p>\n<pre class=\"prettyprint lang-d\">template sizeOfTypeByName(string name)\n{\n  enum sizeOfTypeByName = mixin(name, \".sizeof\");\n}\nunittest\n{\n  assert(sizeOfTypeByName!\"int\" == 4);\n}<\/pre>\n<p>D&#8217;s templates are powerful tools but should not be used unthinkingly. Carelessness could result in long compile times or excessive code generation.<\/p>\n<p>In this blog post, I introduce some simple concepts that can help in writing templates that minimize resource usage. Deeper intuition can also lead to the discovery of new abstractions or increased confidence in existing ones.<\/p>\n<h2 id=\"readthememo\">Read the memo<\/h2>\n<p>The D compiler memoizes template instantiations: if I instantiate <code>MyTemplate!int<\/code> once, the compiler produces an AST for that instantiation; if I instantiate that exact template again, the previous computation is reused.<\/p>\n<p>As a demonstration, let&#8217;s write a generic addition function and use <code>pragma(msg, ...)<\/code> to print the number of instantiations at compile time. I&#8217;m going to use it twice with integers and twice with floating point numbers.<\/p>\n<pre class=\"prettyprint lang-d\">auto genericAdd(T)(const T x, const T y)\n{\n  pragma(msg, \"genericAdd instantiated with \", T);\n  return x + y;\n}\n\n\/\/ Instantiate with int\nwriteln(genericAdd!int(4, 5));\nwriteln(genericAdd!int(6, 1));\n\n\/\/ Now for the float type\nwriteln(genericAdd!float(24.0, 32.0));\nwriteln(genericAdd!float(0.0, float.nan));<\/pre>\n<p>Now let&#8217;s compile the code and look at what our <code>pragma(msg, )<\/code> says about template instantiations in the compiler.<\/p>\n<pre>dmd -c generic_add.d<\/pre>\n<p>This yields the following output during compilation:<\/p>\n<pre>genericAdd instantiated with int\ngenericAdd instantiated with float<\/pre>\n<p>We can see <code>int<\/code> and <code>float<\/code> as we expected, but notice that each is only mentioned once. Newcomers to languages with templates or generics can sometimes mistakenly think that using a template requires a potentially expensive instantiation on every use in the source code. For the benefit of those new users of D, the above is categoric proof that this is not the case; you <em>cannot<\/em> pay twice for templates you have already asked the compiler to instantiate. (You can, however, convince yourself that you <em>are<\/em> asking the compiler to do something it&#8217;s already done when you in fact are not. We&#8217;ll go over contrived and real-world examples of this later in this article.)<\/p>\n<p>The benefit of this feature should be obvious, but what may not be obvious is how it can be employed in writing templates. Within the bounds of our desire for ergonomics, we should design the <em>interfaces<\/em> of our templates to maximize the number of identical instantiations.<\/p>\n<h2 id=\"whatsinaname\">What&#8217;s in a name?<\/h2>\n<p>The following example, adapted from a real-world change to a large D project, yielded a reduction in compile time of a few percent for unit-test builds.<\/p>\n<p>Let&#8217;s say we have an expensive template whose behavior we want to test over a simple type. Our type might be:<\/p>\n<pre class=\"prettyprint lang-d\">struct Vector3\n{\n  float x;\n  float y;\n  float z;\n}<\/pre>\n<p>To demonstrate the phenomenon, we don&#8217;t have to do anything fancy, so we&#8217;ll just declare a stub called <code>send<\/code>.<\/p>\n<pre class=\"prettyprint lang-d\">\/\/ Let's say this sends a value of type T to a database.\nvoid send(T)(T x);<\/pre>\n<p><strong>A note on syntax:<\/strong> Given a variable <code>val<\/code> of type <code>int<\/code>, this template could be explicitly instantiated as <code>send!(int)(val)<\/code>. However, the compiler can infer the type <code>T<\/code>, so we can instantiate it as if it were a normal function call as <code>send(val)<\/code>. Using D&#8217;s <a href=\"https:\/\/dlang.org\/spec\/function.html#pseudo-member\">Uniform Function Call Syntax<\/a>, we could alternatively call it like a property or member, as <code>val.send()<\/code> (the approach used in the following example), or even <code>val.send<\/code>, since parentheses are optional in function calls when there are no arguments.<\/p>\n<p>Our test might then be something like:<\/p>\n<pre class=\"prettyprint lang-d\">struct Vector3\n{\n  float x;\n  float y;\n  float z;\n}\n\nunittest\n{\n  Vector3 value;\n  value.send();\n}<\/pre>\n<p>This is reasonable so far. However, an issue arises when we start to write more than one test. Should we want to test different behaviors of a fancy template, but instantiate it with the same type, then we end up spending more time in compilation than we would have expected. A lot more time. And we see large growth in the number of symbols emitted in the resulting binary, resulting in a larger file size than one would expect. Why is that?<\/p>\n<p>Despite our intuition that the compiler should consider multiple declarations of a type like <code>Vector3<\/code> in multiple <code>unittest<\/code> blocks as identical, it actually does not. We can demonstrate this effect with an extremely simple example. We&#8217;ll provide an implementation of <code>send<\/code> that prints at compile time the type of each instantiation. Then <a href=\"https:\/\/dlang.org\/spec\/version.html#staticforeach\">we&#8217;ll use <code>static foreach<\/code><\/a> to generate five distinct implementations of a single unit test.<\/p>\n<pre class=\"prettyprint lang-d\">void send(T)(T x)\n{\n  pragma(msg, T); \n}\n\n\/\/ Generate 5 unittest blocks\nstatic foreach(_; 0..5)\n{\n  unittest\n  {\n    struct JustInt\n    {\n      int x;\n    }\n    JustInt value;\n    value.send;\n  }\n}<\/pre>\n<p>This results in the following output from the compiler:<\/p>\n<pre>JustInt\nJustInt\nJustInt\nJustInt\nJustInt<\/pre>\n<p>Huh? Doesn&#8217;t this violate our &#8220;you can&#8217;t pay twice&#8221; rule? If you were to take this output from the compiler as gospel, then yes, but there&#8217;s a more subtle truth here.<\/p>\n<h2 id=\"fullyqualifiednames\">Fully qualified names<\/h2>\n<p>The name of a type as you would write it in your editor is not the complete name of a type. Let&#8217;s amend the implementation of <code>send<\/code> to print the return value of a template called <code>fullyQualifiedName<\/code> rather than printing <code>T<\/code> directly. The rest of the example remains the same.<\/p>\n<pre class=\"prettyprint lang-d\">void send(T)(T x)\n{\n  import std.traits : fullyQualifiedName;\n  pragma(msg, fullyQualifiedName!T); \n}<\/pre>\n<p>Assuming the module is named <code>example<\/code>, this yields something like:<\/p>\n<pre>example.__unittest_L13_C3_1.JustInt\nexample.__unittest_L13_C3_2.JustInt\nexample.__unittest_L13_C3_3.JustInt\nexample.__unittest_L13_C3_4.JustInt\nexample.__unittest_L13_C3_5.JustInt<\/pre>\n<p>This explains our previous conundrum. By declaring the type <em>locally<\/em> in each test, we have actually declared a new type per test, each of which results in a new instantiation.<\/p>\n<p>A type&#8217;s fully qualified name includes the name of its enclosing scope (<code>{package-name.}module-name.{scope-name(s).}TypeName<\/code>). The compiler rewrites each <code>unittest<\/code> as a unique function with a generated name. We have five unique functions, each with its own local, distinct declaration of a <code>JustInt<\/code> type. And so we end up with five distinct types.<\/p>\n<p>We want to ensure that one instantiation is reused across <code>unittest<\/code> blocks. We do that by moving the declaration of <code>JustInt<\/code> to module scope, outside of the unit tests.<\/p>\n<pre class=\"prettyprint lang-d\">struct JustInt\n{\n  int x;\n}\n\nstatic foreach(_; 0..5)\n{\n  unittest\n  {\n    JustInt value;\n    value.send;\n  }\n}<\/pre>\n<p>The <code>send<\/code> template now prints:<\/p>\n<pre>example.JustInt<\/pre>\n<p>Much better.<\/p>\n<h3 id=\"someharddata\">Some hard data<\/h3>\n<p>To collect some anecdata about the usefulness of these changes, we&#8217;ll look at compilation times and the size of compiled binaries. Since this template is <em>very<\/em> trivial, let&#8217;s generate a hundred copies of the same <code>unittest<\/code> rather than five so we can see a trend.<\/p>\n<p>On my system, timing the compilation of our programs shows the locally declared types took <strong>243ms<\/strong> to compile, but the version with a single global type declaration took <strong>159ms<\/strong> to compile. A difference of <strong>84ms<\/strong> is not all that much, sure, but in a large codebase, there may be <em>a lot<\/em> of these speedups waiting to be found. Any reduction in compile times is to be embraced, especially when it&#8217;s cumulative.<\/p>\n<p>As for binary size, I saw a savings of <code>69K<\/code> on disk. The quantity of machine code generated by the compiler is worth keeping a close eye on. Larger binaries mean more work for the linker, which in turn means more time waiting for builds to complete. The easiest job is the one you don&#8217;t have to do.<\/p>\n<h2 id=\"amorecomplexexample\">A more complex example<\/h2>\n<p>The following example demonstrates a very simple but fundamental change to a template that yields an enormous improvement in compile times and other metrics.<\/p>\n<p>Let&#8217;s say we have a fairly simple interpreter, and we want to expose functions in our D code to the scripts executed by our interpreter. We can do that with some sort of registration function, which we&#8217;ll call <code>register<\/code>.<\/p>\n<h3 id=\"thesignatureoftheregisterfunction\">The signature of the register function<\/h3>\n<p>To prove the point I&#8217;m discussing, we don&#8217;t need to implement this function&mdash;its <em>interface<\/em> is what can cause a big slow down.<\/p>\n<p>Let&#8217;s say our <code>register<\/code> function looks like this:<\/p>\n<pre class=\"prettyprint lang-d\">\/\/ Context is something our hypothetical interpreter works with\nvoid register(alias func, string registeredName)(Context x); <\/pre>\n<p>It&#8217;s pretty reasonable, right? It takes a template alias parameter that specifies the function to call (a common idiom in D) and a template value parameter of type <code>string<\/code> that represents the name of the function as it is exposed to scripts. The implementation of <code>register<\/code> will presumably map the value of <code>registeredName<\/code> to the <code>func<\/code> alias, and then scripts can call the function using that value. Functions can be registered with, e.g., the following:<\/p>\n<pre class=\"prettyprint lang-d\">\ncontext = createAContext();\ncontext.register!(writeln!string, \"writeln\")();<\/pre>\n<p>The scripts can call the Phobos <code>writeln<\/code> function template using the name <code>writeln<\/code>.<\/p>\n<h3 id=\"thecompile-timeperformanceoftheregistertemplate\">The compile-time performance of the register template<\/h3>\n<p>The interface for <code>register<\/code> looks harmless, but it turns out that it has a significant impact on compile time. We can test this by registering some random functions. The actual contents of the functions don&#8217;t matter&mdash;this article is about template compile times, so we just want a baseline figure for roughly how much time the infrastructure templates take to compile rather than the code they are hooking together.<\/p>\n<p>Although we will pull the functions out of a hat, the thing that will drive our intuition is to realize that a small number of interfaces will likely be reused many times. We could start with a basket of interface stubs like this:<\/p>\n<pre class=\"prettyprint lang-d\">int stub1(string) { return 1; }\n\nint stub2(string) { return 2; }\n\nint stub3(string) { return 3; }\n\n\/\/ etc.<\/pre>\n<p>More broadly, with a bunch of functions that have identical signatures and a bunch of functions with random parameter lists and return types, we can get a rough baseline. With the set of stubs I used, compile times ended up at roughly <strong>5<\/strong> seconds.<\/p>\n<p>So what happens if we move the compile-time parameters to run time? Since <code>registeredName<\/code> is a template value parameter, we can just move it into the function parameter list with no change. We have to handle the <code>func<\/code> parameter differently. Almost any symbol can bind at compile time to a template alias parameter, but symbols can&#8217;t bind at run time to function parameters. We have to use a function pointer instead. In that case, we can use the type of the referenced function as a template parameter.<\/p>\n<pre class=\"prettyprint lang-d\">void register(FuncType)(Context x, FuncType ptr, string registeredName);<\/pre>\n<p>With this signature, the compile time drops to roughly <strong>1<\/strong> second.<\/p>\n<h3 id=\"whatsgoingon\">What&#8217;s going on?<\/h3>\n<p>D is a fairly fast language to compile. Good decisions have been made over the lifetime of the language to make that possible. It is also the case that one can happily write slow-to-compile D code. Although we are choosing to ignore the compilation speeds of the non-infrastructure code to simplify the point being made, this can actually (in a certain sense) be the case in real projects, too. As such it is worthwhile to pay attention not to the quantity of metaprogramming being done <em>semantically<\/em> but rather the quantity of metaprogramming being performed <em>by the compiler<\/em>.<\/p>\n<p>In this case, with the first interface used for <code>register<\/code>, the compiler had no opportunity to reuse any instantiations. Because it accepted the registered functions as symbols, each instantiation was unique. By shifting instead to take the <em>type<\/em> of the registered function as a template parameter and a pointer to the function as a function parameter, the compiler could reuse instantiations. <code>stub1<\/code>, <code>stub2<\/code>, and <code>stub3<\/code> are distinct symbols, but they each have the same type of (<code>int function(string)<\/code>).<\/p>\n<p>To be clear, this is not an indictment of template alias parameters. There are good use cases for them (the Phobos algorithms API is an example). The point of this example is to show how the compile-time costs of unique template instantiations can be hidden. A decision about the trade-offs between compile-time and run-time performance can only be made if the programmer is aware there is a decision to make. So when implementing a template, consider how it will be used. If it&#8217;s going to end up creating many unique instantiations, then you can weigh the benefits of keeping that interface versus redesigning it to maximize reuse.<\/p>\n<h2 id=\"afalsefriend\">A false friend<\/h2>\n<p>In linguistics, <a href=\"https:\/\/en.wikipedia.org\/wiki\/False_friend\">a false friend<\/a> is a pair of words from two different languages that look the same but have different meanings. I&#8217;m going to abuse this term by using it to refer to a pair of programming patterns that actually result in the same program behavior, but via different routes through the language implementation, i.e., one of these patterns has, say, worse performance or compile times than the other.<\/p>\n<h3 id=\"asimpleexample:\">A simple example:<\/h3>\n<p>Let&#8217;s say we have a library that exposes a template as part of its API, like this one that prints a string when a module is loaded during run-time initialization:<\/p>\n<pre class=\"prettyprint lang-d\">template FunTemplate(string op)\n{\n  shared static this()\n  {\n    import std.stdio;\n    writeln(op);\n  }\n}\n\n\/\/ Use like this\nmixin FunTemplate!&quot;Hello from DLang&quot;;<\/pre>\n<p>Now let&#8217;s say we want to refactor the library in some way that it&#8217;s desirable to distinguish the name <code>FunTemplate<\/code> and its implementation. How would you go about doing that?<\/p>\n<p>One way would be to tack <code>Impl<\/code> onto the implementation name, then declare an eponymous template that aliases the shortened name to the implementation name and forwards the template argument like this:<\/p>\n<pre class=\"prettyprint lang-d\">template FunTemplate(string op)\n{\n  alias FunTemplate = FunTemplateImpl!op;\n}<\/pre>\n<p>This does the job, but it also creates an additional instantiation for each different value of <code>op<\/code>, one instance of <code>FunTemplate<\/code>, and one of <code>FunTemplateImpl<\/code>. So if we instantiate it with, e.g., five different values, we end up with ten unique instantiations. Now imagine doing that with a template that&#8217;s heavily used throughout a program.<\/p>\n<p>Since we only want to provide an alternate name for the implementation and aren&#8217;t doing anything to the parameter list, we can achieve the same result without adding another template into the mix: just use <code>alias<\/code> by itself.<\/p>\n<pre class=\"prettyprint lang-d\">alias FunTemplate = FunTemplateImpl; <\/pre>\n<p>Since <code>FunTemplate<\/code> is no longer a template, <code>FunTemplate!&quot;Foo&quot;<\/code> only creates the one instance of <code>FunTemplateImpl<\/code>.<\/p>\n<h2 id=\"normalizationoftemplatearguments\">Normalization of template arguments<\/h2>\n<p>Once we know what we want a template to look like, and we&#8217;re satisfied with the interface we want it to have, there are sometimes subtle ways to separate the interface and implementation of a template such that we can minimize the total amount of work the compiler has to do.<\/p>\n<p>The definition of &#8220;work&#8221; in this context can be important to consider, as we can find ways to balance a tradeoff between compile times and the amount of object code generated for each instantiation. One technique to reduce these costs is by normalizing a given list of template arguments into something called a <em>canonical form<\/em>.<\/p>\n<h3 id=\"canonicalforms\">Canonical Forms<\/h3>\n<p>A canonical form, resulting from a process called canonicalization, is a mathematical structure that is intended to reduce multiple different-looking but identical objects into <em>one<\/em> form that we can then manipulate as we see fit. Using an automatic code formatter is an example of transforming input (in this case, source code) into a canonical form.<\/p>\n<h3 id=\"applicationtotemplates\">Application to templates<\/h3>\n<p>Consider a template like this one:<\/p>\n<pre class=\"prettyprint lang-d\">template Expensive(Args...)\n{\n  \/* Some kind of expensive metaprogramming or code generation *\/\n}<\/pre>\n<p>If we can think of a useful canonical form that isn&#8217;t too hard to compute, we can then write a second template <code>Reduce<\/code> to implement it, then inject it like in the following example.<\/p>\n<pre class=\"prettyprint lang-d\">template Expensive(Args...)\n{\n  \/\/ Reduce to some kind of canonical form\n  alias reduced = Reduce!(Args);\n\n  \/\/ Where ExpensiveImpl is the same as above but renamed\n  alias Expensive = ExpensiveImpl!(reduced);\n}<\/pre>\n<p>To be worth doing, <code>ExpensiveImpl<\/code> must be <em>significantly<\/em> more expensive than the reduction operation (pay attention to differences in <code>sys<\/code> time when measuring this), where &#8220;significant&#8221; is meant statistically rather than informally, i.e., any win is good as long as you can rigorously prove it&#8217;s real.<\/p>\n<h3 id=\"anexample:sortingtemplatearguments\">An example: sorting template arguments<\/h3>\n<p>Take a templated aggregate like this:<\/p>\n<pre class=\"prettyprint lang-d\">struct ExposeMethods(Types...)\n{\n  \/* Some kind of internal state dependant on Types but not their order *\/\n\n  static foreach(Type; Types) {\n    bool test(Type x) { \n      \/* Something slow to compile depending on Type *\/\n    }\n  }\n}<\/pre>\n<p>If it&#8217;s instantiated with, e.g., five different types across a large codebase, we could spend a lot of time redoing semantically identical compilation. If all possible types are used as input many times, we could end up with a few permutations, and if not we will probably get a few identical subsets.<\/p>\n<p>A canonical form that might come to mind (i.e., a potential definition of <code>Reduce<\/code>) is simply sorting the arguments by their names. This can be achieved <a href=\"https:\/\/dlang.org\/library\/std\/meta\/static_sort.html\">via the use of <code>staticSort<\/code><\/a>.<\/p>\n<h2 id=\"conclusion\">Conclusion<\/h2>\n<p>D has powerful metaprogramming and code generation features. But like anything in programming, their use isn&#8217;t free. If you want to avoid the situation where you find yourself making coffee while your project builds, then it&#8217;s imperative to be aware of the cost vs. benefits of the metaprogramming features you use. Then you can make intelligent decisions about your compile-time interfaces and implementations.<\/p>\n<h2 id=\"appendix-tracingthedcompilertocounttemplateinstantiations\">Appendix &#8211; Tracing the D compiler to count template instantiations<\/h2>\n<p>Here&#8217;s a simple lesson in Linux userspace tracing: you can use a tool like <code>bpftrace<\/code> or <code>DTrace<\/code> to spy on the D compiler compiling other things, so we can get basic figures about the compilation of other D programs without either hacking the compiler or changing their build process.<\/p>\n<p>You&#8217;ll need a <code>bpftrace<\/code> file like the following (saved as e.g., <code>main.bt<\/code>):<\/p>\n<pre>BEGIN\n{\n  printf(\"Tracing a D file\\n\");\n}\nuprobe:\/home\/mhh\/dlang\/dmd-2.097.0\/linux\/bin64\/dmd:_Dmain\n{\n  printf(\"This is the main\\n\");\n}\nuprobe:\/home\/mhh\/dlang\/dmd-2.097.0\/linux\/bin64\/dmd:_D3dmd10dsymbolsem24templateInstanceSemanticFCQBs9dtemplate16TemplateInstancePSQCz6dscope5ScopePSQDr4root5array__T5ArrayTCQEq10expression10ExpressionZQBkZv\n{\n  \/\/We do nothing with the knowledge here but if you write some code you can get info about the templates relatively easily\n  printf(\"Instantiating a template\\n\");\n}<\/pre>\n<p>What am I hooking here? That big mangled name in the middle of the script is <code>templateInstanceSemantic<\/code> in the <code>dsymbolsem<\/code> module in the DMD source. By hooking it, we can get a rough idea of when a template is being worked on.<\/p>\n<p>Running it with <code>sudo bpftrace main.bt<\/code> (eBPF tracing currently requires root) when building DMD, for example, I see there are about 50,800 template hits.<\/p>\n<p>You can use a more complicated script in a system like <code>bcc<\/code> to reconstruct the compiler&#8217;s internal data structures. With that, we can get output a bit more like <code>09:05:17 69310 b'\/home\/mhh\/d_dev\/dmd\/src\/dmd\/errors.d:85'<\/code> and actually reconstruct the source\/line info (alongside a timestamp and PID).<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Templates have been enormously profitable for the D programming language. They allow the programmer to generate efficient and correct code at compile time. Long gone are the days of preprocessor macros or handwritten, per-type data structures. D templates, though designed in the shadow of C++ templates, were not made in their image. D makes templates [&hellip;]<\/p>\n","protected":false},"author":45,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[26,30],"tags":[],"_links":{"self":[{"href":"https:\/\/dlang.org\/blog\/wp-json\/wp\/v2\/posts\/3068"}],"collection":[{"href":"https:\/\/dlang.org\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/dlang.org\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/dlang.org\/blog\/wp-json\/wp\/v2\/users\/45"}],"replies":[{"embeddable":true,"href":"https:\/\/dlang.org\/blog\/wp-json\/wp\/v2\/comments?post=3068"}],"version-history":[{"count":5,"href":"https:\/\/dlang.org\/blog\/wp-json\/wp\/v2\/posts\/3068\/revisions"}],"predecessor-version":[{"id":3073,"href":"https:\/\/dlang.org\/blog\/wp-json\/wp\/v2\/posts\/3068\/revisions\/3073"}],"wp:attachment":[{"href":"https:\/\/dlang.org\/blog\/wp-json\/wp\/v2\/media?parent=3068"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dlang.org\/blog\/wp-json\/wp\/v2\/categories?post=3068"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dlang.org\/blog\/wp-json\/wp\/v2\/tags?post=3068"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}