为什么英特尔Haswell XEON CPU偶尔会计算错误的FFT和ART?

37

最近,我注意到我的新工作站出现了一种无法解释的行为。通过对这个问题进行一些研究,可能存在一个INTEL Haswell architecture和当前的Skylake Generation中的潜在漏洞。

在谈论可能的漏洞之前,让我先给你介绍一下使用的硬件、程序代码和问题本身。

工作站硬件规格

  • INTEL Xeon E5-2680 V3 2500MHz 30M Cache 12Core
  • Supermicro SC745 BTQ -R1K28B-SQ
  • 4 x 32GB ECC Registered DDR4-2133内存
  • INTEL SSD 730系列480 GB
  • NVIDIA Tesla C2075
  • NVIDIA TITAN

涉及的操作系统和程序代码

我目前正在运行Ubuntu 15.04 64位桌面版本,最新的更新和内核组件已安装。除了使用这台计算机开发CUDA内核和其他东西之外,我最近测试了一个纯C程序。

该程序在相当大的数据输入集上执行一种修改后的ART。因此,代码执行一些FFT并消耗相当多的时间来完成计算。由于这是无法发布的正在进行的研究,因此我目前无法发布/链接到任何源代码。如果您不熟悉ART,请简单解释一下它的作用。ART是一种技术,用于重建从计算机断层扫描仪接收到的数据,以获得可见的诊断图像。因此,我们的代码版本重构了大小为2048x2048x512的数据集。到目前为止,还没有涉及任何特殊或高级的科学技术。经过几个小时的调试和修复错误,该代码已在参考结果上进行了测试,并且我们可以确认代码按照预期工作。代码唯一使用的库是标准math.h。没有特殊的编译参数,也没有可能带来额外问题的其他库。

观察问题

该代码使用一种技术来最小化重建数据所需的投影,实现了ART。因此,让我们假设我们可以重建涉及25个投影的数据切片。该代码在12个核心上以完全相同的输入数据启动。请注意,该实现不是基于多线程的,目前启动了12个程序实例。我知道这不是最好的方法,强烈建议涉及适当的线程管理,这已经列在改进列表中 :)

因此,当我们运行至少两个程序实例(每个实例处理一个单独的数据切片)时,结果中有一些投影以随机方式出错。为了让您了解结果,请参见Table1。请注意,输入数据始终相同。

仅运行涉及一个CPU核心的代码实例时,所有结果都是正确的。即使进行一些涉及一个CPU核心的运行,结果仍然正确。只有涉及两个或更多核心才会生成如表1所示的结果模式。

Table1: randomly wrong results from Haswell XEON CPU

问题的识别

好的,我们花了相当长时间来了解实际上出了什么问题。因此,我们检查了整个代码,大多数问题都始于小的实现错误。但是,我们不能证明不存在错误,也不能保证不存在错误。为了验证我们的代码,我们使用了两台不同的机器:

  • (Machine1) Intel Core i5 四核(2009 年底型号)
  • (Machine2) 在 Intel XEON 6 核 SandyBridge CPU 上运行的虚拟机

令人惊讶的是,无论是 Machine1 还是 Machine2,都始终产生正确的结果。即使使用所有 CPU 核心,结果仍然正确。在每台机器上编译代码时,没有使用优化选项或任何特定的编译器设置。因此,通过阅读新闻,我们得出以下结论:

看起来Prime95Mersenne Community的人似乎是第一个发现并识别这个nasty bug的人。参考帖子和新闻支持怀疑,该问题仅存在于重负载情况下。根据我的观察,我可以确认这种行为。

问题

  • 您/社区是否在Haswell CPU和Skylake CPU上观察到了这个问题?
  • 由于gcc默认进行AVX(2)优化(只要可能),关闭此优化会有所帮助吗?
  • 我该如何编译我的代码并确保关闭可能受此漏洞影响的任何优化?到目前为止,我只读到过在Haswell / Skylake架构中使用AVX2命令集会有问题。

解决方案?

好的,我可以关闭所有AVX2优化。但这会减慢我的代码速度。英特尔可能会发布一个BIOS更新给主板制造商,以修改英特尔CPU中的微码。由于它似乎是一个硬件错误,即使更新了CPU的微码,这也可能变得有趣。我认为这可能是一个有效的选择,因为英特尔CPU使用一些由微码控制的RISC到CISC转换机制。

