Intel CPUとAMD CPUの混在による問題に直面した話

Created
Aug 7, 2023 6:42 AM
Tags
CPU
Editor
Tomoya Kabe
image

チーフエンジニアの加辺です。

今日は珍しいトラブルに出会ったので紹介します。

起こった問題

ある環境ではEC2によりサーバを運用しており、アプリケーションをデプロイサーバでビルドし、その成果物をアプリケーションサーバへコピーすることでデプロイとしていました。

ここで新規サーバを作成していたところ、一部のアプリケーションサーバで見慣れないエラーが発生し、アプリケーションが起動しないという事象が確認されました。調べたところ、デプロイサーバはt3、アプリケーションサーバはt3aファミリーが利用されていることがわかり、アプリケーションサーバをt3ファミリーへ変更したところ、問題が発生しなくなることが分かりました。

さて、これはどのような機序によるものでしょうか。

調査

記事タイトルで答えをほとんど書いてしまっていますし、t3, t3aを知っていれば明らかですが、これはIntelとAMDの違いです。

本アプリケーションはRubyで記述されており、Rubyは以下のようなログを書き出していました。適宜抜粋します。

2021-12-10 23:58:11.000759500 /path/to/app/shared/bundle/ruby/2.6.0/gems/ffi-1.15.4/lib/ffi/library.rb:112: [BUG] Illegal instruction at 0x00007f1758f39b10
2021-12-10 23:58:11.000789500 ruby 2.6.6p146 (2020-03-31 revision 67876) [x86_64-linux]
2021-12-10 23:58:11.000790500
2021-12-10 23:58:11.000941500 -- Control frame information -----------------------------------------------
2021-12-10 23:58:11.000962500 c:0053 p:---- s:0283 e:000282 CFUNC  :open
2021-12-10 23:58:11.001018500 c:0052 p:0022 s:0277 e:000276 BLOCK  /path/to/app/shared/bundle/ruby/2.6.0/gems/ffi-1.15.4/lib/ffi/library.rb:112 [FINISH]
2021-12-10 23:58:11.001079500 c:0051 p:---- s:0268 e:000267 CFUNC  :each
2021-12-10 23:58:11.001131500 c:0050 p:0113 s:0264 e:000263 BLOCK  /path/to/app/shared/bundle/ruby/2.6.0/gems/ffi-1.15.4/lib/ffi/library.rb:109 [FINISH]
2021-12-10 23:58:11.001191500 c:0049 p:---- s:0257 e:000256 CFUNC  :map
2021-12-10 23:58:11.001243500 c:0048 p:0069 s:0253 e:000252 METHOD /path/to/app/shared/bundle/ruby/2.6.0/gems/ffi-1.15.4/lib/ffi/library.rb:99

0x00007f1758f39b10番地に不正な命令があるとのことです。このログだけを見ると libffi に原因があるように見えます。

ところが実際にこの番地が何者かを確認してみると、libffiではありませんでした。

2021-12-10 23:58:11.022654500 7f1758eb6000-7f17590a3000 r-xp 00000000 103:01 25313550                  /path/to/app/shared/bundle/ruby/2.6.0/gems/sassc-2.2.1/lib/sassc/libsass.so

sasscがmapされていました。さらにこの共有オブジェクトの中身を調べ、どんな命令なのか見てみます。

0x00007f1758f39b10 - 0x7f1758eb6000 = 0x83B10です。

0000000000083b10 <_ZN4Sass10ExpressionC1ENS_11ParserStateEbbbNS0_4TypeE>:
   83b10:       62 f1 fe 08 6f 84 24    vmovdqu64 0x8(%rsp),%xmm0
   83b17:       08 00 00 00
   83b1b:       48 8b 44 24 18          mov    0x18(%rsp),%rax
   83b20:       48 c7 47 08 00 00 00    movq   $0x0,0x8(%rdi)
   83b27:       00

ということで、VMOVDQU64という命令であることがわかりました。

新しい命令

これを調べると、AVX-512の命令であることがわかりました。

https://www.xlsoft.com/jp/products/intel/compilers/manual/cpp_all_os/GUID-312480DB-17AE-41A5-BEB1-AD92FF3830D0.htm

アライメントされていないメモリー位置からパックド int64 要素をロードします。

通常はAMD64でもIntel64の命令はサポートされているはず…と思ったのですが、AVX-512の命令は2021年3月に発表されたZen-4以降でようやくサポートされたばかりだったようです。

https://www.techpowerup.com/279129/amd-zen-4-microarchitecture-to-support-avx-512

This would make "Zen 4" the first AMD microarchitecture to support AVX-512.

Intel64でsasscをコンパイルした際にAVX-512の命令が入ってしまい、それをAMD64で動かそうとした結果だったようです。

とりうる対応

いくつもありますが、例として以下のような方法が考えられます。

  1. CPU familyをIntel64に揃える(AVX-512はSkylake-SP以降)
  2. デプロイサーバをAMD64にする(今回の問題の逆パターンも考えられ、問題が起こる可能性が0になるわけではない)
  3. ビルドするとき(今回であればgemをbundleinstall)にgccのオプションに --disable-avx512 を付ける
  4. AWSがAMD EPYCのプロセッサを全てZen-4以降に置き換えてくれるのを待つ
  5. 各サーバでnativeなバイナリをビルドするようにする

今回、手っ取り早くできる&コストが抑えられる対応として、2つ目の方法をとりました。