@sferik I really liked your talk! I liked that you talked about the language from a different angle than the usual "this is the syntax, this is the semantic", also showing why would anyone want to consider Crystal for their own projects and how they can reuse their Ruby knowledge.
These are just a few notes I made after watching the video. They are small corrections and notes, but there's no way you could have known this because it's not documented or at least it's not easy to find.
-
Mandelbrot and the HTTP server demo worked pretty well, but by default Crystal generates unoptimized binaries. To get an optimized binary use the
--release
flag:crystal build samples/mandelbrot.cr --release
. Unoptimized binaries are still reasonably fast, and that's why it's the default: so you can use crystal as if it were a dynamic language without an intermediate compilation step. Sometimes running the (LLVM) optimizations takes a while. -
The
browser
was broken in that particular release but it's now fixed, hopefully there won't be anything broken in your next presentation. I remember in the last one something was broken too :-) -
When you invoke a method with different types, the compiler generates one version for each type combination leading to the most efficient code. For example;
def plus(x, y) x + y end plus(1, 2) # generates plus<Int32, Int32>(x, y) plus("x", "y") # generates plus<String, String>(x, y)
The actual details are a bit more complex: if you pass a union type, the method is instantiated for that particular union type. That is, unless there are type restrictions, in which case the instantiations are split. In any case, we try to generate the most optimal code, while maybe leading to more generated code (which might eventually be inlined or optimized away anyway).
You can always do
crystal build test.cr --emit llvm-ir
, which will generate atest.ll
file with the LLVM code where you can see the method instantiations. It will be a big file, but searching "plus" in the above example will usually get you to what you want to see/show. For the above I can see this:%120 = call i32 @"*plus<Int32, Int32>:Int32"(i32 1, i32 2) %121 = call %String* @"*plus<String, String>:String"(%String* bitcast ({ i32, i32, i32, [2 x i8] }* @str22 to %String*), %String* bitcast ({ i32, i32, i32, [2 x i8] }* @str23 to %String*))
-
About Array optimizations knowing the types. First: in Ruby every VALUE occupies around 40 bytes, but all VALUEs occupy the same amount of memory. That means that indexing an Array is always O(1). However, an array of 20 integers will occupy 800 bytes while in Crystal it will occupy 80 bytes. That's a memory optimization Crystal can make. Second: if you perform a sum over the elements of an Array of integers, the compiler might optimize this because it knows they are all integers. There's no way Ruby can do that. Third: Crystal knows the size of union types very well, so an Array of unions can also be indexed in O(1). You can read more about the memory representation of types here.
Unfortunately the video seems to be marked as "private" right now, this is all I can remember for now.