在龙芯3A5000上测试SPEC CPU 2006

  SPEC CPU 2006 benchmark是一种行业标准化的CPU测试基准套件,重点测试系统的“CPU-内存”子系统以及编译器的优化程度。看软件的名字就知道,这个测试工具主要是测试CPU,内存对测试成绩的影响没有CPU那么大。我在不同的电脑上反复更换硬件实测对比,结论是硬盘等其它设备的性能对测试成绩几乎没有影响。
  SPEC CPU 2006整套工具分成两个部分,一是测试工具(tools),二是测试基准(benchmarks),基准中包含int和fp两种测试集,int测试集中有12个测试项目,fp测试集中有17个测试项目。运行SPEC CPU 2006测试时,是测试者运行测试工具,再由测试工具根据预先配置的编译器及编译参数去自动编译各个测试项目,编译完成后再自动运行测试集,由测试工具统计测试集中每个项目的运行时长,给出最后的评价分数。
  SPEC CPU 2006的工具提供了部分平台的预编译二进制,文件在$SPEC/tools/bin/目录下,在有预编译工具的平台上就可以直接运行 install.sh 或 install.bat 进行安装。下图中是SPEC CPU 2006 1.2(Version 119 – 2021/8/16)版光盘中全部的测试工具的预编译二进制,可见用于x86/AMD64的预编译二进制最为齐全,既有Linux也有Window版本,可以直接安装。而对MIPS则没有Linux系统的二进制,对ARM架构也没有预编译二进制,都必须自行编译测试工具。LoongArch是一种全新的架构,也肯定不能指望SEPC 2006在十年前的光盘中就做好支持的准备。







  按照SEPC官网上《Building the SPEC CPU2006 Tool Suite》的说明:“有时可能需要重建工具,例如,如果操作系统的变化使预编二进制无法操作,或者如果您是第一个为新架构添加支持的人。”那么,当测试工具的预编译二进制在某些系统上不能运行时,或者当缺少某个架构的预编译二进制时,就应该自己编译测试工具。





  有一些网友因为在x86上测试SPEC CPU 2006时可以直接安装,就认为安装SPEC根本不需要自己编译,这是没有分清Tools和Benchmark而造成的误解。如果一款软件在任何架构的CPU和任何系统上都能直接安装使用,那么也就不会存在软件生态的问题了。
  SPEC CPU 2006 1.2发布时,GCC的版本还是4.x。由于GCC版本更新,部分.h文件有修改,glibc库也迭代了多个版本,在现在的各个Linux发行版上用较新的GCC版本已经不能把测试工具直接编译通过。
《SPEC CPU2006: Read Me First》中提到,如果工具无法在系统上运行或构建时,应该联系SPEC以获得技术支持。然而,电脑爱好者研究和学习性质的测试,去找SPEC技术支持就小题大作了(因为不想给钱),所有的问题咱们都自己解决。在网上早有一些文章探讨了在GCC8.x甚至更高版本上编译SPEC CPU 2006测试工具出现的问题,并给出了解决方法,我想SPEC的技术支持可能也不会有什么创新,毕竟有些问题只能那样解决。本文也只是把这些文章中提到的解决方案进行了一番整合。
  LoongArch是一个全新的架构,在龙芯3A5000上编译SPEC CPU 2006测试工具比在龙芯3A4000上更加麻烦。我在ARM64架构的树莓派4B(Ubuntu、GCC10.3)环境下编译时遇到的问题比基于LoongArch64的3A5000(Loongnix、GCC8.3)的更多,应该是GCC版本更高的原因。但本文主要记录在3A5000(Loongnix、GCC8.3)上编译SPEC CPU 2006测试工具的过程,对其它架构和GCC版本不作细致讨论。对文中这些问题的解决方式,在其它架构的CPU上编译SPEC CPU 2006测试工具时也能用到。
SPEC CPU 2006光盘镜像的获取
  SPEC CPU 2006是一个收费的测试工具,可以向SPEC购买,但官网上现在只能下SEPC CPU 2017版本的订单,已经没有2006了。2017的非商用授权价$250,商用$1000。出于研究和学习的目的,SPEC CPU 2006有一些光盘镜像在技术爱好者间私下扩散,可以去一些交流CPU技术的聊天群里问一问。只是这种没给钱的镜像按《著作权法》的规定,只能用于教学或者科学研究,可少量复制供教学或者科研人员使用。
  拿到光盘镜像后,请检查一下版本。
  官方发布的SPEC CPU 2006最终版本是 v1.2,发布日期是2011年9月7日,光盘中记录的最后更新日期是2011年8月16日。官网上公布的v1.2版光盘镜像文件的MD5校验码是“185F9167FB4C2208380E769D6668285D”,我确认自己手上的光盘镜像的MD5校验码和官网上的一致。
  $CDROM/version.txt 里面是版本号,如果低于v1.2,那么编译测试工具时,需要解决的问题会更多,测试成绩也不能与v1.2版完全等同。
在Linux中安装有预编译二进制程序的SPEC CPU 2006
  SPEC CPU 2006对x86/AMD64几乎有全平台的预编译二进制支持,在使用x86/AMD64 CPU的电脑上,就可以直接运行intsall.sh或install.bat进行安装。下面以在UOS for x86中安装SPEC CPU 2006为例,说明安装步骤。
  1.打开终端程序,进入存放光盘镜像的文件夹,输入命令挂载光盘镜像。