编辑:Techreport.com - Errata prompts Intel to disable TSX in Haswell, early Broadwell CPUs 将检查CPU中的微码版本。

编辑2:截至目前(2016年1月19日15:39 CET),Memtest86+ v4.20正在运行并测试内存。由于这似乎需要很长时间才能完成,我将在明天更新帖子并公布结果。

EDIT3: 截至目前为止(2016年1月21日09:35 CET),Memtest86+已经完成了两次运行并通过了。甚至没有一个内存错误。将CPU的微代码从revision=0x2d更新到revision=0x36。目前正在准备源代码在此发布。问题仍然存在错误结果。由于我不是相关代码的作者,我必须进行双重检查以避免发布我不被允许发布的代码。我也在使用和维护这个工作站。
EDIT4: (2016年1月22日)(12:15 CET)这里是用于编译源代码的Makefile:
# VARIABLES ==================================================================
CC = gcc
CFLAGS = --std=c99 -Wall
#LDFLAGS = -lm -lgomp   -fast -s -m64 
LDFLAGS = -lm 

OBJ = ArtReconstruction2Min.o


# RULES AND DEPENDENCIES ====================================================

# linking all object files
all: $(OBJ)
  
    $(CC) -o ART2Min $(OBJ) $(LDFLAGS)         

    
# every o-file depends on the corresonding c-file, -g Option bedeutet Debugging Informationene setzen
%.o: %.c
    $(CC)  -c -g $<  $(CFLAGS)
  
    
# MAKE CLEAN =================================================================
clean: 
    rm -f *.o
    rm -f main

以及 gcc -v 的输出:

gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/4.9/lto-wrapper
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 4.9.2-10ubuntu13' --with-bugurl=file:///usr/share/doc/gcc-4.9/README.Bugs --enable-languages=c,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.9 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.9 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object --disable-vtable-verify --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-4.9-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-4.9-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-4.9-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 4.9.2 (Ubuntu 4.9.2-10ubuntu13) 

12
为什么会有负投票?这是一个好的、合理的问题!我会尝试在一个等价的设置中复现它。 - rzo1
8
根据他的测试结果,12个完全相同的核心中有11个并未执行相同的操作,尽管它们应该这样做,这使得这个设置变得更加实际。在计算时出现间歇性故障是一个相当困难的任务,提供这样一个“愚蠢”的方法来揭示一些基本前提(代码在任何负载下对于相同的输入会以相同的方式运行)是错误的。 - DThought
2
他可能通过无意义、冗余的操作发现了硬件漏洞(很不可能)或硬件缺陷(很可能),但这并不能使方法更加聪明。他目前经历的是“运气”——他也可以通过运行IBT或prime95等CPU密集型测试工具数天后发现相同的问题。@semm0:下载并运行IBT——如果您的机器锁定,您就知道问题要么与热耗散有关,要么甚至是硬件缺陷——在这两种情况下,零星的计算错误非常普遍。 - specializt
5
"没有特殊的编译参数"。你怎么知道的?你的编译选项是什么?你说“gcc默认使用AVX(2)优化”。这不是真的,它只在64位模式下默认使用SSE2。你必须添加一些选项。你问:“关闭这个优化会有帮助吗?”为什么不测试一下?再次说明你的编译选项、编译器和版本。 - Z boson
2
你的代码使用了任何全局状态变量吗?如果是这样,即使多个线程运行相同的函数并且它们写入全局状态变量,这可能会导致错误的结果。 - Z boson
显示剩余5条评论
2个回答

8

编辑:问题已解决。我必须向社区道歉并感谢大家的提示。对于似乎参与核心开发的用户Anonymous表示抱歉。发生了什么?我们又花了两天时间调试和操纵程序代码。没有找到实施问题,但主要代码涉及另一个辅助程序。该助手程序按需计算ART算法的权重。因此,在运行至少4个进程时,调试和测试后,此辅助程序出现问题。因此这不是Kernel /硬件问题,而是软件(内存访问)问题。

所得教训:

  1. 调试涉及到计算过程的每个工具。
  2. 微码已过时。SuperMicro已被告知。
  3. 可能需要安装其他工具才能确保Ubuntu 15.04所有CPU核心以全速运行。通过安装Ubuntu 14.04实现此目标-所有核心都以2.5GHz运行。
  4. 如果我们有机会在会议上见面,我需要请您喝啤酒。

