Hacker News new | ask | show | jobs
by chrisseaton 2130 days ago
> Next challenge: teach the optimizer to make that almost as fast as the min/max way ;-)

I did exactly this for my PhD!

https://chrisseaton.com/phd/

4 comments

I'm glad things like this are being worked on. I have been writing a set of implementations of the nBody benchmark in JavaScript using various forms of abstractions. In an ideal world they should all take the same speed since they perform the same fundamental task and produce the same result. They just represent different scales of optimization effort.

It's interesting seeing the difference between vectors in arrays vs objects and if you do immutable versions. The trickiest to optimize form is using a micro vector library which uses closures and array map().

    var vop = op => ((a, b) => (a.map((v, i) => op(v, b[i]))));
    var vdiff = vop((a, b) => a - b);
    var vequals = (a, b) => {  return (vdiff(a, b).reduce((c, d) => c + Math.abs(d), 0)) === 0;};
    var vadd = vop((a, b) => a + b);
    var vdot = (a, b) => a.reduce((ac, av, i) => ac += av * b[i], 0);
    var vlength = a => Math.sqrt(vdot(a, a));
    var vscale = (a, b) => a.map(v => v * b);
    var vdistance = (a, b) => vlength(vdiff(a, b));
Currently on my lowly atom laptop and FireFox. The version using mutable objects is ten times faster than the mapping immutable array version. I live in hope that one day there will be an optimizer that turns

    var transpose = matrix => (matrix.reduce(($, row) => row.map((_, i) => [...($[i] || []), row[i]]), []));
    var multiply = (a, b, ...rest) => (!b) ? a : multiply(a.map((p => transpose(b).map(q => vdot(p, q)))), ...rest);
Into a CPU optimum (or dare I suggest GPU) version of a matrix multiply.
For those reading, this work went into TruffleRuby, which implements Ruby on top of Truffle/GraalVM.
Very noble, but I'm assuming (hoping) the technique will be more generally applicable than Ruby?
Wait, how is that not trivial?
> how is that not trivial?

It could be trivial to implement an optimisation which does this for that exact code. But what are you going to do? Hand-code an optimisation for every similar thing people could write? I implemented a general solution.

So it also works through metaprogramming:

    [1, 2, 3].send(:sort).send(:[], 1)
Through user-defined sorting order:

    [1, 2, 3].sort_by { |a, b| b <=> a }[1]
When nested:

    [[1, 2].sort[1], 3].sort[0]
And so on.

Note that it also needs to be transparent to debuggers and profilers, it needs to handle multiple method redefinitions (for example what happens if someone redefines the sorting order for integers).

It's not a pattern-matching optimization - it's partial evaluation enabled by a new kind of polymorphic inline cache.

> But what are you going to do? Hand-code an optimisation for every similar thing people could write?

While I appreciate that you solved the general problem, I wonder if there are legs here. Specifically, could one mine GitHub to find automatically common patterns that one could write specific optimizers for, or at minimum, leverage that to learn what semi-general cases are worth optimizing? To my knowledge optimizing compilers already do have effectively handlers for common operations, but I don’t know if anyone has leveraged “big data” to help guide this.

IIRC, various tweaks and optimizations in Java were guided by Sun analyzing their own code based. GitHub is just so much bigger, and polyglot.

It's not just a hardcoded optimization for that construct.