if (D == D_prev) {
if (L == 0) {
*q++ = 0xF0 + (x + 3); // XM!
} else {
*q++ = (L << 6) + (x << 3) + 6; // LLxxx110
}
*(uint32_t *)q = literal;
q += L; // non-aligned access OK
} else if (D < 2048 - 2 * 256) {
// Short dist D>>8 in 0..5
*q++ = (D >> 8) + (L << 6) + (x << 3); // LLxxxDDD
*q++ = D & 0xFF;
*(uint32_t *)q = literal;
q += L; // non-aligned access OK
} else if (D >= (1 << 14) || M == 0 || (x + 3) + M > 34) {
// Long dist
*q++ = (L << 6) + (x << 3) + 7;
*(uint16_t *)q = D;
q += 2; // non-aligned access OK
*(uint32_t *)q = literal;
q += L; // non-aligned access OK
} else {
// Medium distance
x += M;
M = 0;
*q++ = 0xA0 + (x >> 2) + (L << 3);
*(uint16_t *)q = D << 2 | (x & 3);
q += 2; // non-aligned access OK
*(uint32_t *)q = literal;
q += L; // non-aligned access OK
}It looks just like the style of code in all the other fast LZ codebases. They are all in this style.
The "non-aligned access OK" comment litter is presumably to silence an LLVM performance sanitizer.
When you look at the code, you use the paper that describes the algorithm as documentation. Using same short one letter variable names in the code and paper makes understanding much easier.
The thing I hate most is when the the paper uses 1-based numbering and the programming language uses 0-based numbering. We should settle for 0-based numbering when describing algorithms.
Look at eg https://www.cs.ox.ac.uk/jeremy.gibbons/publications/arith.pd... to see a cleaner alternative.
(This is about describing algorithms in papers. Optimizing for performance after the big-O has been taken care of is a different matter.)
*(uint16_t *)q = D;
q += 2; // non-aligned access OK
*(uint32_t *)q = literal;
instead of memcpy(q, &D, sizeof(uint16_t));
q += 2; // non-aligned access OK
memcpy(q, &literal, sizeof(uint32_t));
which is better defined behavior (i.e. doesn't violate -fstrict-aliasing) and possibly faster.But I could also say that I'm lazy and it's not a big deal anyway.
I'm assuming they came up with the mathematical proofs first and translated that into code, so that has something to do with it, correct?
It looks a lot like some crypto algorithms which are a nearly direct translation of the mathematical formulas.
It's not that it's incredibly difficult to follow, but it's just very "math like".
I recently needed an implementation of the Simplex Noise algorithm (that I could port to Common Lisp). I ended up using this one, which works but the code certainly does nothing to help understanding: https://github.com/josephg/noisejs/blob/master/perlin.js
Note that the Javscript implementation is also a port from another language (whose implementation I have failed to find).
I think it's because in that circle of heavy math coding, there are a different set of well-understood abstractions and shortcuts.
It's no different than saying front-end web developers are not writing readable code because they use $(...) instead of elementMatchingSelector(...) or use functions like xhr() instead of xmlHTTPrequest(). Node.js developers don't think twice about the mechanics of callbacks nor to Erlang developers have any mental block about async message semantics.
Each field has a lingo that has evolved over time, and those who have been in a field for longer tend to make more shortcuts because they are manipulating a concept for the 100th time and are well versed in it.
Sorry for not referencing the original code. The java version I translated from is here: http://webstaff.itn.liu.se/~stegu/simplexnoise/SimplexNoise....
And the paper describing the algorithm is here: http://webstaff.itn.liu.se/~stegu/simplexnoise/simplexnoise....
Glad it was useful to you!
I used it as a base for a map generator for a strategy game. Thanks a lot for the code, it worked perfectly.
Performance was totally unchanged either way. Looks like V8's optimizer eats those vars for breakfast.
var x = a + b;
call(x + c);
and call(a + b + c);
would be literally indistinguishable.And I was a comp sci major first and a physics major second. You spend over a decade doing math with single letter variables. Hard habit to break I guess.
Of course it technically is programming don't get me wrong but it isn't "make a CRUD app with a simple UI" kind of programming.
D clearly refers to some form of "distance". M is "Medium". L is defined before this snippet of code, but I'd imagine it maps to a concept of Long.
And you're left with the variable 'q'.
The real issue is that unless you are comfortable, code involving pointer math can get confusing (all the * and + can be confusing, especially since they are such overloaded symbols in C). The single character variable names (especially when we're talking about what is basically code representation of mathematical formulae) is hardly a huge issue.
C just tends to look like line noise for numerical algorithms sometimes.
} else if (D < 2048 - 2 * 256) {
but if it were grouped with a few more parenthesis it would take a little less time for me to grok it.I think I'll stick with zlib.
(If you want to be glib, complain about misplaced FP perhaps?)
What am I missing?
This doesn't take many cycles, but it does take some. While a GOTO is just a jump.
In GCC/MSVC will only (attempt to) inline what you mark as inline. Then MSVC has a keyword which forces inlining. Unless you set a flag which tells the compiler to inline what ever it wants. But that being said Microsoft has a non-POSIX x64 ABI designed to allow better in-lining.
How inlining works starts to dive pretty deep into the compiler rabbit hole.
Interprocedural register allocation can work better than inlining too, because it keeps the code size smaller, and direct calls have almost no speed penalty.
[0]: https://gcc.gnu.org/onlinedocs/gcc/Inline.html [1]: https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html
Not sure why.
But might be something related, eg like they are talking about a state machine in the description somewhere, and give at most one return per state, or something like that?