sudo mount -t iso9660 SPEC_CPU2006v1.2.iso /mnt
  有的Linux系统的图形界面提供了挂载光盘镜像的简单操作,在文件管理器中的光盘镜像文件上点鼠标右键,如果右键菜单中有挂载光盘镜像的功能项,就不需要输入命令了。
  2.进入到光盘镜像的挂载目录,运行安装程序。
cd /mnt ./install.sh
  然后会出现输入安装路径的提示:





  3.如上图所示,例如先在/home/guee下建立了一个cpu2006文件夹,就输入完整路径 /home/guee/cpu2006,之后回车。注意安装路径中不要有中文,不然在运行时可能会出现一些奇怪问题。然后安装程序会询问你源路径和目标路径是否正确,输入yes确认即可。
  在之后安装程序就会把测试工具复制到安装目录,并解压benchmarks的所有测试项目源码,最后测试一番perl脚本,就会给成安装成功的信息。





  在x86上安装很简单,那么在其它平台呢?比如在SPEC CPU 2006中没有预编译支持的ARM平台上,我们也来试一试能不能直接安装。下面我们在运行Ubuntu的树莓派4B中看看安装SPEC CPU 2006是什么情况,前面要求输入安装路径和确认的步骤还是一样,只是在确认后并没有开始安装,而是给出了一段文字信息:





  这段文字的大意是说:“我们似乎没有适用于您的架构的供应商提供的二进制文件,您必须自己编译工具二进制文件,请阅读tools-build.html获得如何构建它们的说明。”
  在龙芯3A4000和3A5000上运行install.sh也是给出这样的信息,就是告诉我们需要自己编译测试工具。
自己编译SPEC CPU 2006测试工具的初始准备
  光盘中的测试工具默认没有MIPS/LoongISA/LoongArch等架构的Linux版二进制程序,需要自行编译才能使用。下面以Loongnix20系统为例,进行编译前的准备工作。
  1.在编译之前,先要准备好编译器:
sudo apt-get install gcc g++
  如果准备在测试工具编译完成后测试fp(浮点性能),还需要安装gfortran编译器。
sudo apt-get install gfortran
  2.找到光盘根目录下的 intall_archives 文件夹,里面的cpu2006.tar.xz就是测试工具以及Benchmarks的所有测试项目的源码。如果不想挂载ISO,也可以用压缩软件打开iso文件,只把cpu2006.tar.xz这一个文件提取出来。





  3.把cpu2006.tar.xz解压到硬盘上的文件夹中,例如 /home/guee/cpu2006。解压出来的文件与用install.sh安装到目标路径的内容基本一样,只是少了测试工具的二进制文件,但是多出了一个tools文件夹,$SPEC/tools/src里面就是测试工具的源代码。





  我们需要运行$SPEC/tools/src文件夹中的buildtools来编译测试工具,测试工具由许多功能模块组成,上图的各个文件夹大部分都是测试工具的组成部分。其中一部分是用于检查系统环境和各种API功能是否正确的,还有一些是用来监督测试过程和生成测试报告的。由于测试工具不像测试集那么内聚,对系统中的部分基础库有版本依赖,与较新版本的GCC默认情况下也存在不兼容的问题,用GCC8.x直接编译肯定会失败,因此就需要修改测试工具中的一些配置文件和源码。
  注意:只能修改测试工具的源码,不能修改测试集的源码。首先测试集源码不允被修改,测试时会自动校验测试集的源码有没有被修改,如果修改过就不能通过验证进行测试。何况测试集的源码对系统环境依赖很小,在各种平台都能正确编译,本身就不需要修改。
解决SPEC CPU 2006测试工具构建过程中的错误
  0.写在前面
  由于操作系统、基础库、编译器等等升级,在新平台上构建一套有历史的软件肯定不会一帆风顺。必要的时候还需要对软件源代码进行修改,这也是开源的意义所在。但是对于SPEC CPU2006这种标准化的测试工具而言,允许改动的只有Tools部分,而不含Benchmark部分。其官网上的《Runspec Avoidance》一文甚至是在讲解不使用Tools的情况下,如何运行Benchmark。但不使用Tools来运行Benchmark的话,就不会有测试报告,因为有效的Benchmark测试只能在Tools的监督下运行,Tools还会校验Benchmark的代码和数据是否被篡改。
  $SPEC/tools/src/buildtools是用来编译测试工具的,在编译过程中,除了下面提到的第一和第二个问题,其它问题在x86的电脑上编译SPEC CPU 2006测试工具时也会遇到,我在UOS for x86上试过,该遇见的问题都会遇见。只不过因为SPEC CPU 2006的光盘中自带了x86架构的Linux和Window的测试工具二进制文件,大多数测试者不会去尝试自己编译测试工具,而是运行./install.sh或 install.bat直接安装,免去了编译测试工具的麻烦。在3A5000上构建SPEC CPU 2006测试工具遇到的所有问题,在基于ARM64的树莓派上也一个都不少,因为SPEC CPU 2006的光盘内容没有对它们的的原生支持。
  在编译过程中如果出错,重新运行buildtools就会重新执行全部编译过程,这非常耗时。不过buildtools提供了一些变量,可以只运行指定的编译步骤。
  例如 SKIPALL=1 DOPERL=1 ./buildtools 就只编译 perl。下面列出控制 buildtools 编译步骤的各个变量:
 SKIPALL – turns off everything. If you like, set this, then turn individual phases on by setting them.  DOTOOLSRM – Remove previously installed tools  DOCLEAN – Run ‘make clean’, ‘make distclean’, and ‘make realclean’ in all source directories.  DOMAKE – build make  DOXZ – build xz  DOTAR – build tar  DOMD5 – build specmd5sum  DOSPECINVOKE – build specinvoke  DORXP – build rxp  DOEXPAT – build the expat XML parser, used by XML::SAX::ExpatXS  DOPERL – build perl  DOPERL2 – build perl modules  DOCOPY – copy the results to $SPEC/bin and fix shbangs
  1.运行./buildtools出现错误
