Live data from Hacker News

Crystal 1.0 – What to expect

crystal-lang.org

311–320 of 351 posts

Re: Crystal 1.0 – What to expect

#311
post #150

Earlier quoted context omitted.

The cross-platform story of .NET hasn't been complicated in a while. It's fully cross-platform between Windows, Linux, and macOS and has been for a few years. Edit: I just saw the person that you replied to said the same thing, so I'm not sure why you're pushing that it has a complicated cross-platform story.

So... pretty much everything I could do on .net framework runs on Linux and Mac now? Even Winforms, or WPF? To be fair I haven't written .net code in like 10 years, and the docs explaining how to navigate the transition from framework to core were extremely confusing. Has that soup of different technologies solidified any?

> So... pretty much everything I could do on .net framework runs on Linux and Mac now?

No, which doesn't really matter since we're talking about .NET, not .NET Framework. Poor naming choice by Microsoft I guess.

Re: Crystal 1.0 – What to expect

#312

Earlier quoted context omitted.

What is the compilation speed like? EDIT: also forgot to ask if Crystal has a "killer app" yet? Ruby had Rails, Python had scipy/pandas(several others), Rust had servo etc.

Why is that important given Moores law and fast developer workstations? Everyone seems so preoccupied with compile speed for Crystal.

Well, I have ADHD. I've found the most effective approach (on top of treatment) that helps me retain focus is reexec-on-save, a la `while :; do tput clear; $thing; inotifywait -q -e moved_to .; done`. I usually have a dozen of those in old shell histories (^R FTW). (Ha, my laptop actually has exactly 12, and my other machine has 23 - although ignoredups is off...)

$thing might be `bash ./script.sh` (because my text editor's atomic rename doesn't understand execute bits >.>), `php script.php` or `gcc -O0 script.c && ./script`. (Also, as an aside I used to use `-e close_write $file` until I realized watching even giant directories is equivalently efficient to watching a file.)

Shell scripts (the small kind that run few subprocesses) are typically fast. Likewise, small C programs of But for better or worse, PHP is currently the language I use the most. Because it's faster than Python and Ruby.

A while back I wanted to do a bit of analysis on a dataset of information that was only published as a set of PDF documents... yayyy. But after timidly gunzipping the stream blocks and googling random bits of PDF's command language ("wat even is this"), I discovered to my complete surprise that it was trivial to interpret the text coordinate system and my first "haha let's see how bad this is" actually produced readable text on pretty much the first go. (To be pedantic, step #-1 was "draw little boxes where the text should be", step #0 was "how to x,y correctly" (with a side serving of "...those boxes look quite reasonably positioned..."), and step #1 was "replace boxes with texWHAT it worked?!")

With rendering basically... viable (in IIRC 300-500 LOC O.o), the next step was the boring stir-the-soup-for-144-hours bespoke state machine that cross-correlated text coordinates with field meanings ("okay, that's a heading, and the next text instruction draws the field value underneath. OK, assert that the heading is bold, the value is not, and they're both exactly the same (floating-point) Y position").

While that part took a while, it was mostly extremely easy, because I was pretty much linearly writing the script "from start to finish", ie just chipping away at the rock face of the task at hand until I processed an entire document, then the next document ("oh no"), then the next one ("ugh") and so forth ("wait, the edge cases are... decreasing? :D"). My workflow was pretty much founded entirely on the above-noted method where I would re-exec the script from scratch upon save.

Loading/gunzipping a given PDF and getting to the point where the little pipeline would crash would typically complete well before I had a chance to release the CTRL key after hitting CTRL+S. So while the process was objectively quite like stirring soup, it did not feel like that at all and I was able to kind of float a bit as my brain cohesively integrated the mental model of the architecture I was building without any distractions, pauses or forced context switches getting jammed in the mental encoding process like so many wrenches.

