前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >DAY53:阅读Profiler Counter Function

DAY53:阅读Profiler Counter Function

作者头像
GPUS Lady
发布2018-08-01 14:59:37
7120
发布2018-08-01 14:59:37
举报
文章被收录于专栏:GPUS开发者
我们正带领大家开始阅读英文的《CUDA C Programming Guide》,今天是第53天,我们正在讲解CUDA C语法,希望在接下来的47天里,您可以学习到原汁原味的CUDA,同时能养成英文阅读的习惯。

B.17. Profiler Counter Function

Each multiprocessor has a set of sixteen hardware counters that an application can increment with a single instruction by calling the __prof_trigger() function.

代码语言:javascript
复制
void __prof_trigger(int counter);

increments by one per warp the per-multiprocessor hardware counter of index counter. Counters 8 to 15 are reserved and should not be used by applications.

The value of counters 0, 1, ..., 7 can be obtained via nvprof by nvprof --events prof_trigger_0x where x is 0, 1, ..., 7. All counters are reset before each kernel launch (note that when collecting counters, kernel launches are synchronous as mentioned in Concurrent Execution between Host and Device).

本文备注/经验分享:

今天只涉及一个简单的内置函数: __prof_trigger() 该函数比较简单, 但是在很多时候很有用. 我们来看一下它的主要用途和需要注意的事项(一些可能会出乎你意外的东西)。 该函数除了叫profiler counter, 也叫performance monitors, 因此故名思议, 它的主要作用是和性能评估有关. 我们都知道, 一般情况下的开发, 是在开发机器的显卡上, 通过nvprof或者nvvp来做profiling的(性能分析) 这种方式, 通过通过命令行或者图形界面的用户交互,逐步的用户发现当前代码运行后可能出现的性能问题, 然后根据profiler的报告和建议, 逐步的优化自己的代码实现.但是在实际的生产中, 这种方式有一定的问题, 主要集中在两点上: (1)开发时候的卡, 不一定是最终生产运行时候的卡.例如你可以使用GP100上开发, 最后交付后, 客户在Titan-V上运行.此时如果再要求使用图形界面的NVVP(Nvidia Visual Profiler)是不现实的, 用户也不具有现场分析能力. (2)一些代码的运行结果和实际生产环境中的数据输入有关, 而在开发机器上用的开发测试数据, 总是有限的, 无法覆盖全面情况. 这样可能一些算法在数据集A下的运行效率还可以, 但是在实际的数据集B(无法提前知道)性能一般.此时开发者可能就需要能让代码自我调优. 或者简单的, 可以体现实现多种代码路径,在不同的运行环境卡下, 实际的能多少执行不同的代码路径选择或者kernel实现选择.则此时, 应当考虑本章节说道的__prof_trigger()函数.我举个例子说, 开发的时候, 使用的是GDDR5, 256-bit的卡, 而运行的时候则可能是HBM2或者DDR4.此时一张卡原本卡计算的, 现在瓶颈变成在在访存上,或者原本卡访存的, 现在瓶颈变成了在计算上.类似这种的. 而用户则可以配合clock()或者clock64(), 来分析单个线程或者warp中,某段代码的具体串行执行时间代价,然后分别在开发的时候, 准备好不同版本的代码片段, 然后发布的时候, 分别分析每个片段大致的周期代价, 选择不同的版本来执行.有的人看到这里可能会问, 这个完全不需要使用这些函数啊?例如她可以说, "我原本就是这样做的, 通过整体的时间(kernel)进行event测时"或者她可以说, "我原本将kernel中的多个片段用clock64()之类的隔离出来了, 然后将结果保存到global memory然后取回查看",这样不都是可以吗? 请有注意本章节的该函数有特色的:主要特色在于, 代价非常低, 或者通俗的说, "Low Profiling Overhead" 你保存到显存固然可以, 但是这种方式本身, 会造成干扰(例如你的kernel也在进行访存),而本章节的__prof_trigger, 则提供了一种独立的高速性能统计方式, 基本不造成性能干扰,(和执行普通一条单精度浮点加法的代价差不多),同时可以高速的进行统计(大约每周期每SM可以统计约4次(按照warp计)), 所以这种低代价的分析函数, 基本上对实际性能的影响很少.此外, 用户固然可以进行全局分析(kernel整体, 通过event计时),但是很多时候, 这种方式无法对具体的代码中的片段进行,例如用户可能会怀疑自己的某段非常分散的代码, 访存的代码非常高昂(例如大范围近乎随机的访存),此时如果整体测时, 无法提供任何指导意义(例如用户可能夹杂在一堆计算或者其他方式的访存整体中),而通过简单的在访存前和使用后的周期计算,用户可以单独的对超过, 例如1000个周期的结果要求增加特殊的计算数器3,而超过2000个周期的访存(某些代码实际执行中很常见的, 超高延迟), 要求增加计数器值4,最后用户可以搜集这些计数器的结果,然后发现, 哈, 我90%的访存都超过了2000个周期延迟, 只有10%的小于等于1000个周期, 肯定哪里有问题啊. 我决定让代码自动切换到使用计算换取访存吧(例如用户可以提前准备好2个code path, 根据运行时刻的标志选择),或者发现, 哈, 我完全没有计算数值4的问题, 这个代码片段应当执行路径XYZ,类似这种的.这种low overhead的分析方式, 非常有用.除了这些常见的作用外, 该函数还具有一些常见的坑, 需要说明一下.(1)该函数增加的计数器值warp为单位的. 只要warp中有1个线程能增加计数器值, 那么计数器值就会增加1,这也是本章节提到的注意事项.换句通俗的话说, warp内部有1到31到线程分量(lanes), 执行的效果是一样的。所以请尽量不要warp内分支的调用此函数, 除非你知道你在做什么.(2)点则是, 该函数因为很高效, 实际上会编译成单条低代价指令(刚才说了, 如同一次单精度的加法的代价而已),而该单条低代价指令, 支持的是16-bit的立即数(Immediate, 指的是嵌入在指令中的操作数) 因此在调用该函数的时候, 你应当直接给出常数(编译时刻能确定的常数)做为参数.例如调用方式: __prof_trigger(3)来增加3号计数器.而不要写成: __prof_trigger(n)来增加第n号性能计数器(其中n是个变量, 而且无法从编译时刻确定),如果给出了后者, 会造成高昂的性能分析代价.因为刚才说过, 该低代价指令接受的立即数形式的常数,如果你给出了n(0-15, 其中用户应当使用前8个), 编译器会生成一条具有16条等效分支的路径, 和16条条件执行的profiler trigger指令, 造成巨大的代价.请尽量规避这么做.这是该函数的主要问题..

