This action will delete this post on this instance and on all federated instances, and it cannot be undone. Are you certain you want to delete this post?
This action will delete this post on this instance and on all federated instances, and it cannot be undone. Are you certain you want to delete this post?
This action will block this actor and hide all of their past and future posts. Are you certain you want to block this actor?
This action will block this object. Are you certain you want to block this object?
Are you sure you want to delete the OAuth client [Client Name]? This action cannot be undone and will revoke all access tokens for this client.
Are you sure you want to revoke the OAuth token [Token ID]? This action cannot be undone and will immediately revoke access for this token.
#optimization 4 hashtags

Crystal is fast because methods are monomorphized at compile time. In simple terms, that means that at compile time, a polymorphic method is replaced by one or more type-specific instantiations of that method. The following polymorphic code...
def plus(x, y) x + y end
...is effectively replaced by two methods—one that does integer addition if called with two integers, and one that does string concatenation if called with two strings.
This extends to inherited methods, which are implicitly also passed self. You can see this in action if you dump and inspect the symbols in a compiled program:
class FooBar
def self.foo
puts "#{self}.foo"
end
def bar
puts "#{self}.bar"
end
end
FooBar.foo
FooBar.new.bar
class Quux < FooBar
end
Quux.foo
Quux.new.barDumping the symbols, you see multiple instantiations of the methods foo and bar:
... _*FooBar#bar:Nil _*FooBar::foo:Nil _*FooBar@Object::to_s<String::Builder>:Nil _*FooBar@Reference#to_s<String::Builder>:Nil _*FooBar@Reference::new:FooBar _*Quux@FooBar#bar:Nil _*Quux@FooBar::foo:Nil _*Quux@Object::to_s<String::Builder>:Nil _*Quux@Reference#to_s<String::Builder>:Nil _*Quux@Reference::new:Quux ...
The optimizer in release builds is pretty good at cleaning up the obvious duplication. But during my optimization work on Ktistec, I found that a lot of duplicate code shows up anyway.
Most pernicious are weighty methods that don't depend on class or instance state (don't make explicit or implicit reference to self). As I blogged about earlier, this commit replaced calls to the inherited method map on subclasses with calls to the method map defined on the base class and reduced the executable size by ~5.8%. The code was identical and the optimizer could remove the unused duplicates.
So, as a general rule, if you intend to use inheritance, put utility code that doesn't reference the state or the methods on the class or instance in an adjacent utility class—as I eventually did with this commit.
(The full thread starts here.)

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%.

my current canary for build resource utilization is a low end cloud server. when builds start to fail it's time to optimize.
more on the last round of build optimizations for ktistec, shortly.

I replaced five indexes* on the relationships table with two**, improved query performance in at least one case, and cut the size of the database down by 11.4% (98MB).
Lessons (finally) learned:
#ktistec #sqlite #optimization
* The original five:
CREATE INDEX idx_relationships_type_from_iri_created_at
ON relationships (type ASC, from_iri ASC, created_at DESC);
CREATE INDEX idx_relationships_from_iri_created_at_type
ON relationships (from_iri ASC, created_at DESC, type ASC);
CREATE INDEX idx_relationships_type_to_iri
ON relationships (type ASC, to_iri ASC);
CREATE INDEX idx_relationships_to_iri_type
ON relationships (to_iri ASC, type ASC);
CREATE INDEX idx_relationships_type_id
ON relationships (type ASC, id ASC);
* The final two:
CREATE INDEX idx_relationships_type
ON relationships (type ASC);
CREATE INDEX idx_relationships_to_iri
ON relationships (to_iri ASC);