Skip to content

Commit

Permalink
build: try tcc as our C compiler, not gcc (#67)
Browse files Browse the repository at this point in the history
Let's try `tcc` as our C compiler, at least for the start of the
Exercism v3 beta period. It has much faster compile times than `gcc`,
and the C compilation time is a critical factor in maximizing
responsiveness when running the tests via the online editor. This commit
probably reduces the compilation time of the average solution by between
1 and 5 seconds.

Comparing some local compilation times on a typical laptop:

| File                 | Compilation time, gcc | Compilation time, tcc |
| -------------------- | --------------------- | --------------------- |
| hello_world.nim      |                 0.6 s |                 0.2 s |
| gigasecond.nim       |                 1.0 s |                 0.6 s |
| meetup.nim           |                 1.0 s |                 0.6 s |
| test_hello_world.nim |                 2.0 s |                 0.9 s |
| test_gigasecond.nim  |                 2.2 s |                 0.9 s |
| test_meetup.nim      |                 4.8 s |                 1.2 s |

Each timing is the wall-clock running time of a command that begins
with, respectively:
- `nim c -f --cc:gcc`
- `nim c -f --cc:tcc`
The `-f` option means that no compilation cache is used.

As a bonus, the image is much smaller too.

Image size before this commit: 87.48 MB
Image size with this commit:   26.74 MB

This (uncompressed) size is getting pretty close to the theoretical
minimum for a Nim image, given that we use approximately:
- 5 MB for the Alpine parent image
- 5 MB for the Nim compiler
- 5 MB for the Nim standard library
- 10 MB for musl libc
- 1 MB in total for PCRE and tcc

`tcc` is good for non-optimized builds, which is exactly our use case.
Generally it works well, especially for simple programs like Exercism
solutions. But in the long-term, Nim will only fully support GCC, Clang
and MSVC.

We might want to use `tcc` until Nim gains incremental compilation. But
if we run into problems, we can always just revert this commit before
the release of Exercism v3.

In the future we could:
- Add a pre-built `nimcache` into the image, and make the compiler use
  that when compiling every user's solution.
- If we keep using `tcc`: build it from the `mob` branch, rather than
  using the latest release (which is a few years old).

In the long-term, it should be possible for a good Nim solution to run
near-instantly from the online editor.

The largest files in the image:
$ find / -type f -exec du -k {} + | sort -nr | head -n20 | cut -f2 | xargs du -sh
9.4M     /usr/lib/libc.a
4.6M     /nim/bin/nim
2.5M     /lib/libcrypto.so.1.1
808.0K   /bin/busybox
592.0K   /lib/ld-musl-x86_64.so.1
512.0K   /lib/libssl.so.1.1
364.0K   /usr/lib/libpcre.so.1.2.12
356.0K   /usr/lib/libtcc.a
232.0K   /nim/lib/pure/unidecode/unidecode.dat
212.0K   /etc/ssl/certs/ca-certificates.crt
184.0K   /usr/bin/tcc
180.0K   /lib/libapk.so.3.12.0
164.0K   /opt/test-runner/bin/runner
120.0K   /nim/lib/pure/os.nim
108.0K   /nim/lib/system.nim
100.0K   /nim/lib/pure/strutils.nim
100.0K   /lib/libz.so.1.2.11
96.0K    /usr/lib/libtls-standalone.so.1.0.0
96.0K    /nim/lib/pure/times.nim
96.0K    /nim/lib/pure/collections/tables.nim

The final disk usage in the root directory:
$ du -k -d1 / | sort -nr | cut -f2 | xargs du -sh
28.3M    /
12.5M    /usr
9.9M     /nim
3.9M     /lib
1.1M     /bin
428.0K   /etc
324.0K   /sbin
172.0K   /opt
12.0K    /var
0        /tmp
0        /srv
0        /run
0        /root
0        /mnt
0        /media
0        /home
  • Loading branch information
ee7 authored Apr 27, 2021
1 parent 282e388 commit 19b656d
Show file tree
Hide file tree
Showing 3 changed files with 10 additions and 8 deletions.
7 changes: 6 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ jobs:
sudo mv "nim-${NIM_VERSION}" "${INSTALL_DIR}"
echo "${INSTALL_DIR}/bin" >> "${GITHUB_PATH}"
- name: Install tcc
run: |
sudo apt-get update -y
sudo apt-get install -y tcc
- name: Compile and run `tests/trunner.nim`
run: "nim c -r --styleCheck:error --hint[Processing]:off tests/trunner.nim"

Expand Down Expand Up @@ -71,7 +76,7 @@ jobs:
run: |
# Create a container from the image we built, overriding the `ENTRYPOINT`
docker create --rm --entrypoint nim --name ntr "exercism/nim-test-runner:${NIM_VERSION}" \
c -r --styleCheck:error --hint[Processing]:off --hint[CC]:off -d:repoSolutions tests/trunner.nim
c --cc:tcc -r --styleCheck:error --hint[Processing]:off --hint[CC]:off -d:repoSolutions tests/trunner.nim
# Copy the required files and run the tests.
docker cp src/runner.nim ntr:/opt/test-runner/src/
docker cp tests/ ntr:/opt/test-runner/tests/
Expand Down
9 changes: 3 additions & 6 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,11 @@ FROM ${REPO}:${IMAGE}
COPY --from=nim_builder /nim/ /nim/
# hadolint ignore=DL3018
RUN apk add --no-cache \
gcc \
musl-dev \
pcre \
&& ln -s /nim/bin/nim /usr/local/bin/nim \
&& printf '\nRemoving some unneeded large files:\n' \
&& rm -v /usr/bin/lto-dump \
&& find / -path '/usr/libexec/gcc/x86_64-alpine-linux-musl/*/lto*' -exec rm -v {} + \
&& find / -path '/usr/lib/libgphobos.so*' -exec rm -v {} +
&& apk add --no-cache --repository https://dl-cdn.alpinelinux.org/alpine/edge/testing \
tcc \
&& ln -s /nim/bin/nim /usr/local/bin/nim
WORKDIR /opt/test-runner/
COPY --from=runner_builder /build/runner bin/
COPY bin/run.sh bin/
Expand Down
2 changes: 1 addition & 1 deletion src/runner.nim
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ proc writeOutput*(resultsFileName, runtimeOutput: string) =
proc run*(paths: Paths): tuple[output: string, exitCode: int] =
## Compiles and runs the file in `paths.tmpTest`. Returns its exit code and
## the run-time output (which is empty if compilation fails).
let (compMsgs, exitCode1) = execCmdEx("nim c --styleCheck:hint " &
let (compMsgs, exitCode1) = execCmdEx("nim c --cc:tcc --styleCheck:hint " &
"--skipUserCfg:on --verbosity:0 " &
"--hint[Processing]:off " &
paths.tmpTest)
Expand Down

0 comments on commit 19b656d

Please sign in to comment.