Thread overview | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
October 14, 2013 GDC vs dmd speed | ||||
---|---|---|---|---|
| ||||
Hello, Whilst porting some C++ code I have discovered that the compiled output from the gdc compiler seems to be 47% quicker than the dmd compiler. The code I believe that is the 'busy' code is below. Although I could provide a complete test if anyone is interested. Is this an expected result and/or is there something I could change to make the compilers perform similarly. The function render_minecraft gets called repeatedly to render single frames. framebufindex is a simple function to return a buffer index. Perhaps it is not being inlined? rgba is another simple function. further details are: dmd32 v2.063.2 with flags: ["-O", "-release", "-noboundscheck", "-inline"] gdc 4.6 (0.29.1-4.6.4-1ubuntu4) Which I assume might be v2.020? with flags: ["-O2"] // render the next frame into the given 'frame_buf' void render_minecraft(void * private_renderer_data, uint32_t * frame_buf) { render_info * info = cast(render_info *)private_renderer_data; const float pi = 3.14159265f; float dx = cast(float)(Clock.currSystemTick.length % (TickDuration.ticksPerSec * 10)) / (TickDuration.ticksPerSec * 10); float xRot = sin(dx * pi * 2) * 0.4f + pi / 2; float yRot = cos(dx * pi * 2) * 0.4f; float yCos = cos(yRot); float ySin = sin(yRot); float xCos = cos(xRot); float xSin = sin(xRot); float ox = 32.5f + dx * 64; float oy = 32.5f; float oz = 32.5f; for (int x = 0; x < width; ++x) { float ___xd = cast(float)(x - width / 2) / height; for (int y = 0; y < height; ++y) { float __yd = cast(float)(y - height / 2) / height; float __zd = 1; float ___zd = __zd * yCos + __yd * ySin; float _yd = __yd * yCos - __zd * ySin; float _xd = ___xd * xCos + ___zd * xSin; float _zd = ___zd * xCos - ___xd * xSin; uint32_t col = 0; uint32_t br = 255; float ddist = 0; float closest = 32; for (int d = 0; d < 3; ++d) { float dimLength = _xd; if (d == 1) dimLength = _yd; if (d == 2) dimLength = _zd; float ll = 1 / (dimLength < 0 ? -dimLength : dimLength); float xd = (_xd) * ll; float yd = (_yd) * ll; float zd = (_zd) * ll; float initial = ox - cast(int)ox; if (d == 1) initial = oy - cast(int)oy; if (d == 2) initial = oz - cast(int)oz; if (dimLength > 0) initial = 1 - initial; float dist = ll * initial; float xp = ox + xd * initial; float yp = oy + yd * initial; float zp = oz + zd * initial; if (dimLength < 0) { if (d == 0) xp--; if (d == 1) yp--; if (d == 2) zp--; } while (dist < closest) { uint tex = info.map[mapindex(xp, yp, zp)]; if (tex > 0) { uint u = cast(uint32_t)((xp + zp) * 16) & 15; uint v = (cast(uint32_t)(yp * 16) & 15) + 16; if (d == 1) { u = cast(uint32_t)(xp * 16) & 15; v = (cast(uint32_t)(zp * 16) & 15); if (yd < 0) v += 32; } uint32_t cc = info.texmap[u + v * 16 + tex * 256 * 3]; if (cc > 0) { col = cc; ddist = 255 - cast(int)(dist / 32 * 255); br = 255 * (255 - ((d + 2) % 3) * 50) / 255; closest = dist; } } xp += xd; yp += yd; zp += zd; dist += ll; } } const uint32_t r = cast(uint32_t)(((col >> 16) & 0xff) * br * ddist / (255 * 255)); const uint32_t g = cast(uint32_t)(((col >> 8) & 0xff) * br * ddist / (255 * 255)); const uint32_t b = cast(uint32_t)(((col) & 0xff) * br * ddist / (255 * 255)); frame_buf[framebufindex(x, y)] = rgba(r, g, b); } } } |
October 14, 2013 Re: GDC vs dmd speed | ||||
---|---|---|---|---|
| ||||
Posted in reply to Spacen Jasset | > Although I could provide a complete test if anyone is interested. Is this an expected result and/or is there something I could change to make the compilers perform similarly.
Maybe you could do something to make the code compiled with DMD
perform better, but it is not unusual for GDC to produce significantly faster
code than DMD.
|
October 14, 2013 Re: GDC vs dmd speed | ||||
---|---|---|---|---|
| ||||
Posted in reply to Spacen Jasset | Spacen Jasset:
> const float pi = 3.14159265f;
>
> float dx = cast(float)(Clock.currSystemTick.length % (TickDuration.ticksPerSec * 10)) / (TickDuration.ticksPerSec * 10);
> float xRot = sin(dx * pi * 2) * 0.4f + pi / 2;
> float yRot = cos(dx * pi * 2) * 0.4f;
> float yCos = cos(yRot);
> float ySin = sin(yRot);
> float xCos = cos(xRot);
> float xSin = sin(xRot);
>
> float ox = 32.5f + dx * 64;
> float oy = 32.5f;
> float oz = 32.5f;
>
> for (int x = 0; x < width; ++x) {
> float ___xd = cast(float)(x - width / 2) / height;
> for (int y = 0; y < height; ++y) {
> float __yd = cast(float)(y - height / 2) / height;
> float __zd = 1;
The performance difference between the DMD and GDC compile is kind of expected for FP-heavy code. Also try the new LDC2 compiler (ldmd2 for the same compilation switches) that sometimes is better than GDC.
More comments:
- There is a PI in std.math (but it's not a float);
- Add immutable/const to every variable that doesn't need to change. This is a good habit like washing your hands before eating;
- "for (int x = 0; x < width; ++x)" ==> "foreach (immutable x; 0 .. width)";
- I suggest to avoid many leading/trailing newlines in identifier names;
- It's probably worth replacing all those "float" with another name, like "FP" and then define "alias FP = float;" at the beginning. So you can see how much performance you lose/gain using floats/doubles. In many cases in my code there is no difference, but float are less precise. Floats can be useful when you have many of them, in a struct or array. Floats can also be useful when you call certain numerical functions that compute their result by approximation, but on some CPUs sin/cos are not among those functions.
Bye,
bearophile
|
October 14, 2013 Re: GDC vs dmd speed | ||||
---|---|---|---|---|
| ||||
Posted in reply to Spacen Jasset | On 10/14/2013 12:24 PM, Spacen Jasset wrote:
> dmd32 v2.063.2
> with flags: ["-O", "-release", "-noboundscheck", "-inline"]
>
> gdc 4.6 (0.29.1-4.6.4-1ubuntu4) Which I assume might be v2.020?
> with flags: ["-O2"]
dmd uses the x87 for 32 bit code for floating point, while gdc uses the SIMD instructions, which are faster.
For 64 bit code, dmd uses SIMD instructions too.
|
October 15, 2013 Re: GDC vs dmd speed | ||||
---|---|---|---|---|
| ||||
Posted in reply to Spacen Jasset | On Monday, 14 October 2013 at 19:24:27 UTC, Spacen Jasset wrote:
> Hello,
>
> Whilst porting some C++ code I have discovered that the compiled output from the gdc compiler seems to be 47% quicker than the dmd compiler.
....
Here is a few more data points for microbenchmarks of simple functions (Project Euler), which supports an observation (disclaimer: my microbenchmark is not a guarantee of your code performance, etc.) that the fastest code is produced by LDC, then GDC and DMD is the slowest one.
Tested on Xubuntu 13.04 64-bit Core i5 3450S 2.8GHz.
--------------------
Test 1:
// 454ns LDC 0.11.0: ldmd2 -m64 -O -noboundscheck -inline -release
// 830ns GDC 4.8.1: gdc -m64 -march=native -fno-bounds-check -frename-registers -frelease -O3
// 1115ns DMD64 2.063.2: dmd -O -noboundscheck -inline -release
int e28_0(int N = 1002) {
int diagNumber = 1;
int sum = diagNumber;
for (int width = 2; width < N; width += 2)
for (int j = 0; j < 4; ++j) {
diagNumber += width;
sum += diagNumber;
}
return sum;
}
--------------------
Test 2:
// 118ms LDC 0.11.0: ldmd2 -m64 -O -noboundscheck -inline -release
// 125ms GDC 4.8.1: gdc -m64 -march=native -fno-bounds-check -frename-registers -frelease -O3
// 161ms DMD64 2.063.2: dmd -O -noboundscheck -inline -release
bool isPalindrome(string s) {return equal(s, s.retro);}
int e4(int N = 1000) {
int nMax = 0;
foreach (uint i; 1..N)
foreach (uint j; i..N)
if (isPalindrome(to!string(i*j)) && i*j > nMax)
nMax = i*j;
return nMax;
}
--------------------
Test 3:
// 585us LDC 0.11.0: ldmd2 -m64 -O -noboundscheck -inline -release
// 667us GDC 4.8.1: gdc -m64 -march=native -fno-bounds-check -frename-registers -frelease -O3
// 853us DMD64 2.063.2: dmd -O -noboundscheck -inline -release
int e67_0(string fileName = r"C:\Euler\data\e67.txt") {
// Read triangle numbers from file.
int[][] cell;
foreach (line; splitLines(cast(char[]) read(fileName))) {
int[] row;
foreach (token; std.array.splitter(line))
row ~= [to!int(token)];
cell ~= row;
}
// Compute maximum value partial paths ending at each cell.
foreach (y; 1..cell.length) {
cell[y][0] += cell[y-1][0];
foreach (x; 1..y)
cell[y][x] += max(cell[y-1][x-1], cell[y-1][x]);
cell[y][y] += cell[y-1][y-1];
}
// Return the maximum value terminal path.
return cell[$-1].reduce!max;
}
--------------------
Here is the relative to LDC code speed averaged over these three test (larger number is slower):
LDC 1.00
GDC 1.34
DMD 1.76
|
October 15, 2013 Re: GDC vs dmd speed | ||||
---|---|---|---|---|
| ||||
Posted in reply to Spacen Jasset | On Monday, 14 October 2013 at 19:24:27 UTC, Spacen Jasset wrote: > gdc 4.6 (0.29.1-4.6.4-1ubuntu4) Which I assume might be v2.020? > with flags: ["-O2"] That's a really old gdc. If you can, upgrade to ubuntu 13.10 and you'll get a more up-to-date version. Alternatively, build from source: http://gdcproject.org/wiki/Installation/General It'll take an age to run the compilation, but it's not hard to do. |
October 15, 2013 Re: GDC vs dmd speed | ||||
---|---|---|---|---|
| ||||
Posted in reply to bearophile | On 14/10/2013 22:06, bearophile wrote:
> Spacen Jasset:
>
>> const float pi = 3.14159265f;
>>
>> float dx = cast(float)(Clock.currSystemTick.length %
>> (TickDuration.ticksPerSec * 10)) / (TickDuration.ticksPerSec * 10);
>> float xRot = sin(dx * pi * 2) * 0.4f + pi / 2;
>> float yRot = cos(dx * pi * 2) * 0.4f;
>> float yCos = cos(yRot);
>> float ySin = sin(yRot);
>> float xCos = cos(xRot);
>> float xSin = sin(xRot);
>>
>> float ox = 32.5f + dx * 64;
>> float oy = 32.5f;
>> float oz = 32.5f;
>>
>> for (int x = 0; x < width; ++x) {
>> float ___xd = cast(float)(x - width / 2) / height;
>> for (int y = 0; y < height; ++y) {
>> float __yd = cast(float)(y - height / 2) / height;
>> float __zd = 1;
>
>
> The performance difference between the DMD and GDC compile is kind of
> expected for FP-heavy code. Also try the new LDC2 compiler (ldmd2 for
> the same compilation switches) that sometimes is better than GDC.
>
> More comments:
> - There is a PI in std.math (but it's not a float);
> - Add immutable/const to every variable that doesn't need to change.
> This is a good habit like washing your hands before eating;
> - "for (int x = 0; x < width; ++x)" ==> "foreach (immutable x; 0 ..
> width)";
> - I suggest to avoid many leading/trailing newlines in identifier names;
> - It's probably worth replacing all those "float" with another name,
> like "FP" and then define "alias FP = float;" at the beginning. So you
> can see how much performance you lose/gain using floats/doubles. In many
> cases in my code there is no difference, but float are less precise.
> Floats can be useful when you have many of them, in a struct or array.
> Floats can also be useful when you call certain numerical functions that
> compute their result by approximation, but on some CPUs sin/cos are not
> among those functions.
>
> Bye,
> bearophile
Thank you. I may take up some of those suggestions. It was a direct port of some c++ hence the style.
|
October 15, 2013 Re: GDC vs dmd speed | ||||
---|---|---|---|---|
| ||||
Posted in reply to Walter Bright | On 14/10/2013 22:22, Walter Bright wrote:
> On 10/14/2013 12:24 PM, Spacen Jasset wrote:
>> dmd32 v2.063.2
>> with flags: ["-O", "-release", "-noboundscheck", "-inline"]
>>
>> gdc 4.6 (0.29.1-4.6.4-1ubuntu4) Which I assume might be v2.020?
>> with flags: ["-O2"]
>
> dmd uses the x87 for 32 bit code for floating point, while gdc uses the
> SIMD instructions, which are faster.
>
> For 64 bit code, dmd uses SIMD instructions too.
>
Thanks Walter. I shall find a 64 bit system at some point to compare.
|
Copyright © 1999-2021 by the D Language Foundation