Re: Performance issue with fiber

2021-07-30 Thread hanabi1224 via Digitalmars-d-learn

On Friday, 30 July 2021 at 14:41:06 UTC, Daniel Kozak wrote:

I have rewrite it to be same as dart version


Thanks! There're both generator version and fiber version on the 
site(if possible), the 2 versions are not really comparable to 
each other (generator solutions should be much faster). There's 
another dart implementation with Isolate 
[here](https://github.com/hanabi1224/Programming-Language-Benchmarks/blob/main/bench/algorithm/coro-prime-sieve/2.dart), it's unlisted because of very bad performance. (Isolate is the closest thing in dart to thread or fiber but it's much much more expensive to even spawn)


I'd like to list D's generator solution but please note that it's 
only comparable to the kotlin/c#/python generator solutions while 
the fiber one is still a separate issue.


Re: Performance issue with fiber

2021-07-30 Thread Daniel Kozak via Digitalmars-d-learn
On Wed, Jul 28, 2021 at 11:41 PM hanabi1224 via Digitalmars-d-learn <
digitalmars-d-learn@puremagic.com> wrote:

> On Wednesday, 28 July 2021 at 16:26:49 UTC, drug wrote:
> > I profiled the provided example (not `FiberScheduler`) using
> > perf. Both dmd and ldc2 gave the same result - `void
> > filterInner(int, int)` took ~90% of the run time. The time was
> > divided between:
> >   `int std.concurrency.receiveOnly!(int).receiveOnly()` - 58%
> >   `void std.concurrency.send!(int).send(std.concurrency.Tid,
> > int)` - 31%
> >
> > So most of the time is messages passing.
> >
> > Between the fibers creating took very few time. Perf output
> > contains information only of `void
> > std.concurrency.FiberScheduler.create(void delegate()).wrap()`
> > which took less than 0.5%. But I wouldn't say that I did the
> > profiling ideally so take it with a grain of salt.
>
> Very interesting findings! After making the Fiber fix, I also
> made profiling with valgrind, the result shows MessageBox related
> staff contributes to ~13.7% of total cycles, swapContex related
> staff add up to a larger percentage (My rough estimation is
>  >50%), I'd like to share the result svg but did not figure out
> how to upload here.
>

I have rewrite it to be same as dart version

import std;

void main(string[] args) {
auto n = args.length > 1 ? args[1].to!int() : 5;

auto r = new Generator!int(
{
for(auto i = 2;;i++)
yield(i);
});

for(auto i=0;i

Re: Performance issue with fiber

2021-07-28 Thread hanabi1224 via Digitalmars-d-learn

On Wednesday, 28 July 2021 at 16:26:49 UTC, drug wrote:
I profiled the provided example (not `FiberScheduler`) using 
perf. Both dmd and ldc2 gave the same result - `void 
filterInner(int, int)` took ~90% of the run time. The time was 
divided between:

`int std.concurrency.receiveOnly!(int).receiveOnly()` - 58%
	`void std.concurrency.send!(int).send(std.concurrency.Tid, 
int)` - 31%


So most of the time is messages passing.

Between the fibers creating took very few time. Perf output 
contains information only of `void 
std.concurrency.FiberScheduler.create(void delegate()).wrap()` 
which took less than 0.5%. But I wouldn't say that I did the 
profiling ideally so take it with a grain of salt.


Very interesting findings! After making the Fiber fix, I also 
made profiling with valgrind, the result shows MessageBox related 
staff contributes to ~13.7% of total cycles, swapContex related 
staff add up to a larger percentage (My rough estimation is 
>50%), I'd like to share the result svg but did not figure out 
how to upload here.


Re: Performance issue with fiber

2021-07-28 Thread hanabi1224 via Digitalmars-d-learn

On Wednesday, 28 July 2021 at 16:31:49 UTC, Ali Çehreli wrote:
I assume the opposite because normally, the number of times a 
thread or fiber is spawned is nothing compared to the number of 
times they are context-switched. So, spawning can be expensive 
and nobody would realize as long as switching is cheap.


You are right, but that's not my point. whether fiber spawning is 
expensive should be compared to thread, and it should be much 
less expensive, ppl can expect to create much more fibers at the 
same time than system thread, even if it's stackful (that should 
mostly contribute to heap memory usage, fiber stack size should 
not be a perf bottleneck before running out of mem). And when 
analyzing perf issues with fiber, it's not a valid reason to me 
that 'fiber is expensive' because fiber itself is the solution to 
the expensiveness of thread, and non of the other fiber 
implementations in other languages/runtime have the same issue 
with the same test case.




Re: Performance issue with fiber

2021-07-28 Thread hanabi1224 via Digitalmars-d-learn

On Wednesday, 28 July 2021 at 14:39:29 UTC, Mathias LANG wrote:

Hence doing:
```diff
- auto scheduler = new FiberScheduler();
+ scheduler = new FiberScheduler();
```


Thanks for pointing it out! Looks like I was benchmarking thread 
instead of fiber. I just made the change you suggest but the 
result is very similar, that being said, using system thread or 
fiber does not make any obvious difference in this test case, 
this fact itself seems problematic, fiber should be much faster 
than system thread in this test case (as I have proved for many 
other langs with the same case, I published results 
[here](https://programming-language-benchmarks.vercel.app/problem/coro-prime-sieve) but note that not all of them are implemented with stackful coroutine), unless there's some defect in D's current fiber implementation.


Re: Performance issue with fiber

2021-07-28 Thread Ali Çehreli via Digitalmars-d-learn

On 7/28/21 1:15 AM, hanabi1224 wrote:

> On Wednesday, 28 July 2021 at 01:12:16 UTC, Denis Feklushkin wrote:
>> Spawning fiber is expensive
>
> Sorry but I cannot agree with the logic behind this statement, the whole
> point of using fiber is that, spwaning system thread is expensive, thus
> ppl create lightweight thread 'fiber'

I assume the opposite because normally, the number of times a thread or 
fiber is spawned is nothing compared to the number of times they are 
context-switched. So, spawning can be expensive and nobody would realize 
as long as switching is cheap.


There are other reasons why fibers are faster than threads all related 
to context switching:


- CPU cache efficiency

- Translation lookaside buffer (TLB) efficiency

- Holding on to the entirety of the time slice given by the OS

Ali

P.S. The little I know on these topics is included in this presentation:

  https://dconf.org/2016/talks/cehreli.html



Re: Performance issue with fiber

2021-07-28 Thread drug via Digitalmars-d-learn

28.07.2021 17:39, Mathias LANG пишет:

On Wednesday, 21 July 2021 at 22:51:38 UTC, hanabi1224 wrote:
Hi, I'm new to D lang and encounter some performance issues with 
fiber, not sure if there's something obviously wrong with my code.


I took a quick look, and the first problem I saw was that you were using 
`spawnLinked` but not replacing the scheduler.

`std.concurrency` uses a global `scheduler` variable to do its job.
Hence doing:
```diff
- auto scheduler = new FiberScheduler();
+ scheduler = new FiberScheduler();
```

Will ensure that `spawnLinked` works as expected.
There are a few other things to consider, w.r.t. fibers:
- Our Fibers are 4 pages (on Linux) by default;
- We have an extra guard page, because we are a native language, so we 
can't do the same trick as Go to auto-grow the stack;
- Spawning fibers *is* expensive and other languages reuse fibers (Yes, 
Go recycle them);


The `FiberScheduler` implementation is unfortunately pretty bad: it 
[does not re-use 
fibers](https://github.com/dlang/phobos/blob/b48cca57e8ad2dc56872499836bfa1e70e390abb/std/concurrency.d#L1578-L1599). 
I believe this is the core of the issue.


I profiled the provided example (not `FiberScheduler`) using perf. Both 
dmd and ldc2 gave the same result - `void filterInner(int, int)` took 
~90% of the run time. The time was divided between:

`int std.concurrency.receiveOnly!(int).receiveOnly()` - 58%
`void std.concurrency.send!(int).send(std.concurrency.Tid, int)` - 31%

So most of the time is messages passing.

Between the fibers creating took very few time. Perf output contains 
information only of `void std.concurrency.FiberScheduler.create(void 
delegate()).wrap()` which took less than 0.5%. But I wouldn't say that I 
did the profiling ideally so take it with a grain of salt.


Re: Performance issue with fiber

2021-07-28 Thread Mathias LANG via Digitalmars-d-learn

On Wednesday, 21 July 2021 at 22:51:38 UTC, hanabi1224 wrote:
Hi, I'm new to D lang and encounter some performance issues 
with fiber, not sure if there's something obviously wrong with 
my code.


I took a quick look, and the first problem I saw was that you 
were using `spawnLinked` but not replacing the scheduler.
`std.concurrency` uses a global `scheduler` variable to do its 
job.

Hence doing:
```diff
- auto scheduler = new FiberScheduler();
+ scheduler = new FiberScheduler();
```

Will ensure that `spawnLinked` works as expected.
There are a few other things to consider, w.r.t. fibers:
- Our Fibers are 4 pages (on Linux) by default;
- We have an extra guard page, because we are a native language, 
so we can't do the same trick as Go to auto-grow the stack;
- Spawning fibers *is* expensive and other languages reuse fibers 
(Yes, Go recycle them);


The `FiberScheduler` implementation is unfortunately pretty bad: 
it [does not re-use 
fibers](https://github.com/dlang/phobos/blob/b48cca57e8ad2dc56872499836bfa1e70e390abb/std/concurrency.d#L1578-L1599). I believe this is the core of the issue.


Re: Performance issue with fiber

2021-07-28 Thread James Blachly via Digitalmars-d-learn

On 7/27/21 9:12 PM, Denis Feklushkin wrote:
Spawning fiber is expensive (but not so expensive as spawning thread, of 
course), but switching is fast.


Thus, you can spawn and pause "workers" fibers for avaiting of jobs.
(Probably, this behaviour is already implemented in number of libraries 
and it isn't actually need to implement another one.)


Agree with sibling comment by hanabi1224: spawning fiber (in other 
language runtimes) is INCREDIBLY fast, even though they have stack.


Something is wrong here.


Re: Performance issue with fiber

2021-07-28 Thread hanabi1224 via Digitalmars-d-learn
On Wednesday, 28 July 2021 at 01:12:16 UTC, Denis Feklushkin 
wrote:

Spawning fiber is expensive


Sorry but I cannot agree with the logic behind this statement, 
the whole point of using fiber is that, spwaning system thread is 
expensive, thus ppl create lightweight thread 'fiber', then if a 
'fiber' is still considered expensive, should ppl invent 'giber' 
on top of 'fiber', then 'hiber' on top of 'giber'? That will be 
infinite.


If you mean 'each fiber has its own stack' by 'expensive', to be 
fair, golang's goroutine is stackful (4KB stack for each 
goroutine), it's like >50X faster with the same test case.


Re: Performance issue with fiber

2021-07-27 Thread Denis Feklushkin via Digitalmars-d-learn

On Monday, 26 July 2021 at 12:09:07 UTC, hanabi1224 wrote:

Thank you for your response! I've got some questions tho.

On Saturday, 24 July 2021 at 09:17:47 UTC, Stefan Koch wrote:


It will not use a fiber pool.


Why fiber pool? Isn't fiber a lightweight logical thread which 
is already implemented with thread pool internally?


Spawning fiber is expensive (but not so expensive as spawning 
thread, of course), but switching is fast.


Thus, you can spawn and pause "workers" fibers for avaiting of 
jobs.
(Probably, this behaviour is already implemented in number of 
libraries and it isn't actually need to implement another one.)


Re: Performance issue with fiber

2021-07-26 Thread jfondren via Digitalmars-d-learn

On Monday, 26 July 2021 at 15:27:48 UTC, russhy wrote:

```

build:

```
dub build --compiler=ldc -brelease --single primesv1.d
```




-brelease is a typo issue, i don't think that produce defired 
effect, most likely it defaulted to debug build


it should be -b release


No, it builds a release build with -brelease. Try it yourself, 
the first line of output tells you what the build type is. 
Instead of interpreting -abc as -a -b -c like getopt, dub 
interprets it as -a bc


--arch/-a works the same way, and although I don't see this usage 
in the official documentation, I didn't make it up:


https://duckduckgo.com/?q=dub+%22brelease%22&norw=1


Re: Performance issue with fiber

2021-07-26 Thread russhy via Digitalmars-d-learn

```

build:

```
dub build --compiler=ldc -brelease --single primesv1.d
```




-brelease is a typo issue, i don't think that produce defired 
effect, most likely it defaulted to debug build


it should be -b release




Re: Performance issue with fiber

2021-07-26 Thread hanabi1224 via Digitalmars-d-learn

Thank you for your response! I've got some questions tho.

On Saturday, 24 July 2021 at 09:17:47 UTC, Stefan Koch wrote:


It will not use a fiber pool.


Why fiber pool? Isn't fiber a lightweight logical thread which is 
already implemented with thread pool internally?


Spawning a new fiber is expensive because of the stack 
allocation for it.


Actually, I've benchmarked many stackful coroutine 
implementations in different langs but they're all much faster, 
and BTW, AFAIK go's goroutine is stackful as well (4KB IIRC)


Re: Performance issue with fiber

2021-07-24 Thread jfondren via Digitalmars-d-learn

On Saturday, 24 July 2021 at 09:17:47 UTC, Stefan Koch wrote:

On Wednesday, 21 July 2021 at 22:51:38 UTC, hanabi1224 wrote:
Hi, I'm new to D lang and encounter some performance issues 
with fiber, not sure if there's something obviously wrong with 
my code.




There is your problem.

auto scheduler = new FiberScheduler;


The Fiber scheduler will spawn a new fiber for every job.
It will not use a fiber pool. Spawning a new fiber is expensive 
because of the stack allocation for it.
Also if I recall correctly it will run single-threaded but I am 
not 100% sure on that.
Just have a look at the running processes ... if you just see 
one than you are single threaded.


I get 8->3 seconds using vibe's fiber scheduler, which still 
isn't competitive with Elixir.


```d
--- primes.d2021-07-24 21:37:46.633053839 -0500
+++ primesv1.d  2021-07-24 21:35:50.843053425 -0500
@@ -1,16 +1,19 @@
 /++ dub.sdl:
+dependency "vibe-core" version="~>1.16.0"
  +/
-import std;
-import core.stdc.stdlib : exit;
+import std.stdio, std.conv;
+import vibe.core.concurrency;

 __gshared Tid mainTid;
 __gshared bool terminated = false;

 const int mailBoxSize = 1;

+extern(C) void _exit(int status);
+
 void main(string[] args) {
 auto n = args.length > 1 ? args[1].to!int() : 10;
-auto scheduler = new FiberScheduler;
+setConcurrencyPrimitive(ConcurrencyPrimitive.workerTask);
 scheduler.start({
 mainTid = thisTid();
 setMaxMailboxSize(mainTid, n, OnCrowding.throwException);
@@ -22,7 +25,7 @@
 writeln(prime);
 }
 terminated = true;
-exit(0);
+_exit(0);
 });
 }
```

build:

```
dub build --compiler=ldc -brelease --single primesv1.d
```

I think this is just a very goroutine-friendly test that relies 
on constantly spawning fibers and abusing message-passing rather 
than architecting out the concurrent parts of your program and 
how they should communicate. std.parallel's more appropriate in D.


Re: Performance issue with fiber

2021-07-24 Thread Stefan Koch via Digitalmars-d-learn

On Wednesday, 21 July 2021 at 22:51:38 UTC, hanabi1224 wrote:
Hi, I'm new to D lang and encounter some performance issues 
with fiber, not sure if there's something obviously wrong with 
my code.




There is your problem.

auto scheduler = new FiberScheduler;


The Fiber scheduler will spawn a new fiber for every job.
It will not use a fiber pool. Spawning a new fiber is expensive 
because of the stack allocation for it.
Also if I recall correctly it will run single-threaded but I am 
not 100% sure on that.
Just have a look at the running processes ... if you just see one 
than you are single threaded.




Re: Performance issue with fiber

2021-07-21 Thread seany via Digitalmars-d-learn

On Wednesday, 21 July 2021 at 22:51:38 UTC, hanabi1224 wrote:
Hi, I'm new to D lang and encounter some performance issues 
with fiber, not sure if there's something obviously wrong with 
my code.


[...]


Following.

I am also in need of more information to increase speed of D 
binaries using parallel code.


Performance issue with fiber

2021-07-21 Thread hanabi1224 via Digitalmars-d-learn
Hi, I'm new to D lang and encounter some performance issues with 
fiber, not sure if there's something obviously wrong with my code.


Basically, I found D lang's logical thread communication is quite 
like Elixir's (process),
I have programs written in both D and Elixir but the D version 
(release build) is like >5 times slower.


The program is to find first N primes by creating another 
coroutine for every prime found. And the idea is from the 
concurrent prime sieve example on golang's homepage.


Elixir code: 
[1.ex](https://github.com/hanabi1224/Programming-Language-Benchmarks/blob/main/bench/algorithm/coro-prime-sieve/1.ex)


D code: 
[1.d](https://github.com/hanabi1224/Programming-Language-Benchmarks/blob/main/bench/algorithm/coro-prime-sieve/1.d)


Perf result of calculating first 2000 primes on my laptop.

Elixir: 1.33s user 0.25s system 108% cpu 1.451 total

D(dmd): 10.41s user 34.48s system 361% cpu 12.405 total

D(ldc): 8.45s user 33.06s system 356% cpu 11.638 total


Also attaching code inline:

D
```d
import std;
import core.stdc.stdlib: exit;

__gshared Tid mainTid;
__gshared bool terminated = false;

const int mailBoxSize = 1;

void main(string[] args) {
auto n = args.length > 1 ? args[1].to!int() : 10;
auto scheduler = new FiberScheduler;
scheduler.start({
mainTid = thisTid();
setMaxMailboxSize(mainTid, n, OnCrowding.throwException);
auto filterTid = spawnLinked(&filter, n);
setMaxMailboxSize(filterTid, mailBoxSize, 
OnCrowding.block);

auto generatorTid = spawnLinked(&generate, filterTid);
for(auto i=0;i 0) {
filterInner(prime, nLeft);
}
}

void filterInner(int prime, int nLeft) {
auto nextTid = spawnLinked(&filter, nLeft-1);
setMaxMailboxSize(nextTid, mailBoxSize, OnCrowding.block);
while(!terminated){
auto d = receiveOnly!int;
if (d % prime != 0) {
nextTid.send(d);
}
}
}
```
Elixir
```elixir
defmodule App do
  def main(args) do
n = String.to_integer(Enum.at(args,0,"27"), 10)
generate(n)
  end

  def generate(n) do
mainPid = self()
pid = spawn_link(fn -> filter(mainPid, n) end)
generateLoop(pid, 2)
  end

  def generateLoop(pid, n) do
send(pid, {:n, n})
receive do
  :gen -> generateLoop(pid, n + 1)
end
  end

  def filter(mainPid, nLeft) do
receive do
  {:n, n} -> filterInner(mainPid, n, nLeft)
end
  end

  def filterInner(mainPid, prime, nLeft) do
send(mainPid, :gen)
IO.puts("#{prime}")
if nLeft > 1 do
  pid = spawn_link(fn -> filter(mainPid, nLeft-1) end)
  recieveAndSendToFilter(mainPid, self(), pid, prime)
else
  System.halt(0)
end
  end

  def recieveAndSendToFilter(mainPid, rxPid, txPid, prime) do
receive do
  {:n, n} -> recieveAndSendToFilterInner(mainPid, rxPid, 
txPid, prime, n)

end
  end
  def recieveAndSendToFilterInner(mainPid, rxPid, txPid, prime, 
n) do

if Integer.mod(n, prime) != 0 do
  send(txPid, {:n, n})
else
  send(mainPid, :gen)
end
recieveAndSendToFilter(mainPid, rxPid, txPid, prime)
  end
end
```