Soon 15 documents were handled correctly, then 20, then 30, then 100 ("oooh, if all the items on the page add up exactly right it pushes line 2 of the summary heading down to the second page! Hmmm... how on earth to special-case that without refactoring to look at more than 1 page at a time..."), and then I hit some sort of threshold and it suddenly just started ticking through PDFs like crazy without asserting. Which was both awesome and a Problem™: the thing ran at something like ~60 PDFs/sec, and while jumping to just after the last successfully-processed PDF on restart worked great when the code crashed constantly, now I was sitting spinning for tens of seconds, getting distracted as I anticipated the next crash. ADHD(R)(TM).

I wasn't surprised to learn from htop that the script was disk-bound; for some reason my ZFS mirror setup will happily read sequentially at 200MB/s, but thousands-of-tiny-files situations are... suffice to say apt unconditionally takes 60 seconds to install the smallest thing, unless the entire package db is in the FS cache. I'm not sure why. The PDFs were sharded sanely, but they were still in separate files. So I decided to pack them all into a giant blob, and since there weren't too many PDFs and they were numbered sequentially I used a simple offset-based index at the front of the blob where `fseek(data_start + ( * 4)); $o = fread(4); fseek($o);` would give me random seeking when I needed it, or I could just fseek() to data_start and start reading directly.

