A significant performance difference

bearophile via Digitalmars-d-learn digitalmars-d-learn at puremagic.com
Sun Aug 31 03:55:31 PDT 2014


This is C++ code that solves one Euler problem:

--------------
#include <stdio.h>
#include <map>

const unsigned int H = 9, W = 12;

const int g[6][3] = {{7, 0, H - 3},
                      {1 + (1 << H) + (1 << (2 * H)), 0, H - 1},
                      {3 + (1 << H), 0, H - 2},
                      {3 + (2 << H), 0, H - 2},
                      {1 + (1 << H) + (2 << H), 0, H - 2},
                      {1 + (1 << H) + (1 << (H - 1)), 1, H - 1}};

int main() {
     unsigned long long p, i, k;
     unsigned int j, l;
     std::map<unsigned int, unsigned long long> x, y;
     x[0] = 1;

     for (i = 0; i < W; ++i) {
         y.clear();
         while (!x.empty()) {
             j = x.begin()->first;
             p = x.begin()->second;
             x.erase(x.begin());

             for (k = 0; k < H; ++k)
                 if ((j & (1 << k)) == 0)
                     break;

             if (k == H)
                 y[j >> H] += p;
             else
                 for (l = 0; l < 6; ++l)
                     if (k >= g[l][1] && k <= g[l][2])
                         if ((j & (g[l][0] << k)) == 0)
                             x[j + (g[l][0] << k)] += p;
         }
         x = y;
     }

     printf("%lld\n", y[0]);
     return 0;
}
--------------


I have translated it to D like this (I know in D there are nicer 
ways to write it, but I have tried to keep the look of the code 
as much similar as possible to the C++ code):


--------------
import core.stdc.stdio;

const uint H = 9, W = 12;

const uint[3][6] g = [[7, 0, H - 3],
                       [1 + (1 << H) + (1 << (2 * H)), 0, H - 1],
                       [3 + (1 << H), 0, H - 2],
                       [3 + (2 << H), 0, H - 2],
                       [1 + (1 << H) + (2 << H), 0, H - 2],
                       [1 + (1 << H) + (1 << (H - 1)), 1, H - 1]];

int main() {
     ulong p, i, k;
     uint j, l;
     ulong[uint] x, y;
     x[0] = 1;

     for (i = 0; i < W; ++i) {
         y = null;
         while (x.length) {
             j = x.byKey.front;
             p = x.byValue.front;
             x.remove(cast(int)j);

             for (k = 0; k < H; ++k)
                 if ((j & (1 << k)) == 0)
                     break;

             if (k == H)
                 y[j >> H] += p;
             else
                 for (l = 0; l < 6; ++l)
                     if (k >= g[l][1] && k <= g[l][2])
                         if ((j & (g[l][0] << k)) == 0)
                             x[j + (g[l][0] << k)] += p;
         }
         x = y;
     }

     printf("%lld\n", y[0]);
     return 0;
}
--------------


The C++ code is much faster than the D code (I see the D code 30+ 
times slower with dmd and about like 20 times with ldc2). One 
difference between the C++ and D code is that the C++ map uses a 
search tree (red-black probably), while the D code uses a hash.

To test that algorithmic difference, if I replace the map in the 
C++ code with a std::unordered_map (C++11):

#include <unordered_map>
...
std::unordered_map<unsigned int, unsigned long long> x, y;


then the run-time increases (more than two times) but it's still 
much faster than the D code.

Is it possible to fix the D code to increase its performance 
(there are associative array libraries for D, but I have not 
tried them in this program).

Bye,
bearophile


More information about the Digitalmars-d-learn mailing list