简介

运行说明

pcm 监控结果可以分为核心、socket 和系统三部分。在核心监控部分,结果包括如下内容:

• EXEC

• IPC:每 CPU 周期指令数

• FREQ:普通CPU频率系数

• AFREQ:激活状态普通CPU频率系数

• L3MISS:L3(读)缓存miss

• L2MISS:L2(读)缓存miss

• L3HIT:L3(读)缓存命中率(0.00-1.00)

• L2HIT:L2(读)缓存命中率(0.00-1.00)

• L3MPI:每周期L3(读)缓存miss数

• L2MPI:每周期L2(读)缓存miss数

• L3OCC:L3占用比率

• TEMP:温度

• energy:

此外,在核心端还提供了每个核心C-Stat状态(C0-C6)等。

在socket端,监控结果中提供了UPI带宽/利用率,和内存读写大小相关内容,包括:

• READ:从主内存控制器读取字节数

• WRITE:从主内存控制器写入字节数

• LOCAL:本地内存读取百分比

• PMM RD:从 PMM 内存读取字节数

• PMM WR:从 PMM 内存写入字节数

运行分析

整体运行流程

下面介绍函数整体运行流程。按照INTEL性能分析流程,需要首先在事件选择寄存器中定义监控事件,随后读取监控寄存器值进行取样。为了解pcm.x运行过程中监控事件,首先查找其对监控事件定义函数。

缓存事件定义

在 pcm.cpp 中,main函数执行时会首先分析命令行中输入参数,随后对相应参数进行设置,随后在1275 行处,调用如下函数对监控事件进行了编写。

    // program() creates common semaphore for the singleton, so ideally to be called before any other references to PCM
PCM::ErrorCode status = m->program();

其中 program() 方法的定义格式为

ErrorCode program(
const ProgramMode mode_ = DEFAULT_EVENTS,
const void *parameter_ = NULL); // program counters and start counting

在此函数中,参数 mode_ 类型为枚举类型 ProgramMode,其选择包括 DEFAULT_EVENTS,CUSTOM_CORE_EVENTS,EXT_CUSTOM_CORE_EVENTS 和 INVALID_MODE 四种。当没有输入参数时,program() 将采用默认参数 DEFAULT_EVENTS 进行调用。查看代码中对应此模块的监控事件及掩码定义为