输出的错误日志:
UNAME_MACHINE = loongarch64 UNAME_RELEASE = 4.19.0-12-loongson-3 UNAME_SYSTEM  = Linux UNAME_VERSION = #1 SMP Sat Jun 19 03:02:31 UTC 2021 configure: error: cannot guess build type; you must specify one + testordie error configuring make
错误说明:
  在配置编译的目标平台时,configure不知道现在是什么平台,在像LoongArch这样的全新的架构上编译就会出现这种情况。ARM64不是全新架构,但诞生日期也要比这个光盘镜像晚一些,光盘中的各个配置文件没有为它做好准备,因此也会出现相同情况,只是UNAME_MACHINE后面变成了aarch64。
解决方法:
  在./buildtools之前设置变量CONFIGFLAGS为’–build=loongarch64-linux-gnu’。在终端中输入如下的命令行来运行buildtools:
CONFIGFLAGS=’–build=loongarch64-linux-gnu’ ./buildtools
  如果是在ARM64架构上,则把loongarch64-linux-gnu修改为aarch64-linux-gnu。
  可能有其它更加优雅的解决方案,但因为对测试工具的编译只需要完成一次就好,不经常需要,我就不去寻找更好的方案了,感兴趣的朋友可以试试看。
  2.出现machine `loongarch64′ not recognized的错误
输出的错误日志:
checking build system type… Invalid configuration `loongarch64-linux-gnu’: machine `loongarch64′ not recognized configure: error: /bin/bash config/config.sub loongarch64-linux-gnu failed
错误说明:
  和第一个错误一样,也是因为编译脚本不认识LoongArch,导致编译前的检查通不过。在ARM64的电脑上,只是错误日志中的loongarch64会变为aarch64,其它内容都一样。
解决方法:
  对下列文件进行修改。可以在src目录下搜索config.sub,把找到的文件一个一个打开,修改后保存。
$SPEC/tools/src/make-3.82/config/config.sub $SPEC/tools/src/xz-5.0.0/build-aux/config.sub $SPEC/tools/src/expat-2.0.1/conftools/config.sub $SPEC/tools/src/rxp-1.5.0/config.sub $SPEC/tools/src/specinvoke/config.sub $SPEC/tools/src/specsum/build-aux/config.sub $SPEC/tools/src/tar-1.25/build-aux/config.sub
  在文件中搜索 “# Some are omitted here because they have special meanings below.”和“# Recognize the basic CPU types with company name.”(不含引号)这两个字符串,字符串下面的内容是已知的CPU类型列表。需要把“loongarch64”按照相同的格式添加到列表中。如果是ARM64的架构,就添加“aarch64”。
  例如:
找到:# Some are omitted here because they have special meanings below. 把下一行:1750a | 580 \ 修改为:1750a | 580 | loongarch64 \ --------------------------------- 找到:# Recognize the basic CPU types with company name. 把下一行:580-* \ 修改为:580-* | loongarch64-* \
3.出现`__alloca’未定义的错误
输出的错误日志:
linking make… /usr/bin/ld: glob.o: in function `.L38′:(.text+0x514): undefined reference to `__alloca’ /usr/bin/ld: glob.o: in function `.L49′:(.text+0x704): undefined reference to `__alloca’ /usr/bin/ld: glob.o: in function `.L51′:(.text+0x780): undefined reference to `__alloca’ /usr/bin/ld: glob.o: in function `.L57′:(.text+0x830): undefined reference to `__alloca’ /usr/bin/ld: glob.o: in function `.L148′:(.text+0x15cc): undefined reference to `__alloca’ /usr/bin/ld: glob.o:(.text+0x1694): more undefined references to `__alloca’ follow
错误说明:
  SPEC CPU 2006的工具代码中,部分函数声明与新版本的GCC头文件中有差异,出现了代码兼容性问题。
解决方法:
  本来我尝试在命令行参数中加上CFLAGS=’-U __GNU_LIBRARY__ -U __alloca’,但没有起作用,于是只好修改glob.c文件中的宏。
  修改文件:$SPEC/tools/src/make-3.82/glob/glob.c
找到#if !defined __alloca && !defined __GNU_LIBRARY__和配对的#endif,把这两行前面加注释符号“//”,屏蔽掉这两行。下面修改完成之后的样子,修改的是第一行和最后一行:
//#if !defined __alloca && !defined __GNU_LIBRARY__# ifdef __GNUC__#  undef alloca#  define alloca(n) __builtin_alloca (n)# else /* Not GCC.  */#  ifdef HAVE_ALLOCA_H#   include <alloca.h>#  else /* Not HAVE_ALLOCA_H.  */#   ifndef _AIX#    ifdef WINDOWS32#     include <malloc.h>#    elseextern char *alloca ();#    endif /* WINDOWS32 */#   endif /* Not _AIX.  */#  endif /* sparc or HAVE_ALLOCA_H.  */# endif /* GCC.  */# define __alloca alloca//#endif
4.出现’gets’ undeclared here的错误
输出的错误日志:
./stdio.h:459:1: error: ‘gets’ undeclared here (not in a function); did you mean ‘fgets’?  _GL_WARN_ON_USE (gets, “gets is a security hole – use fgets instead”);  ^~~~~~~~~~~~~~~
错误说明:
  仍然是依赖库版本升级造成的代码兼容性问题。
解决方法:
  修改下面两个文件
$SPEC/tools/src/specsum/gnulib/stdio.in.h $SPEC/tools/src/tar-1.25/gnu/stdio.in.h
  找到文件中下面的内容,注释掉 It it very …… use fgets instead”); 这一段。修改完成后的样子如下,注意/*…*/的范围:
/* It is very rare that the developer ever has full control of stdin,   so any use of gets warrants an unconditional warning.  Assume it is   always declared, since it is required by C89.   #undef gets_GL_WARN_ON_USE (gets, “gets is a security hole – use fgets instead”);*/#if defined(__GLIBC__) && !defined(__UCLIBC__) && !__GLIBC_PREREQ(2, 16)_GL_WARN_ON_USE (gets, “gets is a security hole – use fgets instead”);#endif
5.大量出现pow、floor、fmod、sin等函数未定义的错误:
输出的错误日志:
    miniperlmain.o opmini.o perlmini.o /usr/bin/ld: pp.o: in function `.L967′:(.text+0x3aa0): undefined reference to `pow’/usr/bin/ld: pp.o: in function `.L876′:……省略掉一部分,节省篇幅/usr/bin/ld: pp_sys.o: in function `Perl_pp_gmtime’:(.text+0xe7ec): undefined reference to `floor’/usr/bin/ld: (.text+0xe94c): undefined reference to `floor’/usr/bin/ld: pp_pack.o: in function `.L704′:(.text+0x1a48): undefined reference to `floor’/usr/bin/ld: (.text+0x1a70): undefined reference to `floor’/usr/bin/ld: pp_pack.o: in function `.L723′:(.text+0x1ab4): undefined reference to `floor’
