/* Small convolution benchmark Code partly by leonardo maffi, V. 1.0, Jun 6 2008 This code computes 500 200x200 convolutions using a 3x3 kernel, using handwritten code, general dynamic code, and statically created code. The handWritten() and metaGenerated() versions are faster (and they have the same speed) because the kernel is sparse, so some multiplications are useless. Timings on a Dual Core 2 GHz CPU, compiled with DMD V.1.029, -O -release -inline: Dynamic : 1.08 s Hand written : 0.16 s Meta-generated: 0.16 s */ import std.stdio: put = writef, putr = writefln; import std.metastrings: Format, ToString; static import std.c.time; /********************************* Return the seconds (a double) from the start of the program. */ double clock() { // from the d.time module auto t = std.c.time.clock(); if (t == -1) return 0.0; else return t/cast(double)std.c.time.CLOCKS_PER_SEC; } template Tuple(T...) { alias T Tuple; } template GenTerm(string m, string x, string y, int nr, int nc, int posx, int posy, ker...) { static if (ker[nc * posy + posx] == 0) const string GenTerm = ""; else static if (ker[nc * posy + posx] == 1) const string GenTerm = Format!("%s[%s+%s][%s+%s] + ", m, x, ToString!(posx - (nc / 2)), y, ToString!(posy - (nr / 2))); else const string GenTerm = Format!("%s * %s[%s+%s][%s+%s] + ", ker[nc * posy + posx].stringof, m, x, ToString!(posx - (nc / 2)), y, ToString!(posy - (nr / 2))); } template GenConvolutionLine(string m, string x, string y, int nr, int nc, int posx, int posy, ker...) { static if (posx < nc) const string GenConvolutionLine = GenTerm!(m, x, y, nr, nc, posx, posy, ker) ~ GenConvolutionLine!(m, x, y, nr, nc, posx+1, posy, ker); else const string GenConvolutionLine = ""; } template GenConvolution(string m, string x, string y, int nr, int nc, int posy, ker...) { static if (posy < nr) const string GenConvolution = GenConvolutionLine!(m, x, y, nr, nc, 0, posy, ker) ~ GenConvolution!(m, x, y, nr, nc, posy+1, ker); else const string GenConvolution = "0"; } template Convolution(string m, string x, string y, int nc, ker...) { const string Convolution = GenConvolution!(m, x, y, ker.length / nc, nc, 0, ker); } // ------------------------------------------------------ void dynamic(float[][] inm, float[][] outm, float[] kern, int w, int h) { int height = inm.length; int width = inm[0].length; for (int x = 1; x < width - 1; ++x) for (int y = 1; y < height - 1; ++y) { float sum = 0.0; for (int i = 0; i < w; ++i) for (int j = 0; j < h; ++j) sum += kern[j * w + i] * inm[x + i - w / 2][y + j - h / 2]; outm[x - 1][y - 1] = sum; } } void handWritten(float[][] inm, float[][] outm) { int height = inm.length; int width = inm[0].length; for (int x = 1; x < width - 1; ++x) for (int y = 1; y < height - 1; ++y) outm[x - 1][y - 1] = inm[x+1][y] + inm[x-1][y] + inm[x][y-1] + inm[x][y+1] - 4.5 * inm[x][y]; } void metaGenerated(int nc, kernel...)(float[][] inm, float[][] outm) { int height = inm.length; int width = inm[0].length; // pragma(msg, Convolution!("inm", "x", "y", nc, kernel)); // to see it for (int x = 1; x < width - 1; ++x) for (int y = 1; y < height - 1; ++y) mixin("outm[x - 1][y - 1] = " ~ Convolution!("inm", "x", "y", nc, kernel) ~ ";"); } void main() { const int WIDTH = 200; const int HEIGHT = WIDTH; const int NLOOP = 500; auto data = new float[][](WIDTH, HEIGHT); auto output = new float[][](WIDTH-2, HEIGHT-2); for (int j; j < WIDTH; ++j) data[j][] = 1.5; auto t0 = clock(); float[] kernel1 = [0., 1, 0, 1, -4.5, 1, 0, 1, 0]; for (int i; i < NLOOP; ++i) dynamic(data, output, kernel1, 3, 3); auto t1 = clock(); for (int i; i < NLOOP; ++i) handWritten(data, output); auto t2 = clock(); alias Tuple!(0, 1, 0, 1, -4.5, 1, 0, 1, 0) kernel2; for (int i; i < NLOOP; ++i) metaGenerated!(3, kernel2)(data, output); auto t3 = clock(); putr("Dynamic : ", t1 - t0); putr("Hand written : ", t2 - t1); putr("Meta-generated: ", t3 - t2); }