cdktf compile optimization with swc

Time to read: 7 minutes

Laying the grounds works, there are a number of tools in analysis here:

  • cdktf - Allows for defining terraform infrastructure as code in Typescript, Python or Golang.
  • typescript - And in particular tsc, the cli that transpiles typescript to javascript.
  • swc - A new js/ts transpiler that is gaining traction rapidly, and recently even made its way into NextJS test suite, with awesome results here

I am interested in using the cdktf, with typescript, to enhance infrastructure as code declarations for ease of use.

Seeing large projects take on the challenge of changing their compiler made me think if it was worth it; what are the pros and cons.

Summary

Sample code can be found here: https://github.com/simonireilly/cdktf-swc

  • Using tsc --incremental is statistically faster than swc.
  • Using either swc or tsc --incremental will reduce time spent waiting by a factor of 5.

result chart

Trade-offs:

  • Pro - swc is fast on initial build
  • Pro - tsc --incremental is faster, after the first build
  • Con - swc does not produce type declarations

SWC vs. TSC

  • swc was on average 5.6 times faster than tsc for synth(esizing) the terraform cdk stack.
  • swc was on average 2.5 times faster than tsc for plan(ing) the terraform cdk stack.
Compiler Command Mean [s] Min [s] Max [s] Relative
swc yarn synth 4.9 ± 0.2 4.7 5.2 1.0
tsc yarn synth 28.0 ± 3.8 24.9 32.2 5.6 ± 0.8

TSC incremental vs. SWC

  • tsc --incremental was on average 1.1 times faster than swc for synth(esizing) the terraform cdk stack.
  • tsc --incremental was on average 1.2 times faster than swc for plan(ing) the terraform cdk stack.
Compiler Command Mean [s] Min [s] Max [s] Relative
swc yarn synth 5.098 ± 0.439 4.661 5.540 1.07 ± 0.12
tsc --incremental yarn synth 4.744 ± 0.341 4.360 5.008 1.00

Read on if you want to know about the implementation details

Setup

There is a guide here I followed for getting started with cdktf: https://learn.hashicorp.com/tutorials/terraform/cdktf-build?in=terraform/cdktf

This isn't really the subject of the blog, but it is a nice intro to get you started if you plan on using the cdktf.

Experiencing slow synth

I was doing some development on the train, and I was thinking, wow, this seems slow. When I discovered this was actually the compiler, not the network I was pretty shocked.

Digging under the hood there is actually some fairly good reasons this feels like a slow experience, and there are a number of solutions.

  1. The cdktf was setup to re-compile the code, including all files in the .gen folder, on every synth.
  2. The cdktf was setup with a tsconfig.json that wasn't using incremental builds (tsc keeps a build manifest to help it detect relevant changes and only recompile what is necessary).
  3. The cdktf was setup with a tsconfig.json that would produce type declarations (*.d.ts); which is super useful if you are distributing your code for use in js environments. If you are not, and you are just using typescript, you might not need these.

Solution 1

Just set incremental to true in the tsconfig.json.

I am not going to lie, this is the simplest, and biggest win for compile time, and improves the overall performance straight away.

{
  "compilerOptions": {
    "incremental": true,
  }
}

Solution 2

Since the initial build is still slow, another transpiler, swc, could be used to speed up builds:

yarn add --dev @swc/core @swc/cli
// .swcrc
{
  "jsc": {
    "parser": {
      "syntax": "typescript"
    },
    "target": "es2018"
  },
  "sourceMaps": true,
  "module": {
    "type": "commonjs",
    "strict": false,
    "noInterop": false,
    "strictMode": false
  }
}

Update the compile script to use swc:

{
  "scripts": {
    "compile": "swc main.ts --out-file dist/main.js & swc .gen/providers/**/*.ts -d dist/.gen/",
  }
}

Analysis

I used hyperfine on my local machine:

$ lshw -short
H/W path       Device     Class          Description
====================================================
                          system         Latitude 7490 (081C)
/0/4d/0                   memory         16GiB SODIMM DDR4 Synchronous Unbuffered (Unregistered) 2133 MHz (0.5 ns)
/0/4d/1                   memory         16GiB SODIMM DDR4 Synchronous Unbuffered (Unregistered) 2133 MHz (0.5 ns)
/0/54                     processor      Intel(R) Core(TM) i7-8650U CPU @ 1.90GHz

TSC --incremental vs. SWC

Testing against incremental, we allow for the build to be present before each run:

hyperfine 'COMMAND=compile yarn synth' \
  'COMMAND=build yarn synth' \
  --export-csv results/complete.csv

TSC vs. SWC

To test without incremental we perform cleaning of the dist directory between runs:

hyperfine 'COMMAND=compile yarn synth' \
  'COMMAND=build yarn synth' \
  --prepare 'yarn clean' \
  --export-csv results/complete.csv

Wrap-up

Thanks for reading the methods, hopefully this is useful analysis if you are considering implementing swc of tsc --incremental.