2

N-queens 計算用の単純な C プログラムがあります。OpenMPで並列化しました。ここで、シリアル バージョンとパラレル バージョンの両方を実行し、速度アップを計算します。ポイントは、シリアル コード用の新しいファイルを作成したくない、または OpenMP ディレクティブを使用せずにソリューションを新しい関数にコピーしたくないということです。私がやりたいことは、1 つの関数を保持し、それをいつシリアルとして実行し、いつ並列として実行するかをメインから伝えることです。私はプリプロセッサを使用することを考えていますが、それが可能かどうかは確信しています。可能であれば、どうすれば実現できますか。

void solve() 
{
    int i;

    #if PARALLEL == 1
      #pragma omp parallel for
    #endif
    for(i = 0; i < size; i++) {
        int *queens = (int*)malloc(sizeof(int)*size);
        setQueen(queens, 0, i);
        free(queens);
    }
}


int main()
{
   ...

    #define PARALLEL 0
    st_start = clock();
    solve();
    st_end = clock();

    #define PARALLEL 1
    pt_start = omp_get_wtime();
    solve();
    pt_end = omp_get_wtime();

    ...
}
4

3 に答える 3

1

編集:プリプロセッサを使用してこれを行う方法を考えました。これにより、重複コードの問題が修正されますが、コンパイルとリンクが少し複雑になります。コンパイラで OpenMP が有効になっていない場合、OpenMP コンストラクトが無視されるという機能を使用します。

#include <stdlib.h>

void setQueen(int* x, int y, int z) {
   /*code*/
}
#if defined _OPENMP
void solve_parallel(const int size)
#else
void solve_serial(const int size)
#endif
{
    int i;
    #pragma omp parallel for      
    for(i = 0; i < size; i++) {
        int *queens = (int*)malloc(sizeof(int)*size);
        setQueen(queens, 0, i);
        free(queens);
    }
}

でコンパイル

gcc -O3 -c foo.c -o solve_serial
gcc -O3 -fopenmp -c foo.c solve_parallel

次に、関数ポインターを使用して以下のようなメイン関数を使用し、solve_serial および solve_parallel オブジェクト ファイルにリンクできます。

別のオプションは、次のようにスレッド数を渡すことです。

void solve(const int nthreads)
{
    int i;
    const int size = 10;
    #pragma omp parallel for num_threads(nthreads)
    for(i = 0; i < size; i++) {
        int *queens = (int*)malloc(sizeof(int)*size);
        setQueen(queens, 0, i);
        free(queens);
    }
}

ただし、nthreads=1 の場合でも、コンパイラは OpenMP コンストラクトを挿入する必要があります。これにより、OpenMP でコンパイルしない場合と比較してパフォーマンスが低下する可能性があり、したがって偏った比較が行われる可能性があります。

より公平な解決策は、OpenMP を使用する場合と使用しない場合で 2 つの関数を定義し、関数ポインターの配列を使用することです (以下を参照)。これは、最適化のために比較したい関数のバリエーションがいくつかある場合に便利です。

#include <stdlib.h>
#include <omp.h>
void solve_parallel(const int size)
{
    int i;
    #pragma omp parallel for
    for(i = 0; i < size; i++) {
        int *queens = (int*)malloc(sizeof(int)*size);
        setQueen(queens, 0, i);
        free(queens);
    }
}

void solve_serial(const int size)
{
    int i;
    for(i = 0; i < size; i++) {
        int *queens = (int*)malloc(sizeof(int)*size);
        setQueen(queens, 0, i);
        free(queens);
    }
}

int main(void) {
    const int size = 100;
    int i;
    double dtime[2];
    void (*solve[2])(int);

    solve[0] = solve_serial;
    solve[1] = solve_parallel;

    solve[1](size);  /* run OpenMP once to warm it up */

    for(i=0; i<2; i++) {
        dtime[i] = omp_get_wtime();
        solve[i](size);
        dtime[i] = omp_get_wtime() - dtime[i];
    }

    return 0;
}
于 2013-12-04T12:24:28.250 に答える
0

コンパイル プロセスの最初の部分であるプリプロセッサを呼び出します。これで、すべてのインクルージョンが解決され、すべての先行ディレクティブが解決され、定数がその値に置き換えられます...

したがって、プリプロセッサ ディレクティブを使用してランタイムの決定を行うことはできません。コンパイル時の決定のみを行うことができます。

于 2013-12-04T17:05:48.327 に答える
0

残念ながら、そのようにすることはできません。

プリプロセッサはコードをスキャンして #stuff を置き換えます。これが完了すると、コンパイラはコードをコンパイルし、#this には何もありません

したがって、投稿したコードでは、プリプロセッサは最初の行から開始し、PARALLEL が 1 の場合はコードに #pragma を実行し、次に main に進み、PARALLEL を 0 に定義してから 1 に定義します。

main から開始してから solve() に入るわけではありません。

OpenMP: #pragma の条件付き使用をご覧になることをお勧めします。

あなたは試すことができます

void solve(int paral) 
{
    int i;


    #pragma omp parallel for if (paral == 1)

    for(i = 0; i < size; i++) {
        int *queens = malloc(sizeof(int)*size);
        setQueen(queens, 0, i);
        free(queens);
    }
}

私はこのコードを試したことがなく、OMP の経験もありませんが...

于 2013-12-04T11:19:24.387 に答える