错误原因:
  perl编译过程中未引入数学库。
解决方法:
  在$SPEC/tools/src/buildtools文件中关于perl编译部分的./configure 命令行的上面加入 export PERLFLAGS=”-A libs=-lm -A libs=-ldl” 即可。注意空格和引号,如果复制过去有问题,可以手工输入。
    export LD_LIBRARY_PATH DYLD_LIBRARY_PATH    export PERLFLAGS=”-A libs=-lm -A libs=-ldl”    ./Configure -dOes -Ud_flock $PERLFLAGS -Ddosuid=undef -Dprefix=$INSTALLDIR -Dd_bincompat3=undef -A ldflags=-L${INSTALLDIR}/lib -A ccflags=-I${INSTALLDIR}/include -Ui_db -Ui_gdbm -Ui_ndbm -Ui_dbm -Uuse5005threads ; testordie “error configuring perl”
6.PERL部分测试项目没通过
输出的错误日志:
Hey! Some of the Perl tests failed! If you think this is okay, enter y now:
错误原因:
  就像日志中说的,一些Perl测试失败。
解决方法:
  这不是什么大问题,完全不需要紧张。这句话的语气很轻松:“嘿!一些 Perl 测试失败了!如果你认为这没问题,现在输入 y:”,这种时候,当然毫不犹豫地输入一个字母“y”。
7.Test Summary Report 测试通不过
输出的错误日志:
Test Summary Report——————- t/getdate.t (Wstat: 0 Tests: 0 Failed: 0)   Parse errors: Bad plan.  You planned 146 tests but ran 0. Files=5, Tests=311,  0 wallclock secs ( 0.06 usr  0.00 sys +  0.09 cusr  0.01 csys =  0.16 CPU) Result: FAIL Failed 1/5 test programs. 0/311 subtests failed. make: *** [test_dynamic] Error 255
错误原因:
  是$SPEC/tools/src/TimeDate-1.20/t/getdate.t这个perl程序的代码有问题,我不确定是代码本身的bug还是在系统和基础库等升级后,造成这个perl程序的执行结果和预期不一致。我在x86和ARM架构的电脑上测试这个程序,也全是输出“FAIL”,在它所在的文件夹下输入命令 perl getdate.t,和在LoongArch的3A5000上是一样的现象:





  从测试结果可以排除龙芯平台的问题,因为在x86和ARM平台上也会出问题。手上有SPEC CPU 2006光盘镜像的朋友,请帮忙试试您的getdate.t是不是也这样?
解决方法:
  打开getdate.t这个文件,找到“$time_expect += $offset;”这一行,删除掉或加注释符号屏蔽掉它,然后就能测试通过了。
  getdate.t测试的内容,是把一系列的日期字符串转换为时间戳,再与对应的预置时间戳比较是否相等。仅在构建测试工具的步骤用于测试日期函数,在性能测试时不会用到(正常安装不存在这些文件),因此修改了它的代码也不会对性能测试环节有任何影响。SPEC CPU 2006的测试集是在benchspec文件夹中,这里面的内容一定不要修改,一旦有修改就不能通过校验,无法进行测试。
  Test Summary Report 是测试工具构建流程的倒数第二项,如果不想修改代码,也可以不理会这项测试,可以手工输入下面的命令把编译好的二进制文件复制到运行文件夹。
SKIPALL=1 DOCOPY=1 ./buildtools
  上面的DOCOPY就是最后一个步骤,相当于安装,但只是安装了测试工具的二进制文件,测试集是之前解压出来的。
  按照SPEC官网上的说明,把测试工具构建完成后还有一个打包的步骤。打包是为了分发,别人拿到打好的包可以直接用./install.sh安装。因为我只是自己测试,不需要分发安装包,所以忽略掉打包的流程。
  至此,SPEC CPU 2006的测试工具就在使用LoongArch架构的3A5000上编译通过了,接下来就可以试试用SPEC CPU 2006跑分。
