Help optimize D solution to phone encoding problem: extremely slow performace.

Renato renato at athaydes.com
Sun Jan 14 10:43:12 UTC 2024


On Saturday, 13 January 2024 at 23:20:32 UTC, Sergey wrote:
> On Saturday, 13 January 2024 at 19:35:57 UTC, Renato wrote:
>> On Saturday, 13 January 2024 at 17:00:58 UTC, Anonymouse wrote:
>>> On Saturday, 13 January 2024 at 12:55:27 UTC, Renato wrote:
>>>> [...]
>> I will have to try it... I thought that `BigInt` was to blame 
>> for the slowness (from what I could read from the trace logs), 
>> but after replacing that with basically a byte array key (see 
>> [commit 
>> here](https://github.com/renatoathaydes/prechelt-phone-number-encoding/commit/0e9025b9aacdcfef5a2649be4cc82b9bc607fd6c)) it barely improved. It's still much slower than Common Lisp and very, very far from Java and Rust.
>
> In the repo is hard to find the proper version.
> I've checked the Rust from master branch and it looks a bit 
> different from D implementation..

I explicitly linked to the Rust implementation in my question:

>  the [Rust 
> solution](https://github.com/renatoathaydes/prechelt-phone-number-encoding/blob/fastest-implementations-print-or-count/src/rust/phone_encoder/src/main.rs) uses approx. the same algorithm as used in CL and D, but instead of BigInt, it uses a Vec<u8> as key

Can you be more specific about which part of the Rust 
implementation is not roughly equivalent to the D 
implementation?? There are obvious differences in style and 
syntax, but I hope that it's mostly equivalent algorithm-wise.

But to clarify: the branch where the fastest solutions are is 
called `fastest-implementations-print-or-count`.

The D branches with my alternative implementations are all called 
`dlang-*`.

>
> I would suggest to rewrite in the same way as Rust implemented.
> Probably you would like to try:
> * do not use BigInt from std. It could be quite slow. Try to 
> use GMP library from Dub instead

Right, but as I posted above, even using a byte-array just like 
in Rust, the performance was still very bad (but around 2x faster 
than using BigInt).

Also, using GMP is always suggested to me, but I can't accept 
that because my goal is not to use C libraries to achieve the 
fastest speed (which I could do by using C or FFI in any other 
language), it's to use D (and the other languages) to solve my 
problem in an idiomatic way.

I [ended up using 
`Int128`](https://github.com/renatoathaydes/prechelt-phone-number-encoding/commit/98dcbcf1c77d1ded3406cc03af9026e126df5b2d) (the problem description requires more than i64, but int128 is enough), which grealy improved performance over my D baseline AND on the byte-array solution.

> * don't do "idup" every time
> * instead of byLine, try byLineCopy

`idup` is necessary because the strings are stored in a Map after 
the iteration ends. Anyway, I replaced that with `byLineCopy` and 
it became sligthtly slower.

> * instead of "arr ~= data" try to use Appender 
> (https://dlang.org/library/std/array/appender.html)

I was worried about `~=` allocating too much, though IIUC it 
shouldn't be a problem the way I used it... in any case, I 
[replaced it with 
`Appender`](https://github.com/renatoathaydes/prechelt-phone-number-encoding/commit/f1364d0897f1f37882f1d39b92c16b84b1abdc31) and the performance was completely unchanged - which is good as I think it means I used `~=` correctly to avoid overallocation (or I messed up using `Appender` :D - pls have a look).

> * also you could try to use splitter 
> (https://dlang.org/library/std/algorithm/iteration/splitter.html) to lazily process each part of the data

This is not necessary because that would only affect the time 
spent loading the dictionary, which is the faster part of the 
problem... nearly all of the time is spent looking for solutions 
instead.

> * isLastDigit function has many checks, but I think it could be 
> implemented easier in a Rust way

The Rust solution uses sum types for that, but that had a very 
minor effect on the overall performance (though in Rust, that's 
"cleaner" to use anyway)... I know D does have SumType in the 
stdlib, but I thought it is not as "zero cost" as in Rust, and 
because both variants wrap a String, it's awkward to use that... 
so I instead tried using a struct like this:

```d
struct WordOrDigit {
     string value;
     bool isDigit;
}
```

You can check [the commit 
here](https://github.com/renatoathaydes/prechelt-phone-number-encoding/commit/0f6b4ce83373c46b14b5bb40c53bb0e2d0905e66).

This change made the code slightly slower.

> * also consider to use functions from Range (filter, map) as 
> you use it in Rust, instead of using for loops

Why would for loops be slower? Or you're just saying I should 
make the D code nicer?

Anyway, thanks for the suggestions!


On Sunday, 14 January 2024 at 08:33:24 UTC, Anonymouse wrote:
> On Saturday, 13 January 2024 at 23:20:32 UTC, Sergey wrote:
>> I would suggest to rewrite in the same way as Rust implemented.
>> Probably you would like to try:
>> [...]
>
> I would strongly argue for profiling first instead of 
> optimising based on conjecture. If you profile you have solid 
> evidence on what is actually slow. If you're very good at 
> analysing D, well-educated hypotheses *may* be enough, until 
> they suddenly aren't and you will have spent a lot of time on 
> the wrong problem.

Totally agree. I will spend some time on my Linux machine to see 
if I can get more useful profiling data.

## Current Performance

For now, with the int128 change from BigInt, the code is about 4x 
faster than before, but on larger problems, it's still scaling 
very badly compared to other languages (this suggests there's 
still some "exponential growth" going on, which should not happen 
as the problem is not exponential).

```
Proc,Memory(bytes),Time(ms)
===> ./rust
./rust,23252992,59
./rust,23420928,263
./rust,23412736,1025
./rust,7069696,8661
===> src/d/dencoder
src/d/dencoder,11268096,72
src/d/dencoder,23781376,938
src/d/dencoder,23818240,4443
src/d/dencoder,10723328,134703
```

On the bright side: D is using almost as little memory as Rust, 
which is orders of magnitude better than the other, usual GC 
languages.


More information about the Digitalmars-d-learn mailing list