HN user

carlopi

37 karma
Posts5
Comments27
View on HN
DuckDB 0.9 3 years ago

Thanks, cool read, I took a couple of ideas, will see where we land.

DuckDB 0.9 3 years ago

I'd say find an angle that adds value for you (ideally both since you want the answer AND you can benefit from learning) random ideas can be: * C++: extension to interface with GitHub workflows results (say how many times job X passed) * JavaScript: browser-based library to visualize data * Python: crunch geospatial data (say your own GPS data) * ...

DuckDB 0.9 3 years ago

I just discovered Hacker News offers a JSON API, ducking awesome, if only there was a nice tool to crunch data as JSON files...

I think I have just found out yet another great DuckDB-weekend project!

Not sure whether they classify as obscure, but I haven't see cited already:

- Dominator tree (https://en.wikipedia.org/wiki/Dominator_(graph_theory))

- Single-Connected-Components-Graph

- Deterministic data structures (eg. a set that acts deterministic to the fact that addresses might be somehow randomly assigned, very useful for ensuring reproducibility)

Already cited, but it's clearly among the most elegant:

- union-find (!!!!)

and as a bonus one that is easily overlooked:

-std::deque, that when restricted to push_back() or push_front() guarantees not to ever move objects around.

I have to double check this, but the approach should be theoretically sound since we are quite strict with what functions are considered to have known call-sites: Function has to be internal (as in no visibility from outside) + no indirect uses (so no saving a pointer to the function somewhere). This should be enough to solve the problem you are thinking about.

Take this other example: https://gcc.godbolt.org/z/nebP68Tx8

Here by playing HumanCompiler it should be possible to prove that the if condition never evaluates to true, so removing the if is safe.

This is an example of optimization that PartialExecuter is able to do. Note that some combinations of other optimizations might also be potentially able to get to this result (say adding a tag "is always power of 2", but doing this in a general way it's what PartialExecuter does well).

Somehow similarly, clang might be instructed to enable a check to avoid including printf_float and somehow detect it and exclude it (this is what happens here), but this is hardly generalizable.

The step forward that I believe worked well here is that given some actual paths (and since they are simply a sequence of Instruction, they can be fed to an interpreter) they are merged or forked while entering or exiting SCCs, this allows to keep a low count of actual visit but exploring a big enough set of path to prove that it covers all possible executions.

On the second part, I have to do some thinking and coming back, thanks!

PartialExecuter happens at the LLVM's IR level, and in theory it's fully generic. Then has been only partially tested outside the Cheerp-pipeline, so I would expect it to rely on some implicit assumptions on the kind of legalizations or lowering that are done before-hand.

Target-wise, all information is kept encoded in the IR, so that part is easier (the only strange thing that might happens is bumping some alignment to 8, but I expect every target to be able to handle the prescribed IR alignemtn)

Thanks a lot!

I also believe it would be cool to upstream this, we will have to sit down and do some planning.

Compilation time could improve (I was actually working on this today), but it's already in line with other optimizations, taking < 10% of the time spent doing optimizations on a big codebase we use as benchmark.

Currently it's applied to all functions, since runtime it's anyhow somehow linear in the number of Instructions a Function has, but possibly in more costly versions of this (that we have on paper but yet to implement) some logic to filter functions in advance could be used.

WebAssembly implies static linking (malloc / printf & all are part of the shipped module) and code size matters since it directly influence users (since there might be delays in downloading big payloads). Both factors plays a role in deciding to plan putting work in optimizations like this.

There are some general gains to be had with this optimization, and probably the same ideas were already around for a while, but putting together in this way was helped by thinking about WebAssembly specific constraints.

A subset of this could possibly be applied at the wasm-opt level, but consider that at the LLVM's IR level there is more information to be leveraged (in particular PartialExecuter uses information on what memory ranges are read-only).

In general the whole concept behind Cheerp (C++ to WebAssembly + JavaScript compiler) is doing as much as possible at the LLVM IR level, JavaScript concept included, since it's easier and more powerful to do code transformation there.

Pure JS unrolling + bit manipulations lead to roughly 40% speed up, using this function:

  function add() {
    var i = 0, d0 = 0.0, d1 = 0.0, d2=0.0, d3=0.0;
    for (i = 0|0; i < N; i = ((i|0)+(4|0))|0) {
      d0 = a[i^0] + b[i^0];
      d1 = a[i^1] + b[i^1];
      d2 = a[i^2] + b[i^2];
      d3 = a[i^3] + b[i^3];
      c[i^0] = d0;
      c[i^1] = d1;
      c[i^2] = d2;
      c[i^3] = d3;
    }
  }
Could you try it & possibly adding this (possibly instead of the slower unrolling options?)

I am highly skeptical of any claim that anything can be 10x-100x faster that JavaScript. Do you have any concrete example where a similar program coded in (decently written) JS is that slower? I work on a compiler from C++ to JavaScript & WebAssembly (Cheerp), and I have basically never saw any such a difference.