使用SPEC CPU 2006给3A5000简单地跑个分
  使用SPEC CPU 2006跑分,需要先创建一个用于编译测试集的配置文件,在这个文件中可以填写base(基本)和peak(峰值)两种测试模式的编译参数,这两种测试模式都包含了int(整数)和fp(浮点)性能的测试集。
  如果测试base模式性能,那么必须所有的测试子项使用相同的编译优化参数,但可以为每一个子项指定不同的用于解决移植性问题的参数。比如部分子项必须指定 -std=gnu89 或 -std=gnu90 才能编译通过,这样的参数不影响性能,仅影响代码的可移植性。
  因为同一组编译优化参数并不能使所有类型的程序都得到较好的优化,有的参数对有的代码性能甚至有反作用,所以base的目的就是找出一组能够使所有测试子项成绩都不太差的优化参数组合,作出一定的取舍,使总成绩达到较优的状态。这个优化参数的组合,可以用于指导大型软件的编译,求得编译复杂度和性能的平衡。
  如果测试peak模式的性能,就允许对每一个测试子项配置不同的编译优化参数,尽量使每一个子项的成绩都相对最好,尽可能地压榨CPU性能,力求得到CPU的最高测试成绩。SPEC官网上的那些CPU的测试成绩,绝大多数都是使用的ICC编译器,这是Intel专为x86架构设计的极致优化的编译器,且在测试了开启了自动并行跑出的peak成绩。通常的软件项目中很少会使用ICC编译器,一因为ICC要求源代码符合它的规范,否则程序被ICC极端优化后运行不稳定的情况相对较多,二是因为ICC用于商业项目有较严格的授权限制。
  下面就开始在龙芯3A5000上开始一次实际的SEPC 2006测试。这个测试非常耗时,特别是为了找出较优的编译参数而需要反复调整反复运行,每完整地测试一轮就需要数小时,每修改一次参数就需要运行一次进行验证,对时间和精力消耗很大。因此我就只测试base模式的int单核成绩,参数也不是最优,仅是比较常见的优化参数。
1.编写SPEC CPU 2006测试配置文件
  在$SPEC/config中有一些配置文件的例子,可以把其中的 Example-linux64-amd64-gcc41.cfg 在相同文件夹中复制一份,改成自己喜欢的文件名,例如 3A5000.cfg,然后在此基础上进行修改。我是偶得了一份某知名整机企业用GCC测试Intel某款CPU base成绩的配置文件,格式比较规整,作了一些适应性修改就开始测试龙芯3A5000了。主要的改动是把原编译参数中启用向量指令的“-msse4.2”换成了支持LoongArch向量指令集的“-mlxs -mlasx”,对“-march”和“-mbi”也有修改。
  配置文件的详细说明请参阅SPEC官网上的《SPEC CPU2006 Config Files》。我在学习的过程中,按照自己的理解给一些配置上加了中文注释,修改后的配置文件内容如下:
backup_config  = 0         # 0:运行benchmark时不备份配置文件。ignore_errors  = yes       # 1或yes:子项运行时出错时仍然继续进行后续测试。reportable测试时此选项会被忽略。action         = validate  # validate:构建(如果需要)、运行、检查正确性并生成报告。tune           = base      # 默认的测试类型,也可以写 peak,或者 all,可在runspec参数中指定。check_md5      = 1         # 通过MD5来验证配置文件是否与已编译的测试集二进制文件匹配,如果不匹配就强制重新编译。reportable     = 1         # 1或yes:默认为产生正式报告的测试,int或fp对应的测试集中会完整运行3遍。mean_anyway    = 1         # 1或yes:即使部分子项运行失败,也总是计算总成绩,正式测试时忽略该选项。verbose        = 9         # 测试过程中输出调试信息的详细程度,级别为1到99。output_format  = asc, csv,html, screen, pdf, config #需要输出测试报告的类型,当screen存在且verbose>=9时,每个子项测试完成后会在屏幕上立即显示成绩。makeflags      = -j 4      # 并行编译的数量,加快测试项目的编译速度。flagsurl000    = $[top]/config/flags/flags-gcc-linux64-loongarch.xml    #指定描述编译器参数的配置文件。这个文件告知测试工具哪些参数是用于性能优化的,哪些参数是用于解决代码可移植性问题的。teeout         = 0         #设置为0时,运行benchmark时不显示运行的命令行# 把任务绑定到CPU。下面配置为单CPU,每CPU有4个核心。 # 对于其它类型的CPU可能需要对应修改。# 本设置对于Speed(单核)测试没什么意义,对Rate(多核)测试有一点增益。bind0  = numactl -m 0 –physcpubind=0bind1  = numactl -m 0 –physcpubind=1bind2  = numactl -m 0 –physcpubind=2bind3  = numactl -m 0 –physcpubind=3submit = $BIND $command# 设置编译器,下面 int 和 fp 分别表示编译int测试集和fp测试集的编译器int=default=default=default:CC            = gccCXX           = g++OBJ           = .ofp=default=default=default:CC            = gccCXX           = g++FC            = gfortranOBJ           = .o###################################################################### 编译器优化参数####################################################################default:M64_OPT      = -mabi=lp64 -Ofast -mlsx -mlasx -fltoAVX          = -march=loongarch -mtune=loongarchUNROLL       = -funroll-all-loopsFASTNOSTATIC = $(M64_OPT) $(AVX) $(UNROLL)FASTSTATIC   = $(FASTNOSTATIC) -static################################################################# Base 模式的编译器优化参数,测试 int 性能时使用。################################################################int=base=default=default:COPTIMIZE    = $(FASTSTATIC)CXXOPTIMIZE  = $(FASTSTATIC)FOPTIMIZE    = $(FASTSTATIC)################################################################# Base 模式的编译器优化参数,测试 fp 性能时使用。################################################################fp=base=default=default:FOPTIMIZE    = $(FASTSTATIC)COPTIMIZE    = $(FASTSTATIC)CXXOPTIMIZE  = $(FASTSTATIC)###################################################################### 32/64 bit,为解决代码可移植性而添加的编译器参数,写在 PORTABILITY 后面。#####################################################################default:PORTABILITY = -DSPEC_CPU_LP64###################################################################### 测试集各个子项的可移植性参数标识,为解决默认时代码与编译器或其它库的兼容性问题。#####################################################################400.perlbench=default=default=default:CPORTABILITY = -DSPEC_CPU_LINUX_X64 -std=gnu90401.bzip2=default=default=default:CPORTABILITY = -DSPEC_CPU -fno-strict-aliasing462.libquantum=default=default=default:CPORTABILITY=  -DSPEC_CPU_LINUX483.xalancbmk=default=default=default:CXXPORTABILITY = -DSPEC_CPU_LINUX -std=c++03################################################################## 下面是计算机的相关信息#################################################################default=default=default=default:license_num      =test_sponsor     = gueetester           = gueehw_avail         = May-2021sw_avail         = July-2021hw_cpu_char      = Loongson-3A5000hw_cpu_mhz       = 2500hw_fpu           = Integratedhw_memory        = 16 GB (2 x 8GB DDR4 3200)hw_model         = Loongson-3A5000 hw_parallel  = nohw_ncores = 4hw_ncoresperchip = 4hw_nthreadspercore = 1hw_nthreads  = 4hw_ncpuorder = 1hw_other         = Nonehw_pcache        = 64KB I + 64KB D on chip per corehw_scache        = 256kB I+D on chip per corehw_tcache        = 16384KB I+D on chip per chiphw_ocache        = Nonehw_vendor        = Loongsonint=default=default=default:sw_compiler001   = gcc/g++ 8.3sw_base_ptrsize  = 64-bitsw_peak_ptrsize  = 64-bitfp=default=default=default:sw_compiler      = gcc/g++/gfortran 8.3sw_base_ptrsize  = 64-bitsw_peak_ptrsize  = 64-bitsw_parallel_other = no
  注意 “flagsurl000” 那一行,指向的是一个 xml 文件,作用是对编译器的各项参数进行描述。让SPEC CPU 2006的测试工具知道配置的各种编译参数,哪些是用于性能优化的,哪些是用来解决代码可移植性问题的。如果缺少这个文件,或者实际用到的编译参数在文件中找不到,并不会影响测试成绩,只是不符合测试报告的规范,因此测试报告中会被打上“Invalid Run”标记。
 2.3A5000的 SPEC CPU 2006 int_base 单核实测成绩
  在终端进入安装或解压SPEC CPU 2006的文件夹,我的在“ /home/guee/cpu2006”中。刚才为3A5000编写的配置文件名是“3A5000.cfg”,就输入下方的命令:
. ./shrc runspec -c 3A5000 int
  注意第一行的两个“.”之间有一个空格,第二行的3A5000是配置文件名,扩展名为.cfg就可以不写扩展名。输入完成后测试工具会自动读取配置文件,检查测试环境,之后开始编译int的测试集。编译完成后会自动把测试集中所有子项运行3次,最后给出的测试成绩计算方式是:是把每个子项的3次成绩取中位值再求算术平均。
  我测试的3A5000整机CPU频率为2.5GHz,内存是整机原配的DDR4 3200,8G * 2。不过由于龙芯自己设计的内存控制器水平和Intel相比还有差距,因此并不能发挥出DDR4 3200的全部性能。使用上面的配置文件实测3A5000的int_base成绩为26.6,下图是截图自PDF版的测试报告,如果需要SEPC CPU 2006测试报告的原始文件,点这里





  可以看到,我测试出的int_base成绩为26.6,完全符合龙芯官方公布的“26分+”的成绩,感觉龙芯还留出了较大的冗余。由于工作缘故,我没有时间去对各种优化参数一一组合验证,GCC的优化参数非常多,我只是选用了几个常用的优化参数,实在算不上复杂。虽然也经过了几轮简单的调优,但和SPEC官网上那些测试报告中的配置文件相比,单薄得就像春天的草芽。
  至于fp_base成绩以及多核成绩,还有各种模式下的peak成绩,以后再测试了。这篇文件主要是介绍如何在SPEC CPU 2006没有预编译二进制支持的情况下,在一种新架构CPU上编译运行SPEC CPU 2006对CPU性能进行测试。
SPEC CPU 2006测试报告解读
  SPEC CPU 2006在测试完成后,会根据设置生成如 pdf、html、cvs 等各种格式的测试报告。以上图中的pdf格式测试报告为例,只测试int_base的话一般总共有4页,每一页的前3行内容都一样。第一行是标题,第二行左边是CPU名称和公司名称,以及测试者等相关信息,都是在配置文件中填写的。第二行右边是测试成绩,“SPECint2006”是peak成绩,本次没有测试,“SPECint_base2006”是base成绩。如果是测试的浮点,则“int”会换成“fp”,如果是测试的多任务并行,标签中还会加上“rate”的文字。
  第1页中间是每个子项的成绩线条形图,每条横线上有3个可能不重合的短竖线,表示三次测试中每次的成绩,数字是三次成绩的中位值,总成绩是使用中位值作为有效成绩来计算。下方的“Hardware”和“Software”中的内容大部分是测试者填写在配置文件中的,只有少数项目,如操作系统的名字是自动获取。
  后面的第2页“Results Table”是每一轮测试的详细成绩表格,字体加粗的数字是三次测试成绩的中位值。除了表现方式不同,实际内容和第1页的条形图一样。下面的“Platform Notes”除了部分来自配置文件的填写外,还有SPEC CPU 2006测试工具调用系统命令得到的系统信息,这部分内容一般会延续到下一页。





  第3页和第4页上值得注意的,就是“Base Portability Flags”和“Base Optimization Flags”了,从这两项可以看出来测试时使用了哪些编译器参数。如果没有原始配置文件,也可以通过这里的参数说明获得编译选项,对测试结果进行验证。






