I’m sure, a similar question has been asked before, but I’m unable to dig it up. This SO question is somewhat related, though: Crystal-lang: why is the LLVM “hello.bc” not the same if generated by Crystal or by clang?
Looking at the prelude points in the right direction, but doesn’t answer this entirely. Requiring some source code doesn’t automatically end up in a larger executable size. Only code that is actually reachable from the program will be included in the codegen. So if you do require "some_large_dependency"
, it doesn’t blow up your binary size at once. It entirely depends which code is actually used.
The prelude essentially includes the core components of Crystal’s standard library. Not all components are used by each program, though. But quite a few components are required for every program in order to provide a functioning Crystal runtime.
The runtime includes essential features such as:
- process environment (standard file descriptors, argv)
- signal handling
- exception handling
- garbage collection
- concurrency runtime (main fiber, scheduler, threads)
(For further details, many of the entry points for these components can be found in src/kernel.cr
and src/crystal/main.cr
.)
The implementation of these runtime features already depends on many other core libraries from the prelude, such as the basic data types, collections etc.
In the end, most of the prelude is actually used by the runtime and thus in every Crystal program. So this code will always by included in the executable.
For an empty program, the Crystal runtime accounts for about 1.1MB (0,8MB without debug symbols). But that only covers the code generated from Crystal source.
The Crystal runtime features also depend on a few external libraries such as libgc, libpthread, libevent etc. Statically linked libraries also add to the binary size. These are currently libgc and libcrystal. The latter is only a tiny library for sigfault handling, but libgc comes in at about 0,4MB.
Combined, that results in a binary size of about 1.4MB for an empty Crystal program (1.1MB without debug symbols). That’s how much space a program needs to do nothing. Seems bloated, but in practice, this is not really relevant. Because a program that does something always needs most of these dependencies anyway, so it doesn’t add much overhead having them for the basic runtime. For example: When your program uses String#to_i
, that doesn’t increase the executable size because String#to_i
is already used by the runtime and included in the codegen anyway.