0

だから私は、Cilk を使用して C で並列クイックソートの実装に取り​​組んでおり、奇妙な問題に遭遇しています。参考までに、私のコードの関連部分(長さについては事前にお詫びします):

#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <math.h>
#include <cilk/cilk.h>
#include <cilk/cilk_api.h>

void prefixSum(int* input,int* output, int length){
   if(length == 1){
      output[0] = input[0];
      return;
   }
   int i;
   int nPairs = (int) floor(((double)length)/2);
   int pairs = (int) ceil(((double)length)/2);
   int z[pairs];
   int w[pairs];
   cilk_for(i=0;i<nPairs;i++){
      z[i] = input[2*i]+input[2*i+1];
   }

   if(pairs>nPairs){
      z[pairs-1] = input[length-1];
   }
   prefixSum(z,w,pairs);
   cilk_for(i=0;i<length;i++){
      if(i==0){
         output[i] = input[i];
      } 
      else if((i-1)%2==0){
         output[i] = w[(i-1)/2];
      }
      else{
         output[i] = w[(i-2)/2] + input[i];
      }
   }
   return;
}

void prefixScan(int* input, int length){
   int i;
   for(i=length-1;i>0;i--){
      input[i] = input[i-1];
   }
   input[0] = 0;
}

void paraSort(double* array, int length){
   if(length==1){
      return;
   }
   int pivot = rand() % length;
   int lowSet[length];
   int highSet[length];
   int equalSet[length];
   int i;
   cilk_for(i=0;i<length;i++){
      if(array[i]==array[pivot]){
         lowSet[i] = 0;
         highSet[i] = 0;
         equalSet[i] = 1;
      } else if(array[i]<array[pivot]){
         lowSet[i] = 1;
         highSet[i] = 0;
         equalSet[i] = 0;
      } else {
         lowSet[i] = 0;
         highSet[i] = 1;
         equalSet[i] = 0;
      }
   }
   int lowIndex[length];
   int highIndex[length];
   int equalIndex[length];
   prefixSum(lowSet,lowIndex,length);
   int numLow = lowIndex[length-1];
   prefixScan(lowIndex,length);

   prefixSum(highSet,highIndex,length);
   int numHigh = highIndex[length-1];
   prefixScan(highIndex,length);

   prefixSum(equalSet,equalIndex,length);
   int numEqual = equalIndex[length-1];
   prefixScan(equalIndex,length);

   double lowList[imin(numLow,1)];
   double highList[imin(numHigh,1)];
   double equalList[numEqual];

   cilk_for(i=0;i<length;i++){
      if(lowSet[i]==1){
         lowList[lowIndex[i]] = array[i];
      } else if(highSet[i]==1){ 
         highList[highIndex[i]] = array[i];
      } else if(equalSet[i]==1){
         equalList[equalIndex[i]] = array[i];
      }
   }

   if(numLow>0 && numHigh>0){
      cilk_spawn paraSort(lowList,numLow);
      paraSort(highList,numHigh);
      cilk_sync;
   } else if(numLow==0 && numHigh>0){
      paraSort(highList,numHigh);
   } else if(numLow>0 && numHigh==0){
      paraSort(lowList,numLow);
   }
   cilk_for(i=0;i<length;i++){
      if(i<numLow){
         array[i] = lowList[i];
      } else if(i<numLow+numEqual){
         array[i] = equalList[i-numLow];
      } else {
         array[i] = highList[i-(numLow+numEqual)];
      }
   }
   return;
}

ここで、これを 50 要素のテスト ケースで実行すると (デバッグを容易にするために順次)、1 つのレイヤーを再帰の奥深くまで進めてから、範囲外のインデックスが原因であると思われるセグメンテーション エラーに遭遇します。ラインequalList[equalIndex[i]] = array[i];

さらに調べてみると、 equalIndex を割り当てた直後、その中の値は完全に任意です。これは当然のことです。まだ何も割り当てていません。prefixSum は、1 である最後から 2 番目の要素を除いてすべてゼロである要素のリストで呼び出されます。(これは、ピボットと等しい要素をマークするビットマップです。) これは、プレフィックス合計操作の結果を equalIndex に入れます。これを配列へのポインターとして渡し、結果が呼び出しの外でも持続するようにします。

これを行った後、debug printf コマンドは、equalIndex が最後の 2 つの要素 (両方とも 1) を除いてすべてゼロになっていることを示します。これは予想されるプレフィックス合計の結果です。ここまでは順調ですね。prefixScan は、ゼロからインデックス作成に取り組むのに役立つ単純なヘルパー関数です。指定された配列内のすべての要素を 1 スペース右に移動し、最初の要素をゼロで埋めます。これに equalIndex を渡した後、デバッグ ステートメントは、1 である最後の要素を除いて equalIndex がすべてゼロであることを示します。

問題が発生するのは、各要素を適切な配列にコピーする cilk_for ループの直後です。このループの本体で、printf ステートメントは、以前の値とは少しでも一致しない値を表示するようになりました。一部は適切にゼロであり、その他は、この配列を prefixSum で初期化する前に見たような非常に大きな正または負の整数です。 . これらの極端な値の 1 つに到達し、それを配列インデックスとして使用しようとすると、プログラムは正しくクラッシュします。

私の最善の推測は、どういうわけか equalIndex の値が適切に割り当てられていないことです (したがって、配列を初期化していないかのような奇妙な動作です)。

4

1 に答える 1

0

あなたは自分自身と矛盾しています。ある時点であなたは言う

equalIndex は、1 である最後の要素を除いてすべてゼロです。

しかし、後であなたはそれを推測します

どういうわけか equalIndex の値が適切に割り当てられていません

. 両方の方法を持つことはできません。

Cilk についてはわかりませんが、C から推測すると、自分のデータを破棄していると思いがちです。特に、問題の場所と思われる次のコードを検討してください。

double lowList[imin(numLow,1)];
double highList[imin(numHigh,1)];
double equalList[numEqual];

cilk_for(i=0;i<length;i++){
    if(lowSet[i]==1){
       lowList[lowIndex[i]] = array[i];
    } else if(highSet[i]==1){ 
       highList[highIndex[i]] = array[i];
    } else if(equalSet[i]==1){
       equalList[equalIndex[i]] = array[i];
    }
}

配列lowListとの長さはhighList? 答える前に彼らの宣言を調べてください。これらの配列の境界外の要素に値を代入しようとすると、どうなると思いますか?

于 2015-12-04T21:32:06.777 に答える