SPEC CPU 2006测试参数
  SPEC CPU 2006运行Benchmark的命令是runspec,这个命令的参数很多。输入 “./runspec -h” 可以看到runspec的所有参数,有许多参数是以别名的形式存在,和另外不同名称的参数功能相同。我个人不喜欢这种参数设计,无端地增加了参数的数量,甚至一个字母的大小写会用于表示的完全不相干的功能,更加容易造成混淆。
  在这里我介绍一些常用的参数,灵活使用有助于节省时间。当参数名和配置文件中的字段名相同时,输入的参数优先于配置文件的设置。
–config name,-c name
   指定 runspec 使用的配置文件,文件在$SPEC/config中查找。如果配置文件的扩展名为.cfg,就可以只输入文件名。如果不使用这个参数,默认使用default.cfg。
  举例:runspec -c 3A5000 int,以3A5000.cfg为配置文件,测试int成绩。
–action action,-a action
指定 runspec 执行的操作,下面仅列举常用的操作:
  validate:默认值,如果配置有变化就执行编译,然后运行Benchmark并给出测试报告。
  build:仅执行编译。
  clean:消除编译结果。
  举例:runspec -c 3A5000 -a build int,以3A5000.cfg为配置文件,编译int测试集。
–reportable,-s,–strict,–noloose
  进行有正式报告的Benchmark。可在配置文件中设置reportable的默认值。
  举例:runspec -c 3A5000 -reportable fp,以3A5000.cfg为配置文件,进行有正式报告的fp性能测试。
–tune tuning,-T tuning
   指定测试的级别,tuning 可为 base、peak、all。如果需要正式报告,则只能使用 base 或 all。
  举例:runspec -c 3A5000 –tune peak fp,以3A5000.cfg为配置文件,按照文件中的peak编译配置测试fp成绩。
–size size,-i size
  指定测试的数据规模,size有三种数据规模,按从小到大分别为test、train、ref。
  test和train主要是用于验证编译完成的测试项目是否能正常运行,运行后即会给出运行时间。使用ref才能得到正确的测试成绩。如果是reportable方式的运行,会自动按顺序测试这三种测试数据。
  举例:runspec -c 3A5000 -a -i test fp,以3A5000.cfg为配置文件,使用testt的数据尝试运行fp测试项目。
–iterations number,-n number
   指定Benchmark运行的次数,有时调整了编译参数后,只是想看看对测试结果的影响,就只需运行一次,也可以运行很多次。
  举例:runspec -c 3A5000 –tune peak -n 1 fp,以3A5000.cfg为配置文件,按照文件中的peak编译配置测试fp成绩,只运行一次。

–rate [copies],-r [copies]
   指定Benchmark并行运行的数量,用于测试rate性能。copies 不必一定等于CPU核心数量,大于或小于CPU核心数量都是允许的。默认为speed方式运行。
  举例:runspec -c 3A5000 -r 4 all,以3A5000.cfg为配置文件,测试并行4个任务时int和fp的rate成绩。
只运行指定的测试项目:
在上面的各个例子中,出现过int、fp、all,如果想要只运行指定的一个或多个测试项目也是可以的,只要把int、fp或all换成测试项的编号或名字即可。例如:
  runspec -c 3A5000 401.bzip2
  runspec -c 3A5000 459 465
  runspec -c 3A5000 sjeng mcf astar
  对runspec更加详细的说明,请参阅SPEC官网的《The ‘runspec’ Command》
关于测试结果的报告文件中“Invalid Run”的文字和水印
  测试结果的报告中被加上“Invalid Run”的文字和水印是很常见的,只要测试有一点不符合规范就会出现这样的问题。如果是自娱自乐的测试,“Invalid Run”标记就没有什么关系,但这样的测试报告是不能给别人展示的。网上其它关于SPEC CPU 2006的文章中,我没有找到关于“Invalid Run”的说明,因此在这里简述一下。
  当测试报告中出现“Invalid Run”的文字和水印时,在测试报告表格中,会有专门的一栏列出本次测试出现的所有问题,这些问题一般是由两种原因造成的:
1.测试者本身就没打算要正式的测试报告
  配置文件中没有写“reportable= yes”,runspec运行参数也没有加“–reportable”。或者指定了测试次数不是3次,或者指定了只测试某个子项。
  SPEC CPU 2006的测试需要完全正确地运行3次之后,才给出正式的测试报告。有趣的是,SPEC官网上的《SPEC CPU2006 System Requirements》一文中,把测试过程中出现的运行错误称为“Mysterious Failures(神秘故障)”。在SPEC看来,测试项目偶尔出错是无法避免的,也难以追踪到原因,因此不打算解决这个问题。对于3A5000这种使用全新架构的计算机来说,各种基础库才刚刚完成移植,GCC编译器也才加上对LoongArch指令集的支持,出现bug的机率更大,某些测试子项偶尔运行出错的机率也更高。
  在需要给出正式报告的测试中,一旦任何子项运行中出错,测试过程就会终止。如果不要求正式报告,那么可以设置子项测试出错后继续运行后续项目,每个子项出错一次对总成绩不会有明显影响。如果某个子项在3次运行中出错了2次或3次,那么就需要考虑编译参数是否正确,可能需要调整后再重新测试。