Reading the blob instead promptly pegged a single CPU core (yay!), and gave me IIRC ~150-200+ PDFs/sec. This was awesome. But I was still just a tiny bit curious, so after googling around for a profiler and having a small jawdrop moment about SPX (https://github.com/NoiseByNorthwest/php-spx), I had a tentative look at what was actually using the most CPU (via `SPX_ENABLED=1 php ./script.php`, which will automatically print a one-page profile trace to stdout at graceful exit or ^C).

Oh. The PDF stack machine interpreter is what's taking all the CPU time. That tiny 100 line function was the smallest in the whole script. lol

So, I moved that function to the preprocessor/packer, then (after some headscratching) serialized the array of tokenized commands/strings into the blob by prefixing commands with \xFF and strings with \xFF\xFE\xFF so I could explode() on \xFF and tell commands from strings by checking if the previous entry was \xFE (and just skip entries of '\xFE' when I found them) :D. Then I reran the preprocessor to regenerate the pack file.

  $ php convert_dlcache.php 
  Scanning...
  24/66060
  67,927 entries [4.27 sec]
  Sorting... 10013-343271 [1.61 sec]
  data_start=1373094
  * 67,920/67,927 99.99% 83/s 00:00 13:58 343259
Thankfully I've only needed to run the preprocessor rarely, like for example I only needed to run it twice today because it promptly truncated its pack file when I accidentally reran it after the first run (yay). Yes, it really does take ~14 minutes, and yes, there are just under 70,000 PDFs.

Then I reran the pipeline script.

  $ php conv2.php
  67,927/67,927 890/s 01:16 00:00 343271
  
  Complete
Oh. 900 PDFs/sec.

Uh, what happens if I... set up a simple pcntl_fork() + stream_socket_pair() multi-process worker system...?

  $ php conv2.php
  Reading... done
  4 workers, (16,982 x 3) + (16,981 x 1)
  62,162/67,927 1,800/s (469/s 166319, 444/s 237743, 442/s 302338, 444/s 340327) 00:34 00:03
  ^C
"Oh. Okay."

":D"

(The workers disappear from the output as they complete, so I ^C'd it just before they all exited, at 3 seconds left.)

So. 68,000 PDFs/sec on a not particularly amazing 3.3Ghz i3-3220 with 1600MHz RAM.

PHP 8's JIT is aweso--wait, is the JIT actually on?

Oh. It's off by default.

  $ php -dopcache.enable_cli=1 -dopcache.jit_buffer_size=32M conv2.php
  Reading... done
  4 workers, (16,982 x 3) + (16,981 x 1)
  58,206/67,927 2,537/s (669/s 163803, 623/s 230254, 619/s 300420, 627/s 338470) 00:22 00:03
*Blinks*

(In small voice) "I am processing/unit-testing 68k documents in 22 seconds. At over two and a half thousand PDFs a second."

I also noticed that

    PID USER      PRI  NI  VIRT   RES   SHR S CPU% MEM%   TIME+  Command
  31511 i336       20   0  276M 19864  5736 R 100.  0.2  0:18.25 php conv2.php
  31513 i336       20   0  276M 19856  5732 R 100.  0.2  0:18.35 php conv2.php
  31512 i336       20   0  276M 19728  5600 R 100.  0.2  0:18.32 php conv2.php
  31510 i336       20   0  276M 19764  5648 R 97.7  0.2  0:18.03 php conv2.php
  31509 i336       20   0  275M 34072 20036 S  0.7  0.4  0:00.19 php conv2.php
the workers are only using 20MB RAM each. This is with object deferencing going on, after I kinda started going crosseyed and properly moved my JBOGF (Just a Bunch of Globals and Functions) into a proper class (albeit a static one, since I'm still learning, and construction/deconstruction would probably slow things down too). I also note that with the JIT off, VIRT is 115M, RES is 16,312K and SHR is 2,412K for all workers, with the master taking 29M RES and 15M SHR.

--

Why'd I write all this?!

To make the point that a) live reexec on save is an awesome programming model when it can be applied, and b) PHP is my incredibly awkward gold standard reference, lol.

(In rerunning everything for this post I discovered the JIT and learned my script could go even faster!)

I've been keeping vague tabs on recent language developments from a bit of a distance for a while now, and am very interested to dig into Rust and Zig's incremental compilation capabilities at some point.

Based on what I'm hearing I don't know if I should just go dive in yet though - to be entirely honest, there are multiple realms of applications I simply cannot write in PHP - including things as simple as console CLIs, because the neccessary TTY I/O control (eg, turning TTY echo off and reading 1 char at a time) remain unavailable (see also: multiple years-old long-forgotten bugs/feature requests).

I don't really want to discover what's possible only to find myself between a rock and a hard place as my programs start to grow and I hit invisible quadratic brick walls that persist after I enable every "fastest possible" setting the language offers. That happened a few years ago when I tried to play around with FLTK in C++; once build times were around the 3-4 second mark (*with* -O0 and precompiled headers), regardless of the fact that I'd been working on the project for enough time I was invested in it, I just gave up.

Much more recently I installed .NET Core the other day to run and dissect a small F# program. Running `dotnet fsi` cold took 5 seconds to reach a REPL prompt (ouch), and while warmed-up reruns would only take 1 second, I found actually loading a ~300-line program would take a non-reducible ~4-5 seconds.

Everyone's different, and sometimes things people say can look crazy, or stupid, or "...how even...", but they can still be true for that person. For me, waiting 4 seconds for an interpreter/runtime/etc to reach a useful milestone such as "I can actually interact at all with it" is untenable. Yup. I go straight to "I've waited for this thing to do the thing 200 times today " after the 3rd pause. I am utterly incompatible with the Old Guard "code's compiling!" way of doing things. It puts me straight into defensive not-in-my-comfort-zone mode.

Completely open-minded about Crystal, which I've heard repeatedly good things about. Yes, of course, I haven't tested it (yet), again because I don't want to go "wow this is awesome... except I don't have the patience for it :(".

The status quo noted above remains an actively unsolved problem; trying to figure out how many interesting things I can wedge into the "focusable space" defined by "how big I can make my program before the programming languge slows down" is getting boring.

Perhaps I'm just stuck here because I'm using older(ish) hardware, and once I finally figure out that problem I'll suddenly realize all of this deep analysis was unnecessary. heh

Re: Crystal 1.0 – What to expect

#313

Earlier quoted context omitted.

No, Julia is not statically typed and will outperform pretty much anything, at least when dealing with numerical code. Next generation climate models are built with Julia. You would not pick a slow language for such a high performance dependent task.

Julia is statically typed, or at least it is for the inner bottleneck loops. If you want fast numeric code in Julia then you have to have homogeneous arrays. (Besides, I'd be very surprised if Julia doesn't have Fortran libraries driving it under the hood.) You're confusing dynamic typing and type inference.

https://julialang.org/

> Julia is dynamically typed, feels like a scripting language, and has good support for interactive use.

Literally on the front page.

Re: Crystal 1.0 – What to expect

#314

Earlier quoted context omitted.

Would a developer looking a Zig/C++/Rust really also consider Crystal? I figured Crystal was for Ruby developers who want a good static type system.

Ruby 3 does static typing, so that would be the quickest path for static types. Crystal is interesting to me because its a systems language, using Ruby syntax and idioms. I'm a Rails developer by trade but I've been pining to go back into desktop GUI development. Right now the only viable cross-platform options seem to be Swing and Electron. I'm not sure what the desktop story is with Crystal but if it there is a wor…

> Ruby 3 does static typing

Categorically, what I've seen of this falls closer to "lintable comments" than it does "actual static types". Static types are a foundational aspect of crystal, it's not like a sticker slapped on as an after thought.

Re: Crystal 1.0 – What to expect

#315

Earlier quoted context omitted.

Sure enough. Still wondering about why.

Also, this observation: in terms of developer time, compiled languages (a la Crystal, Go, Rust...) the disadvantage of compile time should be offset by much faster local test runs and even CI builds.

You'll usually just run a few tests if you are working on a specific feature and only run more of them when you think you are done. So most of the time there will be too little difference to make up for the increased compilation time.

Also, you are comparing compiled languages to interpreted ones, but the GP talked about differences between compilation time (i.e. compiled languages). And others are also comparing it to e.g. go (compiled but quick to compile).

Re: Crystal 1.0 – What to expect

#316
post #224

Earlier quoted context omitted.

Why would rubyists need elegance in a different language for perf? Do what everyone else does, and write a C extension.

The point is that they / we don't want to write c.

Speaking personally, I don't want to write in anything else but Ruby. If I have to code in something else, well, anything's fine. I don't want "elegance" in that language. I don't need other languages to try to be Ruby. I already have Ruby. I just want to get it done so I can build on that work... in Ruby.

I just don't understand why other people seem to think Ruby isn't enough.

Re: Crystal 1.0 – What to expect

#317

Earlier quoted context omitted.

Unfortunately no, it need some runtime (libevent, boehm gc) which is hard (impossible?) to integrate inside a shared lib.

Is this based on experience or just speculation? I have used Crystal to create shared libraries which I've called from various other languages (JS, Dart, Ruby, even C) and it worked well enough.

It's possible, just not officially supported nor straightforward. You need to run the GC yourself and all this kind of stuff.

https://stackoverflow.com/questions/32916684/can-a-crystal-l...

https://gist.github.com/Papierkorb/02d6ba53c28b5035a80bf7695...

Re: Crystal 1.0 – What to expect

#318

Earlier quoted context omitted.

No, Julia is not statically typed and will outperform pretty much anything, at least when dealing with numerical code. Next generation climate models are built with Julia. You would not pick a slow language for such a high performance dependent task.

Julia is statically typed, or at least it is for the inner bottleneck loops. If you want fast numeric code in Julia then you have to have homogeneous arrays. (Besides, I'd be very surprised if Julia doesn't have Fortran libraries driving it under the hood.) You're confusing dynamic typing and type inference.

Julia can also generate fast code for arrays of small unions

Re: Crystal 1.0 – What to expect

#319
post #272

Earlier quoted context omitted.

Julia

It got MIT backing, no?

But then you've to consider that many languages have been created in academic institutions, few have become semi-popular, fewer have entered the industry sector.

Re: Crystal 1.0 – What to expect

#320

Earlier quoted context omitted.

It’s blazingly fast for pure number / text crunching. As in: I use it to ingest terabytes of data files / JSON / logs at speeds indistinguishable from C but with the convenience of programming in something like ruby. Once replaced a python script while it was running. Expected time, python: 12 hours. Time to get it running in Crystal: 20 min. Time to finish in Crystal: 12 minutes.

> Once replaced a python script while it was running. Expected time, python: 12 hours. Time to get it running in Crystal: 20 min. Time to finish in Crystal: 12 minutes. Can you please elaborate on this?

These are the python scripts: https://github.com/jonadsimon/entendrepreneur/tree/master/pr...

I don’t do enough python to judge them. But, then again, I had even less experience in Crystal back when I worked on this.

Post reply on HN