std.compress

Andrei Alexandrescu SeeWebsiteForEmail at erdani.org
Wed Jun 5 10:36:06 PDT 2013


On 6/5/13 1:10 PM, Jonathan M Davis wrote:
> On Wednesday, June 05, 2013 07:49:22 H. S. Teoh wrote:
>> (Actually, std.algorithm currently sits at 11636 lines. I call BS on
>> whoever claims to be able to "skim over" std.algorithm and "get a feel
>> for how it works". Chances are your finger will get so tired of hitting
>> PgDn about 2000 lines into the file that you won't even look at the
>> rest. And most of the code is only superficially related to each other
>> -- about 20 functions into the file you'd have lost track of all sense
>> of how things fit together -- 'cos they *don't* really fit together!
>> It's the epitome of why we *should* move to smaller modules, rather than
>> the current giant monolithic ones.)
>
> There's a significant difference between a 10,000+ line file and a file that's
> less than 1000 lines. Walter's proposed's std.compress is less than 1000 lines
> long, and yet we're talking about splitting it up. That's going to lead to
> ridiculously small modules IMHO. There's a balance to be had here that's
> somewhere in between the two extremes.

Walter's algo is among the simplest around. I expect any of the 
industry-standard algos to be significantly larger, most likely each 
worth a module.

I think it's worth discussing the interface much more than the approach 
to modularization.

Walter's algo traffics in InputRange!ubyte and offers an 
InputRange!ubyte. That makes sense in some situations, but often 
trafficking one ubyte at a time may be not only inefficient but also the 
wrong granularity. Consider:

auto data = File("input.txt").byChunk().compress();

That won't work because byChunk deals in ubyte[], not ubyte. How do we 
fix this while keeping everybody efficient?

I talked to Walter and during this work he figured a lot of things about 
how ranges work and how they generate code. Turns out that the range 
equivalent of a tight loop is slightly less efficient with dmd because a 
range must keep its state together, which is harder to enregister than a 
bunch of automatic variables.

Right now we have joiner(), which given several ranges of T, offers a 
range of T. Developing along that idea, we should have two opposite 
functions: itemize() and collect().

itemize() takes a range of ranges of T and offers a range of T. For 
example, given a range of T[], offers a range of T.

collect() takes a range of T and offers a range of T[]. The number of 
items in each chunk can be a parameter.

With that in tow, we can set things up such that compress() and expand() 
traffic in ranges of ranges of ubyte (or simply ranges of ubyte[]), 
which ensures work at maximum speed. Then the matter of adapting to and 
fro ranges of ubyte is a simple matter of chaining a call to itemize() 
or collect().

Destroy.

I salute this code. It is concise, well engineered, well written, just 
as general as it needs, uses the right features in the right places, and 
does real work. A perfect example to follow. The only thing I'd add is 
this convenience function:

void putBits(bool[] bits...) {
     foreach (b; bits) putBit(b);
}

That reduces a bunch of code, and leaves room for future optimizations 
inside putBits.


Andrei


More information about the Digitalmars-d mailing list