65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 Intel® Advisor Python API 获取性能洞察

emptyStarIconemptyStarIconemptyStarIconemptyStarIconemptyStarIcon

0/5 (0投票)

2020年4月9日

CPOL

5分钟阅读

viewsIcon

19793

获取高质量数据以做出代码调优决策

良好的设计决策基于良好的数据

  • 首先应该对哪些循环进行线程化和向量化?
  • 性能提升是否值得付出努力?
  • 线程化性能是否能随着更多核心的数量而扩展?
  • 这个循环是否存在阻止向量化的依赖关系?
  • 循环次数和内存访问模式是什么?
  • 您是否使用了最新的 Intel® Advanced Vector Extensions 512 (Intel® AVX-512)
    指令高效地进行了向量化?还是在使用旧的 SIMD 指令?

Intel® Advisor 是 Intel® Parallel Studio XE 的一部分,Intel® Parallel Studio XE 是 Intel 用于构建和现代化代码的全面工具套件。Intel Advisor 可以回答这些问题,以及更多的问题。您可以收集有关应用程序向量化和内存配置文件的有价值的程序指标。除了提供使用 GUI 和命令行量身定制的报告外,Intel Advisor 现在还提供了额外的灵活性,可以使用 Python* 来挖掘收集到的数据库并创建强大的新报告。

运行 Intel Advisor 时,它会将收集到的所有数据存储在专有数据库中,您现在可以使用 Python API 访问该数据库。这提供了一种灵活的方式来生成有关程序指标的定制报告。本文将介绍如何使用此新功能。

入门

要开始,您需要设置 Intel Advisor 环境。(本文中,我们在 Linux* 上运行了所有脚本,但 Intel Advisor Python API 也支持 Windows*。)

source advixe-vars.sh

接下来,要设置 Intel Advisor 数据,您需要运行一些收集。某些程序指标需要额外的分析,例如循环次数、内存访问模式和依赖关系。

advixe-cl --collect survey --project-dir ./your_project -- <your-executable-with-parameters> 

advixe-cl --collect tripcounts -flops-and-masks -callstack-flops --project-dir
 ./your_project -- <your-executable-with-parameters>

要运行 MAP 或依赖关系收集,您需要指定要分析的循环。您可以使用 Intel Advisor GUI 或通过命令行报告找到此信息。

advixe-cl --collect map –mark-up-list=1,2,3,4 --project-dir ./your_project --
<your-executable-with-parameters> 

advixe-cl --collect dependencies –mark-up-list=1,2,3,4 --project-dir ./your_project --
<your-executable-with-parameters>

最后,您需要将 Intel Advisor 参考示例复制到测试区域。

cp –r /opt/intel/advisor_2018/pythonapi/examples .

请注意,本文中我们运行的所有脚本都使用了 Intel Advisor 在 Linux 上提供的 Python。标准的 Python 发行版也同样适用。

使用 Intel Advisor Python API

我们提供的参考示例只是使用这种灵活访问程序数据方式所能实现的报告功能的一小部分。您可以运行 columns.py 示例来获取可用数据字段的列表。例如,在运行基本调查收集后,您可以看到表 1 中的指标。

表 1. 示例调查指标

Intel Advisor Python API 实战

让我们通过一个简单的示例来演示如何使用 Intel Advisor Python API 收集一些强大的指标。第一步是导入 Intel Advisor 库包。

import advisor

然后,您需要打开包含您收集到的结果的 Intel Advisor 项目。

project = advisor.open_project(sys.argv[1])

您还可以选择创建一个项目并运行收集。(在下面的示例中,我们只执行 `open_project`。)在此示例中,我们访问内存访问模式 (MAP) 收集的数据。我们通过以下代码行来完成此操作:

data = project.load(advisor.MAP)

加载此数据后,我们可以遍历表格并收集缓存利用率统计信息。然后,我们打印出收集到的数据:

import sys
try:
# First import the Advisor library
	import advisor
