7

これは教科書の質問であり、特定のプロセッサアーキテクチャで最高のパフォーマンスを発揮するようにCコードを書き直す必要があります。

与えられたもの:4つの加算器と2つの乗算器ユニットを備えた単一のスーパースカラープロセッサをターゲットにしています。

入力構造(他の場所で初期化):

struct s {
    short a;
    unsigned v;
    short b;
} input[100];

このデータを操作するルーチンは次のとおりです。明らかに正確さを確保する必要がありますが、目標はそれからがらくたを最適化することです。

int compute(int x, int *r, int *q, int *p) {

    int i;
    for(i = 0; i < 100; i++) {

        *r *= input[i].v + x;
        *p = input[i].v;
        *q += input[i].a + input[i].v + input[i].b;
    }

    return i;
}

したがって、このメソッドには、整数r、q、pを更新するための3つの算術命令があります。

これが私が考えていることを説明するコメント付きの私の試みです:

//Use temp variables so we don't keep using loads and stores for mem accesses; 
//hopefully the temps will just be kept in the register file
int r_temp = *r;
int q_temp = *q;

for (i=0;i<99;i = i+2) {
    int data1 = input[i];
    int data2 = input[i+1]; //going to try partially unrolling loop
    int a1 = data1.a;
    int a2 = data2.a;
    int b1 = data1.b;
    int b2 = data2.b;
    int v1 = data1.v;
    int v2 = data2.v;

    //I will use brackets to make my intention clear the order of operations I was planning
    //with respect to the functional (adder, mul) units available

    //This is calculating the next iteration's new q value 
    //from q += v1 + a1 + b1, or q(new)=q(old)+v1+a1+b1

    q_temp = ((v1+q1)+(a1+b1)) + ((a2+b2)+v2);
    //For the first step I am trying to use a max of 3 adders in parallel, 
    //saving one to start the next computation

    //This is calculating next iter's new r value 
    //from r *= v1 + x, or r(new) = r(old)*(v1+x)

    r_temp = ((r_temp*v1) + (r_temp*x)) + (v2+x);
}
//Because i will end on i=98 and I only unrolled by 2, I don't need to 
//worry about final few values because there will be none

*p = input[99].v; //Why it's in the loop I don't understand, this should be correct
*r = r_temp;
*q = q_temp;

さて、私のソリューションは私に何を与えましたか?古いコードを見ると、iの各ループ反復はmax((1A + 1M)、(3A))の最小レイテンシーを取ります。ここで、前者の値は新しいrを計算するためのものであり、3Addsのレイテンシーはqのためのものです。

私のソリューションでは、2ずつ展開し、rとqの2番目の新しい値を計算しようとしています。加算器/乗算器のレイテンシーがM=c * A(cは整数> 1)であると仮定すると、rの乗算演算は間違いなく律速段階なので、それに焦点を当てます。可能な限り乗算器を並列に使用しようとしました。

私のコードでは、中間ステップの計算を支援するために最初に2つの乗算器が並行して使用され、次にそれらを加算する必要があり、最後の乗算が最後の結果を取得するために使用されます。したがって、rの2つの新しい値(最後の値のみを保持/気にしますが)の場合、(1M // 1M // 1A)+ 1A + 1Mかかり、合計レイテンシは2M+1Mになります。2で割ると、ループあたりのレイテンシーの値は1M+0.5Aになります。元のレイテンシー/値(rの場合)を1A+1Mと計算します。したがって、コードが正しければ(これはすべて手作業で行い、まだテストしていません!)、パフォーマンスが少し向上します。

また、ループ内のポインターに直接アクセスして更新しないことで(主に一時変数r_tempとq_tempのおかげで)、ロード/ストアのレイテンシーを節約できることを願っています。


それが私の刺し傷でした。他の人が思いついたものを見ることに間違いなく興味があります。

4

1 に答える 1

3

はい、2 つのショーツを活用することは可能です。構造体を再配置します

struct s {
    unsigned v;
    short a;
    short b;
} input[100];

また、アーキテクチャ上のメモリ フィールドをより適切に配置できる可能性があります。これにより、これらの構造体を同じメモリ ページに配置できるようになり、発生するメモリ ページ フォールトが少なくなる可能性があります。

それはすべて投機的であるため、プロファイルを作成することが非常に重要です。

適切なアーキテクチャを使用している場合、再配置によりデータ構造のアライメントが向上し、必要なパディングで失われるビットが少なくなるため、メモリ内のデータ密度が高くなり、一般的なメモリ アーキテクチャによって課されるデータ境界との型のアライメントが確保されます。

于 2012-09-11T18:42:57.910 に答える