然后需要补充的是: (1)你可以同时增加多个计数器的, 例如假设你的代码手工展开量一个整数求模计算, 根据 a % b中的数据的范围不同, 例如小于24-bit, 走快速float模拟. 或者大数据范围的走double模拟. 你分别需要统计读取操作数的代价(例如刚才说的超过1000个周期延迟的), 和, 需要走慢速double路径的代价(假设你对这两种情况分别指定了计数器3和4), 则你可能需要同时增加2个计数器, 以便进一步的降低本函数本身的代价(虽然已经很低了, 但有时候对于小代码路径, 例如只有几条到10几条路径的), 则可以考虑使用能同时增加多个性能计数器的版本, 该版本已经导出到了PTX中. 可以直接嵌入. (2)如果能现场调试和分析, 尽量现场分析, 新版本的profiler和较新的卡(Maxwell/Pascal+)所能支持的PC Sampling/Instruction Sampling功能, 非常易用(毕竟是图形化界面的), (3)在真正需要的场合, 真无法考虑2的, 能用本章节的本函数的, 果断使用. 但需要注意的是, 性能计数器的值无法直接读取回来.你可以考虑通过cupti来得到它们(请参考CUPTI手册),或者如果在Linux下的话, 可以简单的考虑通过nvprof外加grep的方式, 来直接搜集特定的性能计数器的值.(或者例如你可以单独在首次特定环境运行的时候, 通过perl脚本之类的东西, 直接正则分析nvprof的结果之类的),而如何指定nvprof来获取相关Event的值, 已经在本章节说过了. 注意其中的给出常数方式的调用(__prof_trigger(3)例如), 一定要注意.这是造成使用该函数的问题的主要来源.总之如非必要, 不要使用__prof_trigger(编译时刻无法确定的变量)的形式.除非你知道你在做什么.

有不明白的地方,请在本文后留言

或者在我们的技术论坛bbs.gpuworld.cn上发帖

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2018-07-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 GPUS开发者 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • B.17. Profiler Counter Function
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档