After I release a new version of ktistec, I build the server commit-by-commit to see which commits increase the server executable size and build time the most. I do this because I’ve learned that small implementation details (inlined code, small methods, using blocks) can have large impacts on these numbers.
Here's the output:
Commit Size Time
======== ========== ======= ===== =======
248850b1 36426264 10.3
47268073 36425688 -0.00% 10.5 +1.60%
344de272 36425688 +0.00% 10.8 +3.24%
ef561f52 36425944 +0.00% 10.8 -0.08%
8ae2cbd4 36429128 +0.01% 10.8 -0.01%
3e425f3b 36429128 +0.00% 10.8 +0.22%
1487d903 36427704 -0.00% 11.0 +1.42%
935c9ceb 36427016 -0.00% 11.0 +0.14%
de37dc6a 36427016 +0.00% 10.9 -0.97%
a660a326 36427016 +0.00% 10.8 -1.12%
ff3d990e 36427016 +0.00% 10.8 +0.54%
5724a58d 36523192 +0.26% 11.0 +1.78%
7b5057d4 36523640 +0.00% 11.0 -0.44%
30ca6a3f 36541352 +0.05% 11.6 +5.73%
e2327eea 36671592 +0.36% 11.0 -5.36%
ad0d76eb 36671592 +0.00% 10.9 -0.48%
d388e74f 36671592 +0.00% 11.4 +4.59%
dacea7ad 36671592 +0.00% 11.0 -3.76%
03d5dfd8 36671592 +0.00% 10.8 -1.63%
79d9d89f 36671576 -0.00% 11.0 +1.82%
b65d292f 36792376 +0.33% 11.1 +0.95%
0ef53365 36808904 +0.04% 11.6 +4.88%
b3766e7b 36808904 +0.00% 11.1 -4.50%
56ba79ce 36825416 +0.04% 11.1 -0.50%
4824df58 36825736 +0.00% 11.1 +0.31%
c4705143 36837544 +0.03% 11.1 -0.03%
e3d37ef7 36837768 +0.00% 11.5 +3.52%
4509fa0d 36837768 +0.00% 11.0 -3.83%
0ff9237b 36837768 +0.00% 11.0 -0.55%
Overall, the server executable size increased by about 1.1% and the build time increased by about 6.8%. Maybe that's not too bad for a major feature, but let's dig in.
It's nice to see that three commits account for almost all of the increase in server executable size:
- 5724a58d Add `language` to `Object`.
2 files +19 loc - e2327eea Render `contentMap` on ActivityPub objects.
2 files +17 -1 loc - b65d292f Add translation actions to the objects controller.
1 file +35 loc
But, compare 5724a58d to 8ae2cbd4 (Add `language` to `Account`). It added +22 loc but didn't increase the server executable size as much.
In any case, I'll look at e2327eea first. I'd like to understand why this relatively small change adds 130,240 bytes to the server executable size!
The follow ups are here and here and here.
#ktistec #crystallang
Single user ActivityPub (https://www.w3.org/TR/activitypub/) server. - toddsundsted/ktistec
GitHub
When you optimize a Crystal program, pay attention to language features that inline code. For example, pay attention to how you use blocks (consequences here and here).Also pay attention to how you use macros. Macros, like ECR.embed
and Slang.embed
, inline code at the point where they are invoked. This can be powerful, because macros actually generate code—but, ten invocations later, you have ten copies of the code.
Here's a case of too many copies, but with a very happy ending...
Ktistec uses both ECR.embed
and Slang.embed
to generate web pages from views and partials. I wrote code to count the number of places Ktistec used embed
for each view and partial it renders. There's a long tail, but here are the big ones:
| src/views/layouts/default.html.ecr | 204 |
| src/views/partials/modals.html.slang | 204 |
| src/views/partials/header.html.slang | 204 |
| src/views/partials/footer.html.slang | 204 |
| src/views/pages/generic.html.slang | 155 |
| src/views/partials/object/label.html.slang | 36 |
| src/views/partials/object/content.html.slang | 36 |
| src/views/partials/collection.json.ecr | 28 |
| src/views/partials/thread.html.slang | 12 |
| src/views/partials/detail.html.slang | 12 |
| src/views/partials/object.html.slang | 12 |
| src/views/partials/actor-panel.html.slang | 11 |
| src/views/partials/object.json.ecr | 11 |
| src/views/partials/paginator.html.slang | 11 |
| src/views/objects/thread.json.ecr | 8 |
| src/views/partials/activity/label.html.slang | 6 |
| src/views/mentions/index.json.ecr | 6 |
| src/views/remote_follows/index.json.ecr | 6 |
| src/views/settings/settings.json.ecr | 6 |
| src/views/tags/index.json.ecr | 6 |
| src/views/activities/activity.json.ecr | 5 |
| src/views/partials/editor.html.slang | 5 |
| src/views/objects/object.json.ecr | 5 |
| src/views/actors/remote.json.ecr | 4 |
...
The layout is part of every page and is rendered with every view, so lots of copies. Every page has a header and a footer (and some default modal dialogs) so you get those, too. The generic view is a little less obvious. It's used to render pages for which there is no more specific view—typically pages served for 400 Bad Request or 401 Unauthorized. Objects (posts) are rendered in a variety of contexts, so it's no surprise label.html.slang
and content.html.slang
are popular.
ECR.embed
and Slang.embed
inline templates at the point where they are invoked, but beyond that they don't really customize the generated code—they just duplicate it. What we want is one function for each view or partial, which wraps embed
and returns JSON or HTML.
Those changes mostly occur in commits from 399287cf to 4b025f50. To say that they made a huge difference is a gross understatement. Executable size decreased by ~13%. Build time decreased by ~50%, and the memory required to build decreased by ~30%.
#ktistec #crystallang #optimization
Crystal is a general-purpose, object-oriented programming language. With syntax inspired by Ruby, it’s a compiled language with static type-checking. Types are resolved by an advanced type inference algorithm.
Crystal Language