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
D code: 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
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<n;i++){
auto prime = receiveOnly!int;
writeln(prime);
}
terminated = true;
exit(0);
});
}
void generate(Tid tid) {
for (auto i=2;!terminated;i++) {
tid.send(i);
}
}
void filter(int nLeft) {
auto prime = receiveOnly!int;
mainTid.send(prime);
if (nLeft > 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
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