Re: Why is this code slow?

2024-03-24 Thread Salih Dincer via Digitalmars-d-learn

On Sunday, 24 March 2024 at 22:16:06 UTC, Kdevel wrote:
The term containing the `pow` invocation computes the 
alternating sequence -1, 1, -1, ..., which can be replaced by 
e.g.


```d
   immutable int [2] sign = [-1, 1];
   n += sign [i & 1] / (i * 2.0 - 1.0);
```

This saves the expensive call to the pow function.


I also used this code:
```d
import std.stdio : writefln;
import std.datetime.stopwatch;

enum ITERATIONS = 1_000_000;
enum BENCHMARKS = 20;

auto leibniz(bool speed = true)(int iter) {
  double n = 1.0;

  static if(speed) const sign = [-1, 1];

  for(int i = 2; i < iter; i++) {
static if(speed) {
  const m = i << 1;
  n += sign [i & 1] / (m - 1.0);
} else {
  n += pow(-1, i - 1) / (i * 2.0 - 1.0);
}
  }
  return n * 4.0;
}

auto pow(F, G)(F x, G n) @nogc @trusted pure nothrow {
import std.traits : Unsigned, Unqual;

real p = 1.0, v = void;
Unsigned!(Unqual!G) m = n;

if(n < 0) {
if(n == -1) return 1 / x;
m = cast(typeof(m))(0 - n);
v = p / x;
} else {
switch(n) {
  case 0: return 1.0;
  case 1: return x;
  case 2: return x * x;
  default:
}
v = x;
}
while(true) {
if(m & 1) p *= v;
m >>= 1;
if(!m) break;
v *= v;
}
return p;
}

void main()
{
double result;
long total_time = 0;

for(int i = 0; i < BENCHMARKS; i++)
{
auto sw = StopWatch(AutoStart.no);
sw.start();

result = ITERATIONS.leibniz;//!false;

sw.stop();
total_time += sw.peek.total!"nsecs";
}

result.writefln!"%0.21f";
writefln("Avg execution time: %f\n", total_time / BENCHMARKS 
/ 1e9);

}
```

and results:


dmd -run "leibnizTest.d"
3.141594653593692054727
Avg execution time: 0.002005


If I compile with leibniz!false(ITERATIONS) the average execution 
time increases slightly:



Avg execution time: 0.044435


However, if you pay attention, it is not connected to an external 
library and a power function that works with integers is used. 
Normally the following function of the library should be called:


Unqual!(Largest!(F, G)) pow(F, G)(F x, G y) @nogc @trusted pure 
nothrow

if (isFloatingPoint!(F) && isFloatingPoint!(G))
...


Now, the person asking the question will ask why it is slow even 
though we use exactly the same codes in C; rightly. You may think 
that the more watermelon you carry in your arms, the slower you 
naturally become. I think the important thing is not to drop the 
watermelons :)


SDB@79


Re: Why is this code slow?

2024-03-24 Thread Sergey via Digitalmars-d-learn

On Sunday, 24 March 2024 at 22:16:06 UTC, rkompass wrote:
Are there some simple switches / settings to get a smaller 
binary?


1) If possible you can use "betterC" - to disable runtime
2) otherwise
```bash
--release --O3 --flto=full -fvisibility=hidden 
-defaultlib=phobos2-ldc-lto,druntime-ldc-lto -L=-dead_strip -L=-x 
-L=-S -L=-lz

```


Re: Why is this code slow?

2024-03-24 Thread rkompass via Digitalmars-d-learn
The term containing the `pow` invocation computes the 
alternating sequence -1, 1, -1, ..., which can be replaced by 
e.g.


```
   immutable int [2] sign = [-1, 1];
   n += sign [i & 1] / (i * 2.0 - 1.0);
```

This saves the expensive call to the pow function.


I used the loop:
```d
for (int i = 1; i < iter; i++)
n += ((i%2) ? -1.0 : 1.0) / (i * 2.0 + 1.0);
```
in both C and D, with gcc and gdc and got average execution times:

--- C -
original:     loop replacement:   -O2:
0.009989   0.003198 ... 0.001335

--- D -
original:  loop replacement:  -O2:
0.230346      0.003083   ...   0.001309

almost no difference.

But the D binary is much larger on my Linux:
 4600920 bytes instead of 15504 bytes for the C version.

Are there some simple switches / settings to get a smaller binary?


Re: Reworking the control flow for my tactical role-playing game

2024-03-24 Thread Liam McGillivray via Digitalmars-d-learn