因此,在三天思考,测试和操纵机器后,今天我发现了以下观察结果:

  1. Ubuntu 15.04使每个核心的CPU运行速度为420-650 MHz。好吧,我认为这是一种节能选项,因此我按照各种指南将速度设置为最大值(2.50 GHz)。但是它没有起作用。使用cpufreq-utils检查。

  2. 在此机器上进行了多次测试后,结果仍然出错。其他(i5,i7,XEON)机器产生了正确的结果。

  3. 我阅读到其他用户在Ubuntu 15.04和CPU频率方面遇到问题。因此,我决定插入SSD并安装Ubuntu 14.04。再次检查CPU频率是多少..结果显示为我预期的2.50GHz。

  4. 然后重新启动了重建算法(现在比Ubuntu 15.04快4-5倍),并等待结果。好的。结果现在是正确的!我再次验证了,启动了9个进程并比较了结果。仍然正确。

因此,我只能假设,在Ubuntu 15.04 /内核中使用Speedstep时可能存在问题。 15.04中的CPU始终在420-650 MHz之间运行,而最小CPU速度应为1.20 GHz,最大CPU速度为3.30 GHz。如果有人想要检查,我可以提供源代码和导致此问题的示例数据。

对于怀疑这是CPU 错误的情况表示抱歉。

编辑:经过更多测试,该问题仅在某些方案中解决,但尚未完全解决。我会进行更多测试。


2
你尝试过使用可用的编译器选项进行编译吗?比如-O3 -march=native?这应该会产生2到10倍的加速效果,具体取决于内存带宽是否成为瓶颈以及你的代码在AVX/AVX2下自动向量化的效果如何。除非这是你唯一能够重现问题的方法,否则使用-O0进行数值计算是愚蠢的。另外请注意,Ubuntu 15.10自2015年10月就已经发布了(当然)。我建议升级到那个版本(特别是因为像gcc 5.2这样的新编译器要比旧编译器更好)。 - Peter Cordes
感谢您的评论,彼得。通常我使用CUDA 7.5进行开发,目前只能在14.04或15.04上运行 - 我没有在15.10上测试过...感谢指出编译器优化。速度提升了约30-40%。 - semm0
1
启用优化后,您是否获得了正确的结果? - Z boson

7
Skylake-S/U prime95错误位于AVX(而非AVX2)单元中。在微代码0x56(可能)和0x6a(确定)上修复了此类问题。Haswell的这种错误不太可能发生,但有可能(特别是在2014年后的英特尔产品中,“验证”成为质量标准的不受欢迎的成本)。Haswell存在与AVX单元相关的勘误,尽管HSE58很少出现(它只会使AVX单元变慢)。但是,请尝试在AVX2计算之前放置一些MFENCE指令。如果这样可以解决问题,请立即报告,这意味着我们需要在内核中对所有IRET进行MFENCE处理(HSE105)。您的处理器签名为0x306f2。确保您拥有0x36或更高版本的微代码修订版,该微代码包含在英特尔的“Linux微代码更新包”中,日期为2015-11-06。编辑:这并不是一个真正的“答案”,所以我应该将其作为评论发布。对此我深感抱歉。由于微代码更新无法完全解决问题,仍可能存在新的勘误、旧的未解决勘误或其他问题(例如代码错误或gcc代码生成错误)。

感谢您的回答。目前正在努力收集上述评论所需的所有信息。关于微码的信息(此信息将作为原始问题的附加“编辑”后添加):微码:CPU0 签名=0x306f2,pf=0x1,修订版本=0x2d - semm0
1
请更新微码并重新测试...您可以直接在Debian或Ubuntu Xenial Xerus中使用更为更新的“intel-microcode”软件包:只需从Debian或Ubuntu下载“.deb”文件,然后运行“dpkg -i”命令。确保已更新initramfs,并重新启动。同时,请向Supermicro投诉以便让他们更新BIOS。 - anonymous
好的,微码更新已安装:微码:CPU0 sig = 0x306f2,pf = 0x1,revision = 0x36。抱歉,上周是学期的最后一周,有些学生演示需要处理。现在将重新启动计算以查看结果如何。我会尽量在4-5小时内给您更新。 - semm0
微码更新后问题仍然存在。需要休息一下...将在9小时后回来。 - semm0
@semm0,你还没有说明你的编译选项。 - Z boson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接