except ImportError:
	sys.exit(1)
# Open your Advisor project
	project = advisor.open_project(sys.argv[1])
# Load the Memory Access Pattern(MAP) data
	data = project.load(advisor.MAP)
#Loop through the MAP data and gather information about cache utilization
for site in data.map:
	site_id = site['site_id']
	cachesim = data.get_cachesim_info(site_id)
	print(indent * 2 + 'Average utilization'.ljust(width) + ' =
{:.2f}%'.format(cachesim.utilization))

Intel Advisor Python API 高级主题

Intel Advisor Python API 提供的示例为您编写自己的脚本提供了蓝图。表 2 显示了其中一些高级功能。

表 2. Intel Advisor Python API 高级功能

以下是我们各种示例的一些亮点。我们正在不断添加示例列表。

生成一个显示所有收集到的数据的组合报告

project = advisor.create_project(project_dir)

生成 HTML 报告

advixe-python to_html.py ./your_project

您可以使用此代码生成一个 Roofline HTML 图表(图 1)

python roofline.py ./your_project

您必须使用外部 Python 命令运行 `roofline.py` 脚本,而不是 `advixe-python`。它目前仅在 Linux 上运行。它还需要安装额外的库 numpy、pandas 和 matplotlib。使用此代码生成缓存模拟统计信息:

advixe-python cache.py ./your_project

图 1 – Roofline HTML 图表

您可以在表 3 中看到我们从缓存模型中获得的结果。

表 3. 缓存模型结果

案例研究:向量化比较

在此案例研究中,我们创建了一个 Python 脚本,该脚本可以比较给定循环在以不同编译器选项编译时的向量化情况。

步骤 1:使用不同的优化标志编译代码

首先,使用不同的选项编译应用程序。在此示例中,我们使用 Intel® C++ Compiler(但 Intel Advisor 在二进制级别工作,因此任何编译器都应该有效)。在第一种情况下,我们使用编译器选项 -O0 在没有优化的前提下进行编译。第二种情况使用完全优化 -O3。

icc loops1.cpp -O0 -g -debug inline-debug-info -qopt-report=5 -ipo- -o loops1-no-opt
icc loops1.cpp -O3 -g -debug inline-debug-info -qopt-report=5 -ipo- -o loops1

步骤 2:Python 代码

脚本非常简单。首先,从命令行获取一些参数。如果传递了 Intel Advisor 项目,则使用项目中的数据。否则,执行 Intel Advisor 调查运行。调查运行完成后,解码循环的汇编代码,并并排打印这两个循环的指令。我们 Python 代码中的主函数名为 `get_formatted_asm`。此函数能够访问 Intel Advisor 数据库并解码我们循环的汇编代码。它还可以检查汇编代码是否正在使用向量指令,以及循环的执行速度。

import sys
import itertools

import advisor

# second form allows collecting data before analysis
# first form just analyses already collected data

if len(sys.argv) < 3 or len(sys.argv) > 6:
	print('''

Usage:
advixe-python {} path_to_project_dir loop1 [loop2]

 Or:
advixe-python {} path_to_project_dir loop1 [loop2] executable1 executable2

Where loop1 and loop2 are in the form source:line

'''.format(__file__, __file__))
    sys.exit(1)

project_dir = sys.argv[1]
project_dir1 = project_dir + ".1"
project_dir2 = project_dir + ".2"

loop1 = sys.argv[2]
# if we have an odd number of arguments (including script name) then loop2 is the same as loop1
loop2 = sys.argv[3] if len(sys.argv)%2 == 0 else loop1

binary1 = ''
binary2 = ''

# in the second form two last args are executables to run
if 4 < len(sys.argv) < 7:
binary1 = sys.argv[-2]
binary2 = sys.argv[-1]
        
# try open or create project, run collection if needed:
# returns formatted asm listing with vectorized instructions marked with "VEC " for given loop
def get_formatted_asm(project_dir, binary, loop):
    asm = []

    try:
        project = advisor.open_project(project_dir)
    except:
        project = advisor.create_project(project_dir)

	if binary:
	    project.collect(advisor.SURVEY, binary)

	data = project.load(advisor.SURVEY)
        for entry in data.bottomup:
	    if loop in entry['function_call_sites_and_loops']:
	       asm += ["{:54.54} ".format(entry['function_call_sites_and_loops']),
				"{:54.54} ".format("Self time: " + entry['self_time']),
				" "*54]
	       for instruction in entry.assembly:
		   isVectorized = "VEC" if "VECTORIZED" in instruction['instruction_type'] else ""
		   asm.append("{:4.4}{:50.50} ".format(isVectorized, instruction['asm']))
	       asm.append("")
	return asm

asm1 = get_formatted_asm(project_dir1, binary1, loop1)
asm2 = get_formatted_asm(project_dir2, binary2, loop2)

# print alongside asm listings for comparison
for (a1,a2) in itertools.izip_longest(asm1, asm2, fillvalue = ' '*40):
    print("{}{}".format(a1,a2))

步骤 3:运行 Python 脚本

advixe-python compare_asm.py /home/work/projects/loops-compare loops1.cpp:34 
/home/work/tests/loops/loops1-no-opt /home/work/tests/loops/loops1
[loop in main at loops1.cpp:34]                       
Self time: 45.5463

Block 1                                                                        
 movl  -0xbc(%rbp), %eax
 movsxd %eax, %rax
 imul $0x8, %rax, %rax
 addq  -0x88(%rbp), %rax
 movl  -0xac(%rbp), %edx
 imull  -0xac(%rbp), %edx
 mov $0x1, %ecx
 addl  -0xac(%rbp), %ecx
 movq  %rax, -0x28(%rbp)
 mov %edx, %eax
 cdq
 idiv %ecx
 cvtsi2sd %eax, %xmm0
 movl  -0xc0(%rbp), %eax
 cvtsi2sd %eax, %xmm1
 movsdq  0x555(%rip), %xmm2
 divsd %xmm2, %xmm1
 addsd %xmm1, %xmm0
 movq  -0x28(%rbp), %rax
 movsdq  (%rax), %xmm1
 subsd %xmm0, %xmm1
 movl  -0xbc(%rbp), %eax
 movsxd %eax, %rax
 imul $0x8, %rax, %rax
 addq  -0x88(%rbp), %rax
 movsdq  %xmm1, (%rax)
 mov $0x1, %eax
 addl  -0xac(%rbp), %eax
 movl  %eax, -0xac(%rbp)
 movl  -0xac(%rbp), %eax
 cmp $0x64, %eax
 jl 0x401020 <Block 1>
[loop in main at loops1.cpp:34]    
Self time: 4.62404 

      Block 1                                  
  VEC movdqa %xmm11, %xmm2                     
  VEC movdqa %xmm11, %xmm0                     
  VEC psrlq $0x20, %xmm2                       
  VEC movdqa %xmm10, %xmm1                     
  VEC pmuludq %xmm2, %xmm2                     
  VEC pmuludq %xmm11, %xmm0                    
  VEC psllq $0x20, %xmm2                       
  VEC pand %xmm13, %xmm0                       
  VEC por %xmm2, %xmm0                         
  callq  0x401770 <__svml_idiv4>          
      Block 2                                  
  VEC cvtdq2pd %xmm0, %xmm2                    
  VEC punpckhqdq %xmm0, %xmm0                  
      add $0x4, %r15b                          
  VEC cvtdq2pd %xmm0, %xmm3                    
  VEC addpd %xmm14, %xmm2                      
  VEC addpd %xmm14, %xmm3                      
  VEC subpd %xmm2, %xmm12                      
  VEC subpd %xmm3, %xmm8                       
  VEC paddd %xmm15, %xmm11                     
  VEC paddd %xmm15, %xmm10                     
      cmp $0x64, %r15b                         
      jb 0x401397 <Block 1>

步骤 4:使用 AVX2 向量化重新编译

现在让我们尝试进一步优化。由于我们的处理器支持 AVX2 指令集,我们将告诉编译器生成 AVX2。(您应该注意,这通常不是编译器默认生成的。)

icc loops1.cpp -O3 -xCORE-AVX2 -g -debug inline-debug-info -qopt-report=5 -ipo- -o loops1-avx2

步骤 5:重新运行比较

advixe-python compare_asm.py /home/work/projects/loops-compare-opt loops1.cpp:34
/home/work/tests/loops/loops1 /home/work/tests/loops/loops1-avx2
[loop in main at loops1.cpp:34] 
Self time: 4.81401 
    Block 1 
VEC movdqa %xmm11, %xmm2 
VEC movdqa %xmm11, %xmm0 
VEC psrlq $0x20, %xmm2 
VEC movdqa %xmm10, %xmm1 
VEC pmuludq %xmm2, %xmm2 
VEC pmuludq %xmm11, %xmm0 
VEC psllq $0x20, %xmm2 
VEC pand %xmm13, %xmm0 
VEC por %xmm2, %xmm0 
    callq 0x401770 <__svml_idiv4> 
    Block 2 
VEC cvtdq2pd %xmm0, %xmm2 
VEC punpckhqdq %xmm0, %xmm0 
    add $0x4, %r15b 
VEC cvtdq2pd %xmm0, %xmm3 cmp $0x60, %r15b 
VEC addpd %xmm14, %xmm2 jb 0x4015a9  
VEC addpd %xmm14, %xmm3 
VEC subpd %xmm2, %xmm12 
VEC subpd %xmm3, %xmm8 
VEC paddd %xmm15, %xmm11 
VEC paddd %xmm15, %xmm10 
    cmp $0x64, %r15b 
    jb 0x401397 <Block 1>
[loop in main at loops1.cpp:34] 
Self time: 1.97998 
       Block 1 
VEC vpmulld %ymm15, %ymm15, %ymm0
   VEC vmovdqa %ymm14, %ymm1
  callq 0x4018 f0<__svml_idiv8>
      Block 2 
      add $0x8, %r15b
VEC vextracti128 $0x1, %ymm0, %xmm2
    VEC vcvtdq2pd %xmm0, %ymm3
VEC vpaddd %ymm13, %ymm15, %ymm15
VEC vcvtdq2pd %xmm2, %ymm5
VEC vpaddd %ymm13, %ymm14, %ymm14
VEC vaddpd %ymm3, %ymm10, %ymm4
VEC vaddpd %ymm5, %ymm10, %ymm6
VEC vsubpd %ymm4, %ymm8, %ymm8
VEC vsubpd %ymm6, %ymm9, %ymm9
        cmp $0x60, %r15b
        jb 0x4015a9 <Block 1>

您可以看到,汇编代码现在使用 YMM 寄存器而不是 XMM 寄存器,使向量长度加倍,速度提升 2 倍。

结果

通过优化和使用最新的向量化指令集所带来的收益非常显著。

  • 无优化 (-O0):45.148 秒
  • 优化 (-O3):4.403 秒
  • 优化和 AVX2 (-O3 –AVX2):2.056 秒

最大化系统性能

在现代处理器上,要充分发挥处理器的性能潜力,同时进行软件的向量化和线程化至关重要。Intel Parallel Studio XE 中新的 Intel Advisor Python API 提供了一种强大的方式来生成程序统计信息和报告,这些报告可以帮助您最大限度地提高系统的性能。本文中概述的示例说明了此新接口的强大功能。您可以根据具体需求定制和扩展这些示例。Intel 正在积极收集有关 Intel Advisor Python API 的反馈。如果您尝试过并觉得它很有用,或者想提供反馈,请发送电子邮件至 mvector_advisor@intel.com

© . All rights reserved.