简介

运行说明

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. 剑指offer:JZ8 二叉树的下一个结点

    JZ8 二叉树的下一个结点 描述 给定一个二叉树其中的一个结点,请找出中序遍历顺序的下一个结点并且返回.注意,树中的结点不仅包含左右子结点,同时包含指向父结点的next指针.下图为一棵有9个节点的二叉 ...

  2. httpclient 登录成功后返回的cookie值访问下一页面

    HttpClient4.x可以自带维持会话功能,只要使用同一个HttpClient且未关闭连接,则可以使用相同会话来访问其他要求登录验证的服务(见TestLogin()方法中的"执行get请 ...

  3. UltraSoft - Beta - Scrum Meeting 1

    Date: May 17th, 2020. Scrum 情况汇报 进度情况 组员 负责 今日进度 q2l PM.后端 维护Beta阶段文档 Liuzh 前端 增加删除操作按钮 Kkkk 前端 查询增加 ...

  4. Noip模拟58 2021.9.21(中秋祭&&换机房祭)

    第一次在学校过中秋节,给家里人视频电话,感觉快回家了很开心, 然后还吃了汉堡喝饮料非常爽,颓废了一会儿还换了新机房,$Linux2.0$非常dei,少爷机也非常快, 发现好像测评机又成了老爷机,这就是 ...

  5. 冲刺noip2021模拟16

    T1 树上的数 考场上比较脑瘫没有想到直接dfs就行了这样是O(n+m)的,傻不拉几地多添了个log, 不过因为accoder的评测机太弱了,绝大多数人的正解都是60分,所以没有什么差别: 直接dfs ...

  6. Atcoder 题目泛做

    我思维越来越菜了,这样下去感觉要退役了. 听说Atcoder的题练思维?那就试着做一做吧. 坚持每天一两道吧.(很有可能咕掉.) AGC036 官方题解 ---A-Triangle          ...

  7. 记一次线上环境 ES 主分片为分配故障

    故障前提 ElasticSearch 版本:5.2 集群节点数:5 索引主分片数:5 索引分片副本数:1 线上环境ES存储的数据量很大,当天由于存储故障,导致一时间 5个节点的 ES 集群,同时有两个 ...

  8. idea如何在终端使用git并解决终端中文乱码

    idea使用git终端 在idea设置中 找到Settings-Tools-Terminal-Shell path,替换为git安装目录下的bin/bash.exe 解决中文乱码 在git安装目录下找 ...

  9. Go语言核心36讲(Go语言实战与应用三)--学习笔记

    25 | 更多的测试手法 在本篇文章,我会继续为你讲解更多更高级的测试方法.这会涉及testing包中更多的 API.go test命令支持的,更多标记更加复杂的测试结果,以及测试覆盖度分析等等. 前 ...

  10. Handler处理器&&使用代理服务器urllib.request.ProxyHandler

    urllib.request.urlopen(url)  不能定制请求头 urllib.request.Request(url,headers,data) 可以定制请求头 Handler 定制更高级的 ...