8

この最小限のOpenMPプログラム

#include <omp.h>
int main() 
{
  #pragma omp parallel sections
  {
    #pragma omp section
    {
      while(1) {}
    }

    #pragma omp section
    {  
      while(1) {}
    }
  }
}

コンパイルして実行すると、このエラーが発生しますgcc test.c -fopenmp

Illegal instruction (core dumped)

ループのいずれかをで変更すると

  int i=1;
  while(i++) {}

または、コンパイルしてエラーなしで実行するその他の条件。1異なるスレッドのループ条件として、奇妙な動作が発生するようです。なんで?

編集:私はgcc4.6.3を使用しています

編集:これはgccのバグであり、バグ54017としてgcc開発者に提出されました。

4

2 に答える 2

8

これは明らかにGCCのバグです。GCCは、呼び出し元のスレッドが実行する必要がある、またはすべての作業項目が配布されている場合に実行する必要があるベースのセクションIDを返すGOMP_sections_start()ルーチンを使用してOpenMPセクションを実装します。基本的に、変換されたコードは次のようになります。libgomp10

main._omp_fn.0 (void * .omp_data_i)
{
   unsigned int .section.1;

   .section.1 = GOMP_sections_start(2);
L0:
   switch (.section.1)
   {
      case 0:
         // No more sections to run, exit
         goto L2;
      case 1:
         // Do section 1
         while (1) {}
         goto L1;
      case 2:
         // Do section 2
         while (1) {}
         goto L1;
      default:
         // Impossible section value, possible error in libgomp
         __builtin_trap();
   }
L1:
   .section.1 = GOMP_sections_next();
   goto L0;
L2:
   GOMP_sections_end_nowait();
   return;
}

何が起こるかというと、あなたの場合、defaultとの両方がに0つながるということ__builtin_trap()です。__builtin_trap()は、プログラムを異常終了させることになっているGCCビルトインであり、x86ud2では、CPUが不正なオペコード例外で吠えるようにする命令を発行します。これは通常、コードが実行されてはならない場所に配置されます。たとえば、スイッチからのすべての可能な正しい戻り値はGOMP_sections_start()、スイッチのケースでカバーGOMP_sections_next() れ、デフォルトに達した場合(のバグの可能性を通知libgomp)、失敗し、次のように文句を言います。開発者:)

編集:iccこれは明らかにOpenMPの動作ではなく、またはで発生しませんsunccBug54017をGCCBugzillaに送信しました。

編集2: GCCが生成するものをより厳密に反映するようにテキストを更新しました。GCCは、並列領域での制御フローの印象を誤っており、コード生成をさらに台無しにするいくつかの「最適化」を行っているようです。

于 2012-07-18T15:21:36.133 に答える
4

不正な命令ud2/ud2aがあるため、SIGILLが生成されました。http://asm.inightmare.org/opcodelst/index.php?op=UD2によると:

この命令により#UDが発生しました。Intelは、将来のIntelのCPUでこの命令が#UDを引き起こすことを保証しました。もちろん、以前のすべてのCPU(186+)は、このオペコードで#UDを引き起こしました。この命令は、ソフトウェア作成者が#UD例外サービスルーチンをテストするために使用します。

中を見てみましょう:

$ gcc-4.6.2 -fopenmp omp.c -o omp
$ gdb ./omp
...

(gdb) r
Program received signal SIGILL, Illegal instruction.
...
0x08048544 in main._omp_fn.0 ()
(gdb) x/i $pc
0x8048544 <main._omp_fn.0+28>:  ud2a

(gdb) disassemble
Dump of assembler code for function main._omp_fn.0:
0x08048528 <main._omp_fn.0+0>:  push   %ebp
0x08048529 <main._omp_fn.0+1>:  mov    %esp,%ebp
0x0804852b <main._omp_fn.0+3>:  sub    $0x18,%esp
0x0804852e <main._omp_fn.0+6>:  movl   $0x2,(%esp)
0x08048535 <main._omp_fn.0+13>: call   0x80483f0 <GOMP_sections_start@plt>
0x0804853a <main._omp_fn.0+18>: cmp    $0x1,%eax
0x0804853d <main._omp_fn.0+21>: je     0x8048548 <main._omp_fn.0+32>
0x0804853f <main._omp_fn.0+23>: cmp    $0x2,%eax
0x08048542 <main._omp_fn.0+26>: je     0x8048546 <main._omp_fn.0+30>
0x08048544 <main._omp_fn.0+28>: ud2a
0x08048546 <main._omp_fn.0+30>: jmp    0x8048546 <main._omp_fn.0+30>
0x08048548 <main._omp_fn.0+32>: jmp    0x8048548 <main._omp_fn.0+32>
End of assembler dump.

アセンブラファイルにはすでにud2aがあります。

$ gcc-4.6.2 -fopenmp omp.c -o omp.S -S; cat omp.S

main._omp_fn.0:
.LFB1:
        pushl   %ebp
.LCFI4:
        movl    %esp, %ebp
.LCFI5:
        subl    $24, %esp
.LCFI6:
        movl    $2, (%esp)
        call    GOMP_sections_start
        cmpl    $1, %eax
        je      .L4
        cmpl    $2, %eax
        je      .L5
                .value  0x0b0f

.value 0xb0fud2aのコードです

ud2aがgccの意図で挿入されたことを確認した後(初期のopenmpフェーズで)、コードを理解しようとしました。関数main._omp_fn.0は並列コードの本体です。戻りコードを呼び出し _GOMP_sections_start て解析します。コードが1に等しい場合、1つの無限ループにジャンプします。2の場合は、2番目の無限ループにジャンプします。ただし、それ以外の場合はud2aが実行されます。(理由はわかりませんが、Hristo Ilievによると、これはGCCバグ54017です。)

このテストは、CPUコアの数を確認するのに適していると思います。デフォルトでは、GCCのopenmpライブラリ(libgomp)は、システム内のすべてのCPUコアに対してスレッドを開始します(私の場合は4つのスレッドがありました)。そして、セクションは順番に選択されます:最初のスレッドの最初のセクション、2番目のセクション-2番目のスレッドなど。

プログラムを1つまたは2つのCPUで実行した場合、SIGILLはありません(タスクセットのオプションは16進数のCPUマスクです)。

 $ taskset 3 ./omp
 ... running on cpu0 and cpu1 ...
 $ taskset 1 ./omp
 ... running first loop on cpu0; then run second loop on cpu0...
于 2012-07-18T14:46:40.410 に答える