C言語でテーブル引きしたら速度が遅くなった

このQ&Aのポイント
  • C言語でテーブル引きをしている画像処理のプログラムで処理速度が遅くなってしまいました。
  • 以前の質問でテーブル引きを実現しましたが、改善されませんでした。
  • 処理時間の差を埋めるため、テーブル引きの修正方法についてアドバイスをいただきたいです。
回答を見る
  • ベストアンサー

C言語でテーブル引きしたら速度が遅くなった

プログラムに悩んでいるものです. とある画像処理のプログラムを組んでいるのですが,処理が遅くテーブル引きを組んでいます. この前もこの場を借りて質問しsqrt()のテーブル引きは実現したのですが,処理速度が遅くなってしまい原因が分からないので質問させていただきました. 前の質問URL:http://okwave.jp/qa/q7103550.html 前回から修正した現在のプログラムの一部を示します. ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー void filter(unsigned char* d, short *dx, short *dy, int w, int h) { ///// テーブル生成 ///// static int c_size = 0; // static 値を保持 static double c_sqrt[1020][1020]; if(c_size != 1020){ // 初回呼び出しのみ実行 c_size = 1020; for(int i=0; i<c_size; i++){ // 有りえるすべての値を生成 for(int j=0; j<c_size; j++){ c_sqrt[i][j] = sqrt( (double)(i*i + j*j) ); } } } ///// d = sqrt(dx^2 + dy^2) ///// for(int y = 1; y < h-1; ++y){ for(int x = 1; x < w-1; ++x){ double u = (double)dx[y*w+x]; double v = (double)dy[y*w+x]; if(u<0) u=-u; if(v<0) v=-v; int val = (int)c_sqrt[(int)u][(int)v] /4; if(val>255) val=255; d[y*w+x] = val; } } } ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー テーブル引きをしない場合(プログラム省略)はこの関数の処理時間が約9[ms]だったのに対し,上記のプログラムは約15[ms]となってしまいました. どういう風に修正すれば,テーブル引きの効果が出せるでしょうか? 長い文章を最後までお読みいただきありがとうございます. ご回答,よろしくお願い致します.

質問者が選んだベストアンサー

  • ベストアンサー
  • kmee
  • ベストアンサー率55% (1857/3366)
回答No.5

・添字に使うだけのu,vをわざわざdoubleにする必要はありません。 ・値の範囲が0~1020とわかっているなら、 u<0,v<0の処理も必要ありません。 ・sqrtだけテーブルにするのではなく、計算結果そのものをテーブルにしてしまえば、ループ内で計算する必要がなくなります。 あと、あくまで予想なのですが テーブル引きは「いちいち計算するより、計算結果を取ってくるだけの方が速い」という理屈で使うものです。 ところが、最近のCPUなら、浮動小数計算用の回路が付いているので、sqrtくらいだったら相当速く計算できます。 それに対して、メインメモリは、いまや「CPUにとっては遅い」記憶装置となってしまっています。キャッシュに入れば多少高速になりますが、今回のテーブルは1020*1020*sizeof(double)≒8MB、高機能なCPUでもギリギリサイズです。 最初に挙げた対策をしてもそれでも時間がかかるようなら、あなたの実行環境では、メモリアクセスの方が時間がかかるのでテーブル引きには不適切、ということなのでしょう。

apollograffitti
質問者

お礼

ご回答ありがとうございます. ハードウェア的な知見からのアドバイスは勉強になりました.

その他の回答 (4)

  • wormhole
  • ベストアンサー率28% (1619/5653)
回答No.4

>1020*1020回以下ならテーブル引きじゃない方が早いですよ。 上記は「c_sqrtの参照回数の合計が1020*1020回以下なら」ですね・・・

回答No.3

表を全て埋める必要はあるのですか。 実際にc_sqrtの配列中で値が使われるのはどのくらいの割合でしょうか。 例えば,(h - 1) * (w - 1)が1020 * 1020よりも明らかに小さい場合, 非常に大きな回数だけfilter関数を呼び出さないと初期化のコストを回収できません。 u != 0 && v != 0において, ・c_sqrt[u][v]が0.0であれば計算してc_sqrt[u][v]に代入し,その値をvalとする ・そうでなければc_sqrt[u][v]の値をvalとする とした方が速くなるかもしれません。 また,この時にu <= vなどの条件付けでsqrtの回数を減らせます。 究極の手段は,別途計算ですが……。 ソースコード中に1020 * 1020の計算結果を貼り付けておけば, 実行時のコストが一気に削減できます。 # 今回の場合,そもそもc_sqrtはdoubleではなくunsigned charでいい気がします。

