mirror of
https://github.com/espressif/esp-idf.git
synced 2026-09-22 13:01:16 +03:00
48 lines
3.0 KiB
ReStructuredText
48 lines
3.0 KiB
ReStructuredText
缓存访问计数器
|
||
==============
|
||
|
||
:link_to_translation:`en:[English]`
|
||
|
||
简介
|
||
----
|
||
|
||
{IDF_TARGET_NAME} 的缓存请求总线上带有硬件计数器,用于记录已完成的缓存访问次数、未命中停顿事件、请求方冲突次数,以及与下一级存储之间传输的缓存行数量。借助这些计数器,可以测量某段代码的缓存命中/未命中情况,例如用于选择数据在内存中的放置位置,或分析某段代码运行速度不及预期的原因。
|
||
|
||
计数器单元
|
||
----------
|
||
|
||
不同芯片提供的计数器有所不同:缓存层级数、每个缓存的请求总线数量,以及每条总线上存在哪些计数器都可能不同。因此,API 没有定义固定的缓存列表,而是提供由芯片定义的计数器 **单元** 列表。每个单元是观察同一数据流的一组计数器,例如 CPU0 向 L1 缓存发出的取指请求。应用程序可在运行时通过 :cpp:func:`esp_cache_cnt_num_units` 和 :cpp:func:`esp_cache_cnt_get_unit_info` 枚举这些单元,因此当新芯片增加或减少单元时,应用程序无需修改。
|
||
|
||
使用方法
|
||
--------
|
||
|
||
1. 调用 :cpp:func:`esp_cache_cnt_start`,清零并使能所有计数器。
|
||
2. 运行需要测量的代码。
|
||
3. 调用 :cpp:func:`esp_cache_cnt_stop` 停止计数,这样读取和打印结果本身不会被计入。
|
||
4. 调用 :cpp:func:`esp_cache_cnt_dump` 打印所有计数器数值的表格,或通过 :cpp:func:`esp_cache_cnt_get` 读取单个单元的计数值。
|
||
|
||
:cpp:func:`esp_cache_cnt_clear` 在不改变计数使能状态的情况下将计数器清零,适用于需要在多个测量阶段之间保持计数器使能的场景。
|
||
|
||
计数器含义
|
||
----------
|
||
|
||
对于每个单元,:cpp:func:`esp_cache_cnt_get` 返回以下计数值:
|
||
|
||
- ``accesses``:已完成的访问次数。硬件的“命中”计数器在每次访问完成时加一,无论该访问是否先等待了缓存行填充,因此这里将其报告为总访问次数。
|
||
- ``stall_events``:访问因未命中而停顿期间会反复递增。该数值随未命中总延迟增长,而不是未命中的访问次数,因此只能用作相对指标。
|
||
- ``conflicts``:该缓存上请求方之间的冲突次数。
|
||
- ``line_fills``:从下一级存储取回的缓存行数量。这是真正的未命中次数。
|
||
- ``writebacks``:写回到下一级存储的缓存行数量。仅在具有写回型缓存的芯片上对数据流量提供。
|
||
|
||
因此,一个单元的未命中率为 ``line_fills / accesses``,可通过 :cpp:func:`esp_cache_cnt_miss_ratio` 计算。并非每个单元都具有全部计数器;只有当 :cpp:struct:`esp_cache_cnt_data_t` 的 ``valid_mask`` 成员中相应位被置位时,对应字段才有意义。
|
||
|
||
应用示例
|
||
--------
|
||
|
||
- :example:`system/cache_counters` 对不同大小和位置的工作集运行读取负载,并在每次运行后打印计数器数值,展示工作集大小如何决定由存储层级中的哪一级来响应访问。
|
||
|
||
API 参考
|
||
--------
|
||
|
||
.. include-build-file:: inc/esp_cache_cnt.inc
|