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.

Reply via email to