apollograffitti
質問者

お礼

ご回答ありがとうございました. 参考になりました.

  • wormhole
  • ベストアンサー率28% (1619/5653)
回答No.2

#1の方もいわれてますがfilterは実際何回よばれてるのでしょうか? 実行時c_sqrt初期化するのにsqrt()が1020*1020回呼ばれるのですから 1020*1020回以下ならテーブル引きじゃない方が早いですよ。 c_sqrtの内容自体は固定のようですし実行時じゃなく ソース中に初期値書かれた方がよいのではないでしょうか。 他にも直せるとこは ・u, v, c_sqrtはdoubleの必要がなくintで十分、c_sqrtにいたってはunsigned charで十分 ・valを求めるのに必ず4で割るのならc_sqrtには4で割った値を入れとく じゃないでしょうか。 私なら以下のようにしてc_sqrtのソースを作っておいて、 printf("unsigned char c_sqrt[][] = {"); for (int i = 0; i < 1020; i++) { printf("{ "); for (int j = 0; j < 1020; j++) { int val = (int)(sqrt(i * i + j * j)) / 4; if (val > 255) val = 255; printf("%d,", val); } printf("}, "); } printf("};\n"); 使うときには d[y*w+x] = c_sqrt[u][v]; で済ますかな。

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.1

呼び出す側の情報がなければ考えられない, ってのは理解できますか? この関数をどのくらい呼び出しているのですか? どのような引数で呼び出しているのですか?

apollograffitti
質問者

補足

ご回答ありがとうございます. 呼び出す側(main文)はWhileループになってます. 呼び出す側で画像の画素データを読み込んで,この関数に渡してます. この関数は100回くらい呼び出します(画像が100枚). 呼び出す側では filter(img.pixel, dx, dy, img.w, img.h); と宣言してます. 引数に関してですが,img.pixelが関数で出力する画像画素輝度データ,dx,dyがとあるフィルタをX,Y方向にかけた結果画素輝度,img.wとhが画像の幅(=640),高さ(=480)です.pixelは0~255の値をとり1*307200(=640*480)行列,dx,dyは0~1020の値をとり1*307200行列です.この関数はX,Y方向のフィルタ結果をベクトル積にするものです. 呼び出し回数についてですが,最終的にはカメラ画像を呼び出す形になるので無限回に呼び出すと考えていただけると助かります. よろしくお願い致します.