switch (cpu_model)
{
case SKL:
case SKX:
case KBL:
assert(useSkylakeEvents());
coreEventDesc[0].event_number = SKL_MEM_LOAD_RETIRED_L3_MISS_EVTNR;
coreEventDesc[0].umask_value = SKL_MEM_LOAD_RETIRED_L3_MISS_UMASK;
coreEventDesc[1].event_number = SKL_MEM_LOAD_RETIRED_L3_HIT_EVTNR;
coreEventDesc[1].umask_value = SKL_MEM_LOAD_RETIRED_L3_HIT_UMASK;
coreEventDesc[2].event_number = SKL_MEM_LOAD_RETIRED_L2_MISS_EVTNR;
coreEventDesc[2].umask_value = SKL_MEM_LOAD_RETIRED_L2_MISS_UMASK;
coreEventDesc[3].event_number = SKL_MEM_LOAD_RETIRED_L2_HIT_EVTNR;
coreEventDesc[3].umask_value = SKL_MEM_LOAD_RETIRED_L2_HIT_UMASK;

其中 SKL_MEM_LOAD_RETIRED_L3_MISS_EVTNR 与 SKL_MEM_LOAD_RETIRED_L3_MISS_UMASK 等都为宏变量,通过查找Intel手册查找对应的监控事件为

| 事件 | 事件编号 | 掩码 | 掩码编号 | 说明 |

| :---: | :---: | :---: | :---: | :---: | :---: |

| SKL_MEM_LOAD_RETIRED_L3_MISS_EVTNR | 0xD1 | SKL_MEM_LOAD_RETIRED_L3_MISS_UMASK | 0x20 | Counts retired load instructions with at least one uop that missed in the L3 cache. |

| SKL_MEM_LOAD_RETIRED_L3_HIT_EVTNR | 0xD1 | SKL_MEM_LOAD_RETIRED_L3_HIT_UMASK | 0x04 | Counts retired load instructions with at least one uop that hit in the L3 cache. |

| SKL_MEM_LOAD_RETIRED_L2_MISS_EVTNR | 0xD1 | SKL_MEM_LOAD_RETIRED_L2_MISS_UMASK | 0x10 | Retired load instructions missed L2 cache as data sources. |

| SKL_MEM_LOAD_RETIRED_L2_HIT_EVTNR | 0xD1 | SKL_MEM_LOAD_RETIRED_L2_HIT_UMASK | 0x02 | Retired load instructions with L2 cache hits as data sources. |

通过使用核心中四个通用函数寄存器以上事件的监控,即可获得对应时间监控结果。

缓存事件输出

在了解定义监控事件后,跳过中间监控过程分析,首先分析对监控事件结果的分析,了解最终输出使用的结果变量。在 pcm.x 中,结果输出位于第 1366 行,包括 csv 格式和命令行格式输出。

        if (csv_output)
print_csv(m, cstates1, cstates2, sktstate1, sktstate2, ycores, sstate1, sstate2, cpu_model, show_core_output, show_partial_core_output, show_socket_output, show_system_output);
else
print_output(m, cstates1, cstates2, sktstate1, sktstate2, ycores, sstate1, sstate2, cpu_model, show_core_output, show_partial_core_output, show_socket_output, show_system_output);

下面主要查看非 csv 格式输出时对应结果。在输出过程中,所有核心结果显示在下列代码中

    if (show_core_output)
{
for (uint32 i = 0; i < m->getNumCores(); ++i)
{
if (m->isCoreOnline(i) == false || (show_partial_core_output && ycores.test(i) == false))
continue;
if (cpu_model == PCM::KNL)
cout << setfill(' ') << internal << setw(5) << i
<< setw(5) << m->getTileId(i) << setw(5) << m->getCoreId(i)
<< setw(7) << m->getThreadId(i);
else
cout << " " << setw(3) << i << " " << setw(2) << m->getSocketId(i);
print_basic_metrics(m, cstates1[i], cstates2[i]);
print_other_metrics(m, cstates1[i], cstates2[i]);
}
}

其中包含了 print_basic_metrics()print_other_metrics() 两个不同过程,分别对缓存命中率相关指标,缓存占用和内存带宽等指标进行了监控。在基本指标中,缓存事件结果输出对应代码如下

template <class State>
void print_basic_metrics(const PCM * m, const State & state1, const State & state2)
{
cout << " " << getExecUsage(state1, state2) <<
" " << getIPC(state1, state2) <<
" " << getRelativeFrequency(state1, state2);
if (m->isActiveRelativeFrequencyAvailable())
cout << " " << getActiveRelativeFrequency(state1, state2);
if (m->isL3CacheMissesAvailable())
cout << " " << unit_format(getL3CacheMisses(state1, state2));
if (m->isL2CacheMissesAvailable())
cout << " " << unit_format(getL2CacheMisses(state1, state2));
if (m->isL3CacheHitRatioAvailable())
cout << " " << getL3CacheHitRatio(state1, state2);
if (m->isL2CacheHitRatioAvailable())
cout << " " << getL2CacheHitRatio(state1, state2);
if (m->isL3CacheMissesAvailable())
cout << " " << double(getL3CacheMisses(state1, state2)) / getInstructionsRetired(state1, state2);
if (m->isL2CacheMissesAvailable())
cout << " " << double(getL2CacheMisses(state1, state2)) / getInstructionsRetired(state1, state2);
}

可以看到,L2/L3 Miss 与命中率等指标都在函数 getL3CacheMisses() 和 getL3CacheHitRatio() 等过程中计算。进一步查看对应代码

template <class CounterStateType>
uint64 getL3CacheMisses(const CounterStateType &before,
const CounterStateType &after)
{
if (!PCM::getInstance()->isL3CacheMissesAvailable())
return 0;
return after.L3Miss - before.L3Miss;
}

可以了解到,在函数中输入的 before 和 after 分别代表前后两步的监控结果,而 L3Miss 则为监控结果。对应的在计算缓存命中率时,对应的函数过程为

template <class CounterStateType>
double getL3CacheHitRatio(const CounterStateType &before,
const CounterStateType &after) // 0.0 - 1.0
{
if (!PCM::getInstance()->isL3CacheHitRatioAvailable())
return 0;
const auto hits = getL3CacheHits(before, after);
const auto misses = getL3CacheMisses(before, after);
return double(hits) / double(hits + misses);
}

其中计算缓存命中公式为 hits/(hits + misses) ,而命中事件又由两部分组成,分别为

template <class CounterStateType>
uint64 getL3CacheHits(const CounterStateType &before,
const CounterStateType &after)
{
if (!PCM::getInstance()->isL3CacheHitsAvailable())
return 0;
return getL3CacheHitsSnoop(before, after) +
getL3CacheHitsNoSnoop(before, after);
}

getL3CacheHitsSnoop()getL3CacheHitsNoSnoop() 中分别调用 after.SKLL3Hit 和 after.L3UnsharedHit 进行计算。

缓存事件监控

在定义监控事件后,下一步就是读取对应监控寄存器结果,并按照对应定义计算结果。在 pcm.x 监控过程中,使用如下代码实现事件监控功能。

   m->getAllCounterStates(sstate1, sktstate1, cstates1);

但是在整个函数运行过程中,并没有找到对应变量 before.L3Miss 或 after.SKLL3Hit/after.L3UnsharedHit 等的赋值过程。

从头查看三个相关属性的定义,在头文件 cpucounters.h 中可以看到,相关变量采用的是联合体进行定义

  union  {
uint64 L3Miss;
uint64 Event0;
uint64 ArchLLCMiss;
};
union {
uint64 L3UnsharedHit;
uint64 Event1;
uint64 ArchLLCRef;
uint64 SKLL3Hit;
};
union {
uint64 L2HitM;
uint64 Event2;
uint64 SKLL2Miss;
};
union {
uint64 L2Hit;
uint64 Event3;
};

所谓联合体定义就是花括号内所有变量起始地址都完全相同,即在同一块内存地址区域内使用了多个变量名。从这就可以看出,在缓存监控中使用的四个事件监控寄存器对应变量分别为

• PCM0:L3Miss

• PCM1:L3UnsharedHit

• PCM2:L2HitM

• PCM3:L2Hit

对应计算不同层级缓存Miss和命中率公式为

• L3 Cache Miss = L3Miss

• L2 Cache Miss = L2HitM + L3UnsharedHit + L3Miss

• L3 Cache Hits = L3UnsharedHit + L2HitM

• L2 Cache Hits = L2Hit

• L3 Cache Hit Ratio = L3 Cache Hit /(L3 Cache Miss + L3 Cache Hits)

• L2 Cache Hit Ratio = L2 Cache Hit /(L2 Cache Miss + L2 Cache Hits)

通过以上公式,即可计算出对应 L2 和 L3 缓存对应 Miss 和命中率大小。

总结

本文对 pcm.x 代码运行过程进行了分析,考察了对缓存进行监控时需要采用事件和掩码编号,最后对结果输出过程中,缓存命中率计算公式进行了分析。

pcm.x代码分析的更多相关文章

  1. Android代码分析工具lint学习

    1 lint简介 1.1 概述 lint是随Android SDK自带的一个静态代码分析工具.它用来对Android工程的源文件进行检查,找出在正确性.安全.性能.可使用性.可访问性及国际化等方面可能 ...

  2. pmd静态代码分析

    在正式进入测试之前,进行一定的静态代码分析及code review对代码质量及系统提高是有帮助的,以上为数据证明 Pmd 它是一个基于静态规则集的Java源码分析器,它可以识别出潜在的如下问题:– 可 ...

  3. [Asp.net 5] DependencyInjection项目代码分析-目录

    微软DI文章系列如下所示: [Asp.net 5] DependencyInjection项目代码分析 [Asp.net 5] DependencyInjection项目代码分析2-Autofac [ ...

  4. [Asp.net 5] DependencyInjection项目代码分析4-微软的实现(5)(IEnumerable<>补充)

    Asp.net 5的依赖注入注入系列可以参考链接: [Asp.net 5] DependencyInjection项目代码分析-目录 我们在之前讲微软的实现时,对于OpenIEnumerableSer ...

  5. 完整全面的Java资源库(包括构建、操作、代码分析、编译器、数据库、社区等等)

    构建 这里搜集了用来构建应用程序的工具. Apache Maven:Maven使用声明进行构建并进行依赖管理,偏向于使用约定而不是配置进行构建.Maven优于Apache Ant.后者采用了一种过程化 ...

  6. STM32启动代码分析 IAR 比较好

    stm32启动代码分析 (2012-06-12 09:43:31) 转载▼     最近开始使用ST的stm32w108芯片(也是一款zigbee芯片).开始看他的启动代码看的晕晕呼呼呼的. 还好在c ...

  7. 常用 Java 静态代码分析工具的分析与比较

    常用 Java 静态代码分析工具的分析与比较 简介: 本文首先介绍了静态代码分析的基 本概念及主要技术,随后分别介绍了现有 4 种主流 Java 静态代码分析工具 (Checkstyle,FindBu ...

  8. SonarQube-5.6.3 代码分析平台搭建使用

    python代码分析 官网主页: http://docs.sonarqube.org/display/PLUG/Python+Plugin Windows下安装使用: 快速使用: 1.下载jdk ht ...

  9. angular代码分析之异常日志设计

    angular代码分析之异常日志设计 错误异常是面向对象开发中的记录提示程序执行问题的一种重要机制,在程序执行发生问题的条件下,异常会在中断程序执行,同时会沿着代码的执行路径一步一步的向上抛出异常,最 ...

随机推荐

  1. Codeforces1575D

    思路分析 此题采用dfs,注意X选中了之后所有的X值相同,所以需要一个flag来存储X的值. 注意前导0要单独讨论,然后就是当'X'或者'_'在第一位时不能选0,其它位可以选0 - 9 任意一个数. ...

  2. Java:static关键字小记

    Java:static关键字小记 对 Java 中的 static 关键字,做一个微不足道的小小小小记 static 修饰变量 静态变量:是被 static 修饰的变量,也称为类变量,它属于类,因此不 ...

  3. BUAA2020软工团队beta得分总表

    BUAA2020软工团队beta得分总表 [TOC] 零.团队博客目录及beta阶段各部分博客地址 团队博客 计划与设计博客 测试报告博客 发布声明博客 事后分析博客 敏 杰 开 发♂ https:/ ...

  4. Scrum Meeting 0423

    零.说明 日期:2021-4-23 任务:简要汇报两日内已完成任务,计划后两日完成任务 一.进度情况 组员 负责 两日内已完成的任务 后两日计划完成的任务 qsy PM&前端 完成引导页UI# ...

  5. Scrum Meeting 0609

    零.说明 日期:2021-6-9 任务:简要汇报两日内已完成任务,计划后两日完成任务 一.进度情况 组员 负责 两日内已完成的任务 后两日计划完成的任务 困难 qsy PM&前端 完成前端功能 ...

  6. STM32 学习笔记之中断应用概览--以f103为例

    异常类型 F103 在内核水平上搭载了一个异常响应系统, 支持为数众多的系统异常和外部中断.其中系统异常有8 个(如果把Reset 和HardFault 也算上的话就是10 个),外部中断有60个.除 ...

  7. 常用Java API:Math类

    求最值 最小值 Math.min(int a, int b) Math.min(float a, float b) Math.min(double a, doubleb) Math.min(long ...

  8. Python课程笔记(八)

    一些简单的文件操作,学过linux的话理解感觉不会很难.课程代码 一.OS 目录方法 这个模块提供了一种方便的使用操作系统函数的方法 函数 说明 os.mkdir("path") ...

  9. Cesium实现右键框选

    思路 1.先取消地图的右键事件 2.右键框选事件,屏幕坐标转为经纬度坐标 取消地图的右键事件 //此处容易犯一个错误:以为右键事件绑定了缩放功能,伪代码即 Cesium.MouseEvent.右键事件 ...

  10. 修改 oracle 数据库的 sys 账号密码,ERROR at line 1: ORA-01034: ORACLE not available

    挺久没有登录的 oracle 数据库,因为公司要求加固密码,登录后修改失败 1.启动数据库的同时启动控制文件.数据文件,提示:cannot mount database in EXCLUSIVE mo ...