缓存访问计数器
简介
ESP32-C61 的缓存请求总线上带有硬件计数器,用于记录已完成的缓存访问次数、未命中停顿事件、请求方冲突次数,以及与下一级存储之间传输的缓存行数量。借助这些计数器,可以测量某段代码的缓存命中/未命中情况,例如用于选择数据在内存中的放置位置,或分析某段代码运行速度不及预期的原因。
计数器单元
不同芯片提供的计数器有所不同:缓存层级数、每个缓存的请求总线数量,以及每条总线上存在哪些计数器都可能不同。因此,API 没有定义固定的缓存列表,而是提供由芯片定义的计数器 单元 列表。每个单元是观察同一数据流的一组计数器,例如 CPU0 向 L1 缓存发出的取指请求。应用程序可在运行时通过 esp_cache_cnt_num_units() 和 esp_cache_cnt_get_unit_info() 枚举这些单元,因此当新芯片增加或减少单元时,应用程序无需修改。
使用方法
调用
esp_cache_cnt_start(),清零并使能所有计数器。运行需要测量的代码。
调用
esp_cache_cnt_stop()停止计数,这样读取和打印结果本身不会被计入。调用
esp_cache_cnt_dump()打印所有计数器数值的表格,或通过esp_cache_cnt_get()读取单个单元的计数值。
esp_cache_cnt_clear() 在不改变计数使能状态的情况下将计数器清零,适用于需要在多个测量阶段之间保持计数器使能的场景。
计数器含义
对于每个单元,esp_cache_cnt_get() 返回以下计数值:
accesses:已完成的访问次数。硬件的“命中”计数器在每次访问完成时加一,无论该访问是否先等待了缓存行填充,因此这里将其报告为总访问次数。stall_events:访问因未命中而停顿期间会反复递增。该数值随未命中总延迟增长,而不是未命中的访问次数,因此只能用作相对指标。conflicts:该缓存上请求方之间的冲突次数。line_fills:从下一级存储取回的缓存行数量。这是真正的未命中次数。writebacks:写回到下一级存储的缓存行数量。仅在具有写回型缓存的芯片上对数据流量提供。
因此,一个单元的未命中率为 line_fills / accesses,可通过 esp_cache_cnt_miss_ratio() 计算。并非每个单元都具有全部计数器;只有当 esp_cache_cnt_data_t 的 valid_mask 成员中相应位被置位时,对应字段才有意义。
应用示例
system/cache_counters 对不同大小和位置的工作集运行读取负载,并在每次运行后打印计数器数值,展示工作集大小如何决定由存储层级中的哪一级来响应访问。
API 参考
Header File
This header file can be included with:
#include "esp_cache_cnt.h"
Functions
-
size_t esp_cache_cnt_num_units(void)
Number of counter units on this chip.
- 返回:
Number of units; 0 if the chip has no cache access counters.
-
esp_err_t esp_cache_cnt_get_unit_info(size_t unit, esp_cache_cnt_unit_info_t *out)
Get the description of a counter unit.
- 参数:
unit -- Unit index, 0 to esp_cache_cnt_num_units() - 1
out -- [out] Unit description
- 返回:
ESP_OK on success
ESP_ERR_INVALID_ARG if unit is out of range or out is NULL
-
esp_err_t esp_cache_cnt_start(void)
Clear and enable all cache access counters.
- 返回:
ESP_OK on success
ESP_ERR_NOT_SUPPORTED if the target has no cache access counters
-
esp_err_t esp_cache_cnt_stop(void)
Disable all cache access counters. Counter values are retained.
- 返回:
ESP_OK on success
ESP_ERR_NOT_SUPPORTED if the target has no cache access counters
-
esp_err_t esp_cache_cnt_clear(void)
Reset all cache access counters to zero. Counting state is not changed.
- 返回:
ESP_OK on success
ESP_ERR_NOT_SUPPORTED if the target has no cache access counters
-
esp_err_t esp_cache_cnt_get(size_t unit, esp_cache_cnt_data_t *out)
Read the current counter values for the given unit.
- 参数:
unit -- Unit index, 0 to esp_cache_cnt_num_units() - 1
out -- [out] Counter values
- 返回:
ESP_OK on success
ESP_ERR_INVALID_ARG if unit is out of range or out is NULL
ESP_ERR_NOT_SUPPORTED if the target has no cache access counters
-
float esp_cache_cnt_miss_ratio(const esp_cache_cnt_data_t *data)
Miss ratio (0.0 to 1.0) computed from a set of counter values.
- 参数:
data -- Counter values obtained from esp_cache_cnt_get()
- 返回:
Miss ratio; 0.0 if the unit does not provide the counters needed to compute it.
Structures
-
struct esp_cache_cnt_unit_info_t
Description of one counter unit.
Public Members
-
const char *name
Short human-readable name, e.g. "l1-icache-core0"
-
uint8_t cache_level
Cache level the counters belong to, counting from the CPU. On most chips there is a single level (the flash/PSRAM cache); on the ESP32-P4, level 1 is the L1 cache in front of internal memory and level 2 is the flash/PSRAM cache.
-
cache_profile_traffic_t traffic_type
Kind of traffic observed
-
int8_t core_id
Core the traffic originates from, or -1 if unknown/mixed
-
const char *name
-
struct esp_cache_cnt_data_t
Counter values for one unit.
Note that the hardware "hit" and "miss" counters do not directly hold the number of hit and missed accesses:
accesses: the hit counter increments once for every access that completes, whether or not it had to wait for a line fill first.
stall_events: the miss counter increments repeatedly while an access is stalled on a miss, so it grows roughly with the total miss latency. This is only useful as a relative measure.
line_fills: the next-level read counter increments once per line fetched from the next level, so it is the true miss count.
The miss ratio of a cache is therefore line_fills / accesses.
Public Members
-
uint32_t valid_mask
Bitwise OR of ESP_CACHE_CNT_VALID_* flags for the fields below
-
uint32_t accesses
Completed accesses (hardware hit counter)
-
uint32_t stall_events
Miss stall events; grows with total miss latency, NOT the number of missed accesses
-
uint32_t conflicts
Conflicts between requesters on this cache
-
uint32_t line_fills
Lines fetched from the next level (true miss count)
-
uint32_t writebacks
Lines written back to the next level. Only present for data traffic on chips with a write-back cache (PSRAM support, see SOC_CACHE_WRITEBACK_SUPPORTED).
Macros
-
ESP_CACHE_CNT_VALID_ACCESSES
-
ESP_CACHE_CNT_VALID_STALL_EVENTS
-
ESP_CACHE_CNT_VALID_CONFLICTS
-
ESP_CACHE_CNT_VALID_LINE_FILLS
-
ESP_CACHE_CNT_VALID_WRITEBACKS