関連するQ&A

  • C言語でsqrt(a^2+b^2)のテーブル引き

    プログラムに悩んでいるものです. とある画像処理のプログラムを組んでいるのですが,処理が遅くテーブル引きを組んでいます. 三角関数などはすんなりできたのですが,質題にもある通りsqrt(a^2+b^2)が実現できず,この場を借りて質問させていただきました. 以下にプログラムの一部を示します. ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー void filter(unsigned char* d, short *dx, short *dy, int w, int h) { ///// テーブル生成 ///// static int c_size = 0; // static 次の呼び出しでも値保持 static double *c_sqrt = NULL; // c_size = 255;              // u,v:0~255 c_sqrt = (double *)malloc(sizeof(double)*c_size*c_size); // 領域確保 for(int i=0; i<c_size; ++i){     // 有りえるすべての値を生成 for(int j=0; j<c_size; ++i){ c_sqrt[i*j] = sqrt( (double)(i*i + j*j) ); } } ///// d = sqrt(dx^2 + dy^2) ///// for(int y = 1; y < h-1; ++y){ for(int x = 1; x < w-1; ++x){ double u = (double)dx[y*w+x]; double v = (double)dy[y*w+x]; int val = (int)c_sqrt[ (int)(u*v) ] /4; if (val>255) val=255; d[y*w+x] = val; } } } ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー 見てご察し頂ける(?)と思いますが,この関数は何回も呼び出すので,上のほうででテーブル引きしようとしてます. ただ明らかなプログラム経験不足のためかうまくいってません. 個人的にはc_sqrtを別途関数c_sqrt(u,v)にしたほうがよいのかと思ってます. どういうプログラム記述をすれば,このテーブル引きが実現できるでしょうか? ご回答,お力添え,よろしくお願い致します.

  • c言語

    c言語で写真の課題を出されたのですが自分のプログラムでは上手くいきません。どこが間違っているのか教えて欲しいです。 自分のプログラム #include<stdio.h> #include<math.h> int main(){ int i,j; double c,d,x,y,z; for(i=0;i<=360;i++){ c=10*cos(i*M_PI/180); d=10*sin(i*M_PI/180); if(c>=0 && d>=0){ for(j=0;j<=1000;j++){ x=0.001*j; y =x*d/c; z=1-x*x-(sqrt(x)+y)*(sqrt(x)+y); if(z<=0.0){break;} } } if(c<=0 && d>=0){ for(j=0;j<=1000;j++){ x=-0.001*j; y=x*d/c; z=1-x*x-(sqrt(-x)+y)*(sqrt(-x)+y); if(z<=0.0){break;} } } if(c<=0 && d<=0){ for(j=0;j<=1000;j++){ x=-0.001*j; y=x*d/c; z=1-x*x-(sqrt(-x)+y)*(sqrt(-x)+y); if(z<=0.0){break;} } } if(c>=0 && d<=0){ for(j=0;j<=1000;j++){ x=0.001*j; y=x*d/c; z=1-x*x-(sqrt(x)+y)*(sqrt(x)+y); if(z<=0.0){break;} } } printf("x=%lf y=%lf z=%lf\n",x,y,z); } return(0); }

  • c言語 パスカルの三角形

    c言語でパスカルの三角形を出力するプログラムを作りたいのですが、上手くいきません。 何を直せばいいのか教えてください。 #include <stdio.h> #define N 10 int main(void){ int i, j = 1, x, y; int d[N][N]; /* 三角形を作成 */ for (i = 1 ; i < N ; i++){ d[i][0] = 1; while (j <= i - 1){ d[i][j] = d[i-1][j-1] + d[i-1][j]; j ++; } } /* 三角形の表示 */ for (y = 0; y < N; y++) { for (x = 0; x < N-y; x++) printf(" "); for (x = 0; x < y; x++) printf("%3d ", d[x][y]); printf("\n"); } return 0; } 実行結果 -2147417616 2665208 1629976532 1627572249 1629101723 1 1629982744 2665256 2665548 3407923 1629345053 1627571017 0 3538997 1629739051 10 1629345053 2665368 3670071 2665384 1629739040 1627927140 2665244 1628040295 57 1628810863 1629476960 1628602749 2665560 2665304 1629345053 0 1629739040 1629740576 1628992224 2 4411498 1628040588 -2147417600 0 1629476960 1629740664 1629739040 1 267574 0

  • C++の質問です

    C++の質問です。 c++をコンパイルしたとき、 出力されるのが次のように3つ同時にされるようにしたいのですが、 1、 名前 ○○○○ 番号 ○○○○ x= {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}の平均 2、 x= {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}の分散 y= {10, 9, 8, 7, 6, 5, 4, 3, 2, 1}の標準偏差 3、 x={1,2,3,4,5,6,7,8,9,10}と y={10,9,8,7,6,5,4,3,2,1}の相関係数 下記のソースをどのように変えればいいでしょうか。 ちなみに、C言語ではなくC++なので C++形式でお願いします。 どうかお願いします。 #include <iostream> #include <cmath> using namespace std; double Mean(int *a, int size); double StandardDeviation(int *a, int size); double CoefficientOfCorrelation(int *a, int *b, int sizeA, int sizeB); int main() { int x[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; int y[] = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1}; cout << " 標準偏差 : " << StandardDeviation(x, sizeof(x) / sizeof(int)) << endl << " 相関係数 : " << CoefficientOfCorrelation(x, y, sizeof(x) / sizeof(int), sizeof(y) / sizeof(int)) << endl; return 0; } double Mean(int *a, int size) { if (size <= 0) { return -1.0; } double d = 0.0; for (int i = 0; i < size; i++) { d += *(a + i); } return d / size; } double StandardDeviation(int *a, int size) { if (size <= 0) { return -1.0; } double mean = Mean(a, size); double d = 0.0; for (int i = 0; i < size; i++) { d += pow(*(a + i) - mean, 2); } return sqrt(d / size); } double CoefficientOfCorrelation(int *a, int *b, int sizeA, int sizeB) { if (sizeA > 0 && sizeB > 0 && sizeA != sizeB) { return -1.0; } double meanX = Mean(a, sizeA); double meanY = Mean(b, sizeB); double sdX = StandardDeviation(a, sizeA); double sdY = StandardDeviation(b, sizeB); double coeff = 0.0; for (int i = 0; i < sizeA; i++) { coeff += (*(a + i) - meanX) * (*(b + i) - meanY); } return (coeff / (sizeA * sdX * sdY)); }

  • このプログラムについて。

    #include <stdio.h> #include <stdlib.h> #include <math.h> #include <time.h> #include <string.h> #define NVALUE 30 #define MAXSIZE NVALUE struct City{ float x; float y; }; struct Country{ struct City cities[MAXSIZE]; int size; }; struct Itine{ float quality; int route[MAXSIZE]; int noc; }; float plength(struct Itine *tour,struct Country *pcountry) { int i,j; double dy; double dx; float length=0.0; for(i=0;i<tour->noc;i++){ j=i+1; if(j==tour->noc) j=0; struct City &c1=pcountry->cities[tour->route[i]-1]; struct City &c2=pcountry->cities[tour->route[j]-1]; double dx = c1.x-c2.x; double dy = c1.y-c2.y; length+=(float)sqrt(dx*dx+dy*dy); } return length; } なんか間違っていますか? エラーメッセージは、この部分 struct City &c1=pcountry->cities[tour->route[i]-1]; struct City &c2=pcountry->cities[tour->route[j]-1]; double dx = c1.x-c2.x; double dy = c1.y-c2.y; が、 ';'が'型'の前にありません と出ています。Visual Express 2005です。

  • 放射状ブラー C言語で書いたのですが結果がうまくいっていない

    こんばんは! Windows環境,VS2005で放射状ブラーを以下の様に書きましたが、 結果が添付ファイルの様になってしまい、うまくいきません。 コンパイルは通り実行もできるのですが、結果がうまくいっていないのです。(いろんな画像で試しましたが明らかに うまくいっていない気がするのです) 参考にしたのは、 http://www.sbcr.jp/books/download/art.asp?newsid=2198 の " 第3章 エフェクト処理の応用(その1) IPP_Chap3a.zipの中にある list3_14.cです。 又、web上では、http://d.hatena.ne.jp/matsu4512/20090726/1248575190 参考にしました。 以下が私が書いたものです。Input=24bppのrawファイル名 幅、高さが入った構造体です。 プログラミング思想としては、1次元配列で画像を表しています。 #define NN 17 #define NF 8 int Main24bppToRadialBlur(COMMONDATA *Common_Data_Raw) { FILE *fpt; FILE *fpt_output; int width=Common_Data_Raw->width; int height=Common_Data_Raw->height; int i; unsigned char *layer,*img_output; int rr,gg,bb,oo; int x1=0; int y1=0; int x2=3*width-3; int y2=3*width*height-3*width; WCHAR DebugStr[256]; double ox,oy; ox=(double)(x2-x1)/2.0; oy=(double)(y2-y1)/2.0; double dx,dy; dx=(double)x2-ox; dy=(double)y2-oy; double disMAX; disMAX=sqrt(dx*dx+dy*dy); int x,y; int xx,yy; double rate,rad=0,dis,disI; int pat_sum,pat; double ef=30.0; _wfopen_s(&fpt,Common_Data_Raw->filename,L"rb"); layer=(unsigned char*)malloc(3*width*height*sizeof(unsigned char)); //読み込み fread(&layer[0],sizeof(unsigned char),3*width*height,fpt); img_output=(unsigned char*)malloc(3*width*height*sizeof(unsigned char)); //◆画像処理をするスペース for(y=y1;y<=3*width*height-3*width;y+=3*width) { for(x=x1;x<=3*width-3;x+=3) { rr=gg=bb=oo=0; dx=(double)x-ox; dy=(double)y-oy; if(dx!=0.0) { rad=atan(dy/dx); } else { rad=3.14159265/2.0; } //rad+=(3.14159265/2.0); dis=sqrt(dx*dx+dy*dy); rate=ef*dis/disMAX; rate/=((double)NF); pat_sum=0; for(i=0;i<NN;i++) { if(i==NF) { pat=3; } else { pat=1; } disI=(double)(i-NF)*rate; xx=(int)(disI*cos(rad))+x; yy=(int)(disI*sin(rad))+y; rr+=layer[xx+yy] * pat; gg+=layer[xx+yy+1]* pat; bb+=layer[xx+yy+2]* pat; oo+= pat; pat_sum+=pat; } img_output[x+y]=rr/(oo); img_output[x+y+1]=gg/(oo); img_output[x+y+2]=bb/(oo); }//x }//y _wfopen_s(&fpt_output,L"RGBToRadialBlur.raw",L"wb"); fwrite(&img_output[0],sizeof(unsigned char),3*width*height,fpt_output); fclose(fpt); fclose(fpt_output); free(layer); free(img_output);

  • C言語の問題

    以下はC言語の問題です。お教えください。 1000以下の素数を求めるプログラム prog.c を作成せよ。各素数を整数4桁で出力し、15個の素数を出力した時点で改行処理 を行うこと。作成したプログラムを提出せよ。 です。 僕の考えでは、 #include <stdio.h> #include <math.h> main(){ int i; int j; int ix; int k; printf("正の整数を入力して下さい: "); scanf("%d",&i); ix=(int)(sqrt((double)i)); k=0; for(j=2;j<=ix;j++) { if(i%j==0) { k=1; } } if(k==0) { printf("%d は素数です\n",i); } else { printf("%d は素数ではありません\n",i); } となると思うのですが。どうやら違うようです。全然わからないので、正しい答えを教えてください。

  • C言語に関して

    C言語に関して 100までの自然数を文字列に変換したいのですが、以下のプログラムを実行すると、001,002,…010,…099,100のようになってしまいます。左詰めにしたいのですが、どこが間違っているかご教示下さい。 #include <stdio.h> #define N1 100 #define N2 5 int get_ketasuu(); void henkankun(); int main(void) { int i, dig, x; int num1 = N1; int num2 = N2; int buff1[N1], buff2[N1]; char buff3[N1][N2]; for (i = 0; i < N1; i++) { x = buff2[i] = buff1[i] = i + 1; dig = get_ketasuu(x); henkankun(&buff2[i], &buff3[i], dig); printf("%s\n", buff3[i]); } return 0; } int get_ketasuu(x) int x; { int dig; dig = 0; do { x /= 10; dig++; } while (x > 0); return dig; } void henkankun(x, y, dig) int *x; int dig; char (*y)[N2]; { int j, k; switch (dig) { case 1 : k = 1; case 2 : k = 10; case 3 : k = 100; } j = 0; do { (*y)[j] = (*x / k) + '0'; *x %= k; k /= 10; j++; } while (k > 0); (*y)[j] = '\0'; }

  • C言語を実行すると-infが出てきて困っています。

    C言語を実行すると-infが出てきて困っています。 コンパイラはgccを使っています。 よろしくお願いします。 #include <stdio.h> #include <math.h> #include <stdlib.h> #define data 100//計算回数 double seiki(void)//正規乱数 { double n; double i; double y; double x1,x2; double sigma; double mean; double Pi = 2*asin(1); sigma = 1; mean = 0; { x1 = (double)rand()/(RAND_MAX); x2 = (double)rand()/(RAND_MAX); y = sigma*sqrt(-2*log(x1))*sin(2*Pi*x2) + mean; //printf("%f\n",y); } return (y); } int main(void) { double* price; int i, j, k; double a, b, c; //メモリ確保 price=(double*)malloc(sizeof(double)*data+10); for(i = 0;i<data;i++) { price[i+1] = price[i]+seiki(); printf("%lf\n",price[i+1]); } return 0; }

  • C言語。どうしてコンパイルできません^^;

    最近プログラミングの勉強をはじめました。 C言語を勉強しています。 /*入力した値の、平均値・最大値・最小値・を出す。*/ #include <stdio.h> int main(void) { int x[5],i,j,w,x,y,z,sum; printf("5つの実数の平均、最大値、最小値を求めます\n"); sum = 0; for(i=0; i<5; i++){ printf("値%d:",i+1); scanf("%d",&x[i]); sum += x[i]; } for(y=0; y<5; y++){ for(j=0; j<4; j++){ w=j+1; if(x[j] < x[w]){ z = x[i]; x[i] = x[w]; x[w] = z; } } } printf("平均値:%f\n最大値:%d\n最小値:%d\n", (double)sum/5, x[0], x[4]); return 0; } Microsoft Visual C++ 2008 Express Edition でコンパイルをしようとしたのですが、 「error C2040: 'x' : 'int' は 'int [5]' と間接操作のレベルが異なります。」 と出てできませんでした^^; 何度も見直したのですが、どうしても間違っている場所がわかりません^^; どこがいけないのでしょうか^^;