registerキーワードがCで実際に役立つのはいつですか?

Cでのregisterキーワードの使用について混乱しています。一般的には使用されていないと言われています。 “スタックオーバーフローに関するこの質問のように、が必要です。

このキーワードは、最新のコンパイラのためにCで完全に冗長ですか、それとも状況がありますか。それでも役立つ可能性がありますか?はいの場合、registerキーワードの使用が実際に役立つ状況は何ですか?

コメント

  • リンクされた質問とその回答は、ここで期待できるものと同じだと思います。したがって、ここで取得できる新しい情報はありません。
  • @UwePlonus constキーワードについても同じですが、この質問は私が間違っていることを証明しました。したがって、私は’待って、何が得られるかを確認します。
  • constキーワードは登録とは異なるものだと思います。
  • それ’は、誤って過去にさかのぼり、初期のCコンパイラの1つを使用せざるを得ない場合に役立ちます。 ‘まったく役に立たないことを除けば、’は何年もの間完全に廃止されています。
  • @ UwePlonusキーワードが役立つかもしれない、私には知られていないシナリオがあるかもしれないということを意味しました。

回答

言語の面で冗長ではありません。それを使用することで、コンパイラに変数をレジスタに格納することを「好む」と言っているだけです。ただし、これが実際に行われるという保証はまったくありません。実行時に発生します。

コメント

  • それ以上に、’ほとんどの場合次のようになります。コンパイラーは最もよく知っており、’息を無駄にしている
  • @jozefg:さらに悪いことに、コンパイラーが要求/ヒントを尊重してより悪いコード。

回答

すでに述べたように、コンパイラオプティマイザは本質的にレンregisterキーワードは、エイリアシングの防止以外の目的で廃止されました。ただし、最適化をオフにしてコンパイルされたコードベース全体があります( gcc-speak -O0)。このようなコードの場合、registerキーワードが大きな効果を発揮します。具体的には、スタック上でスロットを取得する変数(つまり、すべての関数パラメーターと自動変数)は、register divで宣言されている場合、レジスターに直接配置できます。 >キーワード。

実際の例:データベースの取得が行われ、取得コードが取得したタプルをC構造体に詰め込んだと仮定します。さらに、このC構造体のサブセットがあると仮定します。別の構造体にコピーする必要があります。おそらく、この2番目の構造体は、データベースに格納されているメタデータを表すキャッシュレコードであり、メモリの制約により、データベースに格納されている各メタデータレコードのサブセットのみをキャッシュします。

各構造体タイプへのポインターを受け取り、その唯一の仕事が最初の構造体から2番目の構造体にいくつかのメンバーをコピーすることである関数を考えると、構造体ポインター変数はスタック上に存在します。割り当ては1つの構造体のメンバーから発生します。他のアドレスに対して、構造体のアドレスは、割り当てごとに、コピーされている構造体のメンバーのアクセスを実行するために、レジスタにロードされます。構造体ポインタがregisterキーワードで宣言された場合、構造体のアドレスはレジスタに残り、各割り当てのload-address-into-register命令が効果的に削除されます。

繰り返しになりますが、上記の説明は最適化されていないコードに適用されることに注意してください。

回答

基本的に、変数のアドレスを取得しないことをコンパイラーに指示すると、コンパイラーは表面上は作成できます。さらなる最適化。私が知る限り、最近のコンパイラは、変数をレジスタに保持できるかどうかを判断することができます。

例:

int main(){ int* ptr; int a; register int b; ptr = &a; ptr = &b; //this won"t compile return 0; } 

コメント

  • 参照を解除するか、アドレスを取得しますか?
  • @detly:もちろん正しいです

回答

16ビットコンピュータの時代には、32ビットの乗算と除算を実行するために複数のレジスタが必要になることがよくありました。フローティングポイントユニットがチップに組み込まれ、64ビットアーキテクチャが「引き継いで」、レジスタの幅と数の両方が拡張されました。これにより、最終的にCPUが完全に再構築されます。ウィキペディアでファイルを登録します。

要するに、理解するのに少し時間がかかります。 64ビットX86またはARMチップを使用している場合は、実際に何が起こっているのかを確認してください。「16ビットの組み込みCPUを使用している場合、これで実際に何かが得られる可能性があります。ただし、ほとんどの小さな組み込みチップは、タイムクリティカルなものは何も実行しません。電子レンジがタッチパッドを1秒間に10,000回サンプリングしている可能性があります。 4MhzCPU。

コメント

  • 4 MIPS / 10,000ポーリング/秒= 400命令/ポール。その’は、’が望むほどのマージンではありません。また、かなりの数の4 MHzプロセッサが内部でマイクロコード化されていることにも注意してください。つまり、1 MIP / MHzにはほど遠いことです。
  • @ JohnR.Strohm-命令の正確な数を把握することを正当化できる状況があるかもしれません。 ‘がかかるサイクルですが、多くの場合、現在のより安価な方法は、より高速なチップを入手して製品をドアから出すことです。もちろん、与えられた例では、コマンドがある場合、’ 10,000でサンプリングを継続する必要はありません。害を及ぼすことなく1/4秒間サンプリングを再開しない可能性があります。完了しました。プログラマー主導の最適化がどこで重要になるかを理解することはますます困難になっています。
  • “より高速なチップを入手して、ドアの外の製品”。リアルタイムの画像処理を検討してください。 640x480ピクセル/フレームx60フレーム/秒xピクセルあたりN個の命令がすばやく追加されます。 (リアルタイム画像処理からの教訓は、ピクセルカーネルに血を流し、他のすべてを無視することです。これは、行ごとに数百回、またはパッチまたはフレームあたり数万回または数十万回。)
  • @ JohnR.Strohm-リアルタイムの画像処理の例をとると、最小環境は32ビットだと思います。手足に出かける(’これがどれほど実用的かわからないため)チップに組み込まれている多くのグラフィックアクセラレータも画像認識に使用できる可能性があるため、ARMチップ(たとえば)レンダリングエンジンが統合されている場合、認識に使用できる追加のALUがある場合があります。その時までに、最適化のために’ register ‘キーワードを使用することは問題のごく一部です。

回答

registerキーワードに意味があるかどうかを確認するために、小さなサンプルコードでは機能しません。これがcコードです。これは私に示唆しているように、registerキーワードにはまだ意味があります。しかし、Linux上のGCCでは異なるかもしれません。私にはわかりません。レジスタintk & lはCPUレジスタに格納されますか? Linuxユーザーは(特に)GCCと最適化を使用してコンパイルする必要があります。 Borland bcc32では、&演算子がレジスタで宣言された整数のエラーコードを提供するため、registerキーワードが機能しているように見えます(この例では)。注意!これは、Windows上のBorlandの小さな例には当てはまりません。コンパイラが何を最適化するかを実際に確認するには、それは小さな例以上のものでなければなりません。空のループは実行されません!それでも、アドレスが&演算子で読み取れる場合、変数はCPUレジスタに格納されません。しかし、レジスタで宣言された変数を読み取れない場合(コンパイル時にエラーコードが発生する)-registerキーワードが実際に変数をCPUレジスタに配置していると推測する必要があります。プラットフォームによって異なる可能性があるので、わかりません。 。 (それが機能する場合、「ティック」の数はレジスタ宣言ではるかに少なくなります。

/* reg_or_not.c */ #include <stdio.h> #include <time.h> #include <stdlib> //not requiered for Linux #define LAPSb 50 #define LAPS 50000 #define MAXb 50 #define MAX 50000 int main (void) { /* 20 ints and 2 register ints */ register int k,l; int a,aa,b,bb,c,cc,d,dd,e,ee,f,ff,g,gg,h,hh,i,ii,j,jj; /* measure some ticks also */ clock_t start_1,start_2; clock_t finish_1,finish_2; long tmp; //just for the workload /* pointer declarations of all ints */ int *ap, *aap, *bp, *bbp, *cp, *ccp, *dp, *ddp, *ep, *eep; int *fp, *ffp, *gp, *ggp, *hp, *hhp, *ip, *iip, *jp, *jjp; int *kp,*lp; /* end of declarations */ /* read memory addresses, if possible - which can"t be done in a CPU-register */ ap=&a; aap=&aa; bp=&b; bbp=&bb; cp=&c; ccp=&cc; dp=&d; ddp=&dd; ep=&e; eep=&ee; fp=&f; ffp=&ff; gp=&g; ggp=&gg; hp=&h; hhp=&hh; ip=&i; iip=&ii; jp=&j; jjp=&jj; //kp=&k; //won"t compile if k is stored in a CPU register //lp=&l; //same - but try both ways ! /* what address , isn"t the issue in this case - but if stored in memory some "crazy" number will be shown, whilst CPU-registers can"t be read */ printf("Address a aa: %u %u\n",a,aa); printf("Address b bb: %u %u\n",b,bb); printf("Address c cc: %u %u\n",c,cc); printf("Address d dd: %u %u\n",d,dd); printf("Address e ee: %u %u\n",e,ee); printf("Address f ff: %u %u\n",f,ff); printf("Address g gg: %u %u\n",g,gg); printf("Address h hh: %u %u\n",h,hh); printf("Address i ii: %u %u\n",i,ii); printf("Address j jj: %u %u\n\n",j,jj); //printf("Address k: %u \n",k); //no reason to try "k" actually is in a CPU-register //printf("Address l: %u \n",l); start_2=clock(); //just for fun /* to ensure workload */ for (a=1;a<LAPSb;a++) {for (aa=0;aa<MAXb;aa++);{tmp+=aa/a;}} for (b=1;b<LAPSb;b++) {for (bb=0;bb<MAXb;bb++);{tmp+=aa/a;}} for (a=1;c<LAPSb;c++) {for (cc=0;cc<MAXb;cc++);{tmp+=bb/b;}} for (d=1;d<LAPSb;d++) {for (dd=0;dd<MAXb;dd++);{tmp+=cc/c;}} for (e=1;e<LAPSb;e++) {for (ee=0;ee<MAXb;ee++);{tmp+=dd/d;}} for (f=1;f<LAPSb;f++) {for (ff=0;ff<MAXb;ff++);{tmp+=ee/e;}} for (g=1;g<LAPSb;g++) {for (gg=0;gg<MAXb;gg++);{tmp+=ff/f;}} for (h=1;h<LAPSb;h++) {for (hh=0;hh<MAXb;hh++);{tmp+=hh/h;}} for (jj=1;jj<LAPSb;jj++) {for (ii=0;ii<MAXb;ii++);{tmp+=ii/jj;}} start_1=clock(); //see following printf for (i=0;i<LAPS;i++) {for (j=0;j<MAX;j++);{tmp+=j/i;}} /* same double loop - in supposed memory */ finish_1=clock(); //see following printf printf ("Memory: %ld ticks\n\n", finish_1 - start_1); //ticks for memory start_1=clock(); //see following printf for (k=0;k<LAPS;k++) {for (l=0;l<MAX;l++);{tmp+=l/k;}} /* same double loop - in supposed register*/ finish_1=clock(); //see following printf printf ("Register: %ld ticks\n\n", finish_1 - start_1); //ticks for CPU register (?) any difference ? finish_2=clock(); printf ("Total: %ld ticks\n\n", finish_2 - start_2); //really for fun only system("PAUSE"); //only requiered for Windows, so the CMD-window doesn"t vanish return 0; } 

コメント

  • 上記のゼロの除算があります。{tmp + = ii / jj;}を{tmp + = jj / ii;}に変更してください-本当に申し訳ありません
  • kとiも1で始まります-ゼロではありません。申し訳ありません。
  • コメントに訂正を書き込む代わりに、回答を編集できます。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です