2.编译器符号描述文件不正确
  这种情况很常见,SPEC CPU 2006给出的配置文件范例中都有flagurl这一项,例如“Example-linux64-amd64-gcc41.cfg”中引用的就是“Example-gcc4x-flags-revA.xml”作为编译器符号描述文件。
flagsurl0     = $[top]/config/flags/Example-gcc4x-flags-revA.xml flagsurl1     = $[top]/config/flags/Example-linux-platform-revA.xml
  编译器符号描述文件的作用是:告诉SPEC CPU 2006测试工具,本次测试使用到的每一个编译器参数的格式和作用。详细说明见SPEC官网的《SPEC CPU2006 Flag Description Format》
  编译器参数分为两类,分别是用于性能优化的和用于解决代码可移植性的。比如 -O2、-Ofast、-march=xxxx这些就是用于性能优化的,而-std=gnu89、-mabi=64这样的就是处理代码可移植性的标记。SPEC CPU 2006测试工具需要明确地知道每一个参数的分类,而参数描述则由测试者自己提供。“Example-gcc4x-flags-revA.xml”这个文件中只支持了GCC4.x的不完整的参数描述,只要在测试中使用了没被编译器符号描述文件涵盖的编译器参数,测试报告中就会出现“Invalid Run”的标识。
  我们可以做一个实验来验证一下:
  在x86电脑上正常安装SPEC CPU 2006,配置文件使用“Example-linux64-amd64-gcc41.cfg”做一些适应性修改。主要是给一些测试项参数加上 “-std=gnu89”或“-std=c++98”,运行完成后我们看看在测试报告中指出的错误是什么:





  这次测试的唯一错误就是有一些标记是未知的,测试报告中还会列出那些未知的标记到底是什么,出现在了哪些地方:





  可以看到未知的标志就是“-std=gnu89”,而“-std=c++98”则是可以被识别的。
  解决这个问题的方法,就是要有一个合适的编译器符号描述文件。如果是用的ICC编译器,那么可以去spec官网找现成的。测试者在向spec官网提交测试报告的同时,就会把测试配置文件和编译器符号描述文件也一并提交上去。





  但spec官网上少有使用GCC的测试成绩,且要么版本过低,要么是魔改版,我们拿来也没法用。因此我们在用较高版本的GCC测试时,就需要自己编写编译器符号描述文件。可以参考“Example-gcc4x-flags-revA.xml”中的内容进行修改,在spec官网上有详细的格式说明。只是编写这个文件太麻烦了,如果咱们测试只是为了自娱自乐,完全没有必要理会,反正也不会影响到测试成绩。我测试3A5000时倒是自己编写了一个,只写上了我测试中需要用到的编译参数。
  比如龙芯提供的的GCC8.3编译器中,启用LoongArch指令集的向量指令有两个参数,分别是 -mlsx 和 -mlasx,要想让SPEC CPU 2006测试工具认识这两个参数,在编译器符号描述文件就可以加入下面的内容。
<flag name=”f-mlsx” class=”optimization” regexp=”-mlsx(?=\s|$)”>   <example>-mlsx</example>   <![CDATA[<p>mlsx.</p>]]></flag><flag name=”f-mlasx” class=”optimization” regexp=”-mlasx(?=\s|$)”>   <example>-mlasx</example>   <![CDATA[<p>mlasx.</p>]]></flag>
  XML属性“class=”optimization””表示这个参数是用于性能优化,对于“std=gnu98”这种用于处理代码移植问题的编译参数,需要把“class”属性的值指定为“portability”。
<flag name=”stdgnu” class=”portability” regexp=”(?:/\S+/)?-std=gnu(\S+)\b”>    <![CDATA[      <p>Tells the compiler to conform to a specific language standard.</p>   ]]></flag>
  SPEC CPU 2006测试工具通过“class”属性来检查base测试时,是否为某测试项单独指定了“optimization”参数。参数类型是写“optimization”还是“portability”,也不全靠xml文件编写者的自觉,因为测试报告中会写上各种编译设置,向spec官网上传测试报告时,还会附带上这个xml格式的标记配置文件。“regexp”属性中是一个正则表达式,用于匹配对应的参数,CDATA中的内容是对标记作用的文字说明。
  本文告结,最后希望大家如果手中有3A5000的电脑,都试一试,测一测。如果没有,也支持折腾一下其它的电脑,比如我使用与上面测3A5000时对等的编译配置去测试i5-4460,常态下测出的SPEC CPU 2006_int_base成绩,就和3A5000差不多,仅27分。不过把i5-4460的运行频率锁定在3.4GHz后,测试成绩就要比3A5000高多了,超过了36分。但折算到与3A5000相同频率后的成绩仍然差不多,说明当它自动频率控制时,Benchmark时的平均频率也是2.5GHz左右。另外,估计是因为i5-4460的功耗太高,锁频跑了几轮后,这台电脑就坏掉了……成为了本次测试的消耗品。没事儿时测一测各种电脑也是很有意思的事情,只不过每次修改了编译参数后完整测试,都要等几个小时才能看到结果,适合睡前测试,风扇助眠。

转载请注明:《在龙芯3A5000上测试SPEC CPU 2006

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注