Cの速さにがっくり来た

Rubyで書いたグラフィックバッファ処理と同じものをCで書き直してみた。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

typedef struct {
  int height;
  int width;
  int **raw;
} buf;

buf *init_buf (int width, int height) {
  buf *b;
  int i, j;

  b = (buf *)malloc(sizeof(int) * 2 + sizeof(int*));
  b->height = height;
  b->width =width;

  b->raw = (int **)malloc(sizeof(int*) * height); 
  b->raw[0] = (int *)malloc(sizeof(int) * width * height); 
  for (i = 1; i < height; i ++) {
    b->raw[i] = b->raw[0] + (width * i);
  }
  for (i = 0; i < height; i++) {
    for (j = 0; j < width; j++) {
      //      b->raw[i][j] = 0x00ffccff; // purple-ish white
      b->raw[i][j] = 0x00ffffff; // white
    }
  }
  return b;
}

int save_buf (buf *b) {
  FILE *fp;
  int i, j;

  if ((fp = fopen("test.ppm", "w")) == NULL) {
      printf("Can't open the file\n");
      exit(1);
  }

  fprintf(fp, "P6\n%d %d\n255\n", b->width, b->height);

  for (i = 0; i < b->height; i++) {
    for (j = 0; j < b->width; j++) {
      fwrite(&b->raw[i][j], sizeof(char), 3, fp);
    }
  }
  fclose(fp);
  return 0;
}

int fill_circle (buf *buf, int x, int y, int rad, int color, int opc) {
  int i, j;
  float back, fore;
  unsigned char r, g, b;
  unsigned char r2, g2, b2;

  if ((opc < 0) || (opc >100)) {
      return 1;
  }

  r2 = color & 0x000000ff;
  g2 = (color & 0x0000ff00) / 0x100;
  b2 = (color & 0x00ff0000) / 0x10000;

  fore = (float)opc / 100;
  back = (100 - (float)opc) / 100;

  for (i = 0; i < buf->height; i++) {
    for (j = 0; j < buf->width; j++) {
      if ((rad*rad - (y-i)*(y-i) - (x-j)*(x-j)) > 0) { 
	r = buf->raw[i][j] & 0x000000ff;
	g = (buf->raw[i][j] & 0x0000ff00) / 0x100;
	b = (buf->raw[i][j] & 0x00ff0000) / 0x10000;
	r = (r2 * fore) + (r * back);
	g = (g2 * fore) + (g * back);
	b = (b2 * fore) + (b * back);
	buf->raw[i][j] = (b * 0x10000) + (g * 0x100) + r;
      }
    }
  }
  return 0;
}

int main (int argc,  char *argv[]) {
  buf *b;
  int i;
  int x, y, r, col;

  srand(time(NULL));

  b = init_buf(320, 240);

  for (i = 0; i < 100; i++) {
    x = rand() % b->width;
    y = rand() % b->height;
    r = rand() % 50 + 10;
    col = rand() & 0x00ffffff;
    fill_circle(b, x, y, r, col, 60);
  }

  save_buf(b);
  return 0;
}

intのポインタのポインタを含む構造体の初期化のところで、エラーが出まくって、こんなのきっとぼくには無理だと諦めそうになったほど、メモリとポインタの扱いがややこしい(メモリの解放のほうも単純じゃなくてめんどくさそう)。

うまく動かないときに何はともあれでgdbを起動してブレークポイントを設定すると、これが非常に役立つことを発見しつつある。構造体メンバの初期化忘れとか、intからfloatへのキャストする場所の間違えとか、バグの原因となっているところが、すぐに分かる。

それにしても、Cが劇っ速で驚いた。100個の円を塗りつぶすというほぼ同じ処理をやるのに、Rubyで9秒、Cでは0.09秒と100倍の差がついた。無駄なループを省くとさらに、0.04秒に縮まった。このぐらい速いんなら、ピクセル単位の差分を計算してホゲホゲという処理も、現実的な時間で可能な気がする。