次のコードスニペットを検討してください
double *x, *id;
int i, n; // = vector size
// allocate and zero x
// set id to 0:n-1
for(i=0; i<n; i++) {
long iid = (long)id[i];
if(iid>=0 && iid<n && (double)iid==id[i]){
x[iid] = 1;
} else break;
}
このコードはid
、typeのvectorの値をvectorへのdouble
インデックスとして使用しますx
。インデックスが有効であるためには、インデックスが0以上、ベクトルサイズn未満であり、に格納されているdoubleid
が実際には整数であることを確認します。この例id
では、1からnまでの整数を格納するため、すべてのベクトルに線形にアクセスし、if
ステートメントの分岐予測が常に機能するはずです。
コードの場合n=1e8
、私のコンピューターでは0.21秒かかります。計算上軽量なループのように思われるので、メモリ帯域幅に制限があると思います。ベンチマークされたメモリ帯域幅に基づいて、0.15秒で実行されると予想しています。id
メモリフットプリントは、値ごとに8バイト、値ごとに16バイトとして計算しx
ます(SSEストリーミングは使用されていないと想定しているため、書き込みとメモリからの読み取りの両方が必要です)。したがって、ベクトルエントリごとに合計24バイトです。
質問:
- このコードはメモリ帯域幅を制限する必要があり、改善できると言っているのは間違っていますか?
- そうでない場合は、メモリの速度で動作するようにパフォーマンスを向上させる方法を知っていますか?
- それとも、すべてが正常で、並行して実行する以外に簡単に改善することはできませんか?
のタイプを変更することはオプションでid
はありません-それはオプションでなければなりませんdouble
。また、一般的なケースid
では、x
サイズが異なり、別々の配列として保持する必要があります。これらは、プログラムのさまざまな部分から取得されます。要するに、境界チェックと型キャスト/整数検証をより効率的な方法で書くことが可能かどうか疑問に思います。
便宜上、コード全体は次のとおりです。
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
static struct timeval tb, te;
void tic()
{
gettimeofday(&tb, NULL);
}
void toc(const char *idtxt)
{
long s,u;
gettimeofday(&te, NULL);
s=te.tv_sec-tb.tv_sec;
u=te.tv_usec-tb.tv_usec;
printf("%-30s%10li.%.6li\n", idtxt,
(s*1000000+u)/1000000, (s*1000000+u)%1000000);
}
int main(int argc, char *argv[])
{
double *x = NULL;
double *id = NULL;
int i, n;
// vector size is a command line parameter
n = atoi(argv[1]);
printf("x size %i\n", n);
// not included in timing in MATLAB
x = calloc(sizeof(double),n);
memset(x, 0, sizeof(double)*n);
// create index vector
tic();
id = malloc(sizeof(double)*n);
for(i=0; i<n; i++) id[i] = i;
toc("id = 1:n");
// use id to index x and set all entries to 4
tic();
for(i=0; i<n; i++) {
long iid = (long)id[i];
if(iid>=0 && iid<n && (double)iid==id[i]){
x[iid] = 1;
} else break;
}
toc("x(id) = 1");
}