On Saturday, 23 March 2024 at 04:32:29 UTC, harakim wrote:
* You should probably not do this, but it might give you some 
ideas for later. What I would do is make a separate thread for 
managing the UI state and push events to that thread through 
the mailbox. I have done this once (on my third version of a 
program) and it was by far the cleanest and something I was 
proud of. The benefit is you can publish those same events to a 
log and if something in your UI goes wrong, you can look at the 
log.


I was thinking about how I can have one thread doing the 
rendering and another doing everything else, given that the 
"everything else" thread would be idle most of the time. I 
thought about giving the "everything else" thread a queue; an 
array of functions that it's tasked with going over. Every UI 
interaction would add one to the queue.


It just occurred to me that this must be what you were suggesting 
here.


Re: Why is this code slow?

2024-03-24 Thread kdevel via Digitalmars-d-learn

On Sunday, 24 March 2024 at 19:31:19 UTC, Csaba wrote:
I know that benchmarks are always controversial and depend on a 
lot of factors. So far, I read that D performs very well in 
benchmarks, as well, if not better, as C.


I wrote a little program that approximates PI using the Leibniz 
formula. I implemented the same thing in C, D and Python, all 
of them execute 1,000,000 iterations 20 times and display the 
average time elapsed.


Here are the results:

C: 0.04s
Python: 0.33s
D: 0.73s

What the hell? D slower than Python? This cannot be real. I am 
sure I am making a mistake here. I'm sharing all 3 programs 
here:


C: https://pastebin.com/s7e2HFyL
D: https://pastebin.com/fuURdupc
Python: https://pastebin.com/zcXAkSEf


Usually you do not translate mathematical expressions directly 
into code:


```
   n += pow(-1.0, i - 1.0) / (i * 2.0 - 1.0);
```

The term containing the `pow` invocation computes the alternating 
sequence -1, 1, -1, ..., which can be replaced by e.g.


```
   immutable int [2] sign = [-1, 1];
   n += sign [i & 1] / (i * 2.0 - 1.0);
```

This saves the expensive call to the pow function.


Re: Why is this code slow?

2024-03-24 Thread Sergey via Digitalmars-d-learn

On Sunday, 24 March 2024 at 19:31:19 UTC, Csaba wrote:
As you can see the function that does the job is exactly the 
same in C and D.

Not really..

The speed of Leibniz algo is mostly the same. You can check the 
code in this benchmark for example: 
https://github.com/niklas-heer/speed-comparison


What you could fix in your code:
* you can use enum for BENCHMARKS and ITERATIONS
* use pow from core.stdc.math
* use sw.reset() in a loop

So the main part could look like this:
```d
auto sw = StopWatch(AutoStart.no);
sw.start();
foreach (i; 0..BENCHMARKS) {
result += leibniz(ITERATIONS);
total_time += sw.peek.total!"nsecs";
sw.reset();
}
sw.stop();
```


Re: Why is this code slow?

2024-03-24 Thread matheus via Digitalmars-d-learn

On Sunday, 24 March 2024 at 19:31:19 UTC, Csaba wrote:

...

Here are the results:

C: 0.04s
Python: 0.33s
D: 0.73s

...


I think a few things can be going on, but one way to go is trying 
using optimization flags like "-O2", and run again.


But anyway, looking through Assembly generated:

C: https://godbolt.org/z/45Kn1W93b
D: https://godbolt.org/z/Ghr3fqaTW

The Leibniz's function is very close each other, except for one 
thing, the "pow" function on D side. It's a template, maybe you 
should start from there, in fact I'd try the pow from C to see 
what happens.


Matheus.


Why is this code slow?

2024-03-24 Thread Csaba via Digitalmars-d-learn
I know that benchmarks are always controversial and depend on a 
lot of factors. So far, I read that D performs very well in 
benchmarks, as well, if not better, as C.


I wrote a little program that approximates PI using the Leibniz 
formula. I implemented the same thing in C, D and Python, all of 
them execute 1,000,000 iterations 20 times and display the 
average time elapsed.


Here are the results:

C: 0.04s
Python: 0.33s
D: 0.73s

What the hell? D slower than Python? This cannot be real. I am 
sure I am making a mistake here. I'm sharing all 3 programs here:


C: https://pastebin.com/s7e2HFyL
D: https://pastebin.com/fuURdupc
Python: https://pastebin.com/zcXAkSEf

As you can see the function that does the job is exactly the same 
in C and D.


Here are the compile/run commands used:

C: `gcc leibniz.c -lm -oleibc`
D: `gdc leibniz.d -frelease -oleibd`
Python: `python3 leibniz.py`

PS. my CPU is AMD A8-5500B and my OS is Ubuntu Linux, if that 
matters.


Re: Mutability issue

2024-03-24 Thread Menjanahary R. R. via Digitalmars-d-learn

On Saturday, 23 March 2024 at 20:49:14 UTC, Nick Treleaven wrote:
On Saturday, 23 March 2024 at 19:30:29 UTC, Menjanahary R. R. 
wrote:
for (T candidate = T(5); candidate * candidate <= n; 
candidate += T(6)) {


When T is `const int`, the above code declares and initializes 
a constant variable:

```d
const int candidate = const int(5);
```

Then, at the end of each loop iteration, it does:
```d
candidate += const int(6);
```

So you are trying to modify a constant. Constants can only be 
initialized, never assigned.


T candidate = (n % T(2) == T(0)) ? n + T(1) : n + T(2); // 
Start from next Odd


for (;; candidate += T(2)) { // Skip even


Same here, you declare a constant then try to assign to it at 
the end of each loop iteration.


Thanks for your prompt answer.


Re: Mutability issue

2024-03-24 Thread Menjanahary R. R. via Digitalmars-d-learn
On Saturday, 23 March 2024 at 20:38:40 UTC, Jonathan M Davis 
wrote:
On Saturday, March 23, 2024 1:30:29 PM MDT Menjanahary R. R. 
via Digitalmars- d-learn wrote:

[...]


Well, when nextPrime is instantiated, the type of T is inferred 
from the function argument. So, if num is int, then T is int, 
whereas if num is const int, then T is const int. The same with 
isPrime.


[...]


Thanks for your prompt answer. It works like a charm.

It's always a pleasure to learn from the Dlang community.



Re: Mutate immutable inside shared static constructor

2024-03-24 Thread Menjanahary R. R. via Digitalmars-d-learn

On Saturday, 23 March 2024 at 21:59:57 UTC, Nick Treleaven wrote:
On Saturday, 23 March 2024 at 21:53:43 UTC, Jonathan M Davis 
wrote:
Yes, it's a bug. It's a clear violation of the type system if 
a non-mutable variable is ever given a value more than once. 
It should be initialized, and then it should be treated as 
illegal to ever assign to it - or to do anything else which 
would mutate it. So, clearly, the logic in static constructors 
with regards to non-mutable variables is overly simple at the 
moment.


Thanks, filed:
https://issues.dlang.org/show_bug.cgi?id=24449


Thanks for your prompt answer.


Re: impure

2024-03-24 Thread Jonathan M Davis via Digitalmars-d-learn
On Sunday, March 24, 2024 1:41:41 AM MDT Dom DiSc via Digitalmars-d-learn 
wrote:
> I'm creating a library that is completely pure, but it doesn't
> compile with pure: at the top because of one impure unittest
> (which uses random to  test some things only probabilistic)!
>
> So do I really need to declare every function pure individually
> because of a test?!?
>
> Can we please have a @impure attribute?
> And by the way also @throws and @gc?
> That would make live so much easier...

It's been brought up a number of times before that it would be desirable to
have a way to negate attributes, and maybe we'll get that ability at some
point, but for now, we don't have it. The only attributes that can be
negated are @safe, @trusted, and @system, because using one of them directly
on a function overrides any others that are applied more globally. So, for
now, you cannot apply pure to an entire module and then have it not apply to
something within the module (though you could put that one test at the top
before you apply pure).

Another thing you could do would be to use debug {} to ignore attributes
within that block (though then that code will only be run when building with
-debug). How much sense that makes depends on what your test is doing, but
it is a way to get around pure in code that isn't intended to be used in
production.

All of that being said, I'd be inclined to argue that in general,
mass-applying attributes is asking for trouble. It works to a point, but it
makes it easy to forget which attributes apply, and in some cases,
attributes get ignored when they're mass-applied (though that's mostly on
types IIRC). It makes more sense when you're applying an attribute to the
entire module and not just a section of a module, but it does have a
tendency to become a maintenance problem - particularly when it's code that
more than one person works on. It also makes code harder to review, because
diffs won't include any of the attributes that are being mass-applied,
making it easy to miss the fact that a particular attribute applies to the
code being changed.

So, yes, you've run into a problem that it would be nice to have a better
fix for, but even if we could negate attributes in general, there are good
reasons to prefer to avoid mass-applying attributes.

- Jonathan M Davis