
最近,我想了解这篇文章的内容并作一些说明. 希望我不要犯错. 如果有错误,请指出.
参考:
cenalulu.github.io/linux/all-about-cpu-cache /
/ archive / gallery-of-processor-cache-effects /
通常来说,x86结构的cpu缓存行的大小为64byte,而arm缓存行的大小为32byte.
缓存行可以简单地理解为CPU缓存中最小的缓存单元. 当前主流的CPU缓存缓存行大小为64Bytes. 假设我们有一个512字节的L1缓存,那么根据64B缓存单元的大小,该L1缓存可以存储的缓存数量为512/64 = 8.

会有很多cpu缓存行,然后不同的缓存行形成一个集合,这意味着同一集合中有多个缓存行. 如果一个集合有8条缓存行,则该集合可以缓存的数据为512字节
那么我们在编写代码时如何更好地使用缓存行?
尽可能按顺序访问内存,请勿随机访问内存. 因为高速缓存行的大小为64btye,所以如果访问的内存不是连续的,则该预取的内容将被浪费,并且对于写入,顺序写入也是一样,可以批量提交至高速缓存,这可以最大化使用总线带宽. 组织结构时,可以尽可能多地一起访问,因为这可以确保CPU从高速缓存行中的内存中读取数据,并且cpu默认情况下会预取结构中的填充,即,请根据64个字节尽可能地填充. 可以避免的问题是,如果多个CPU在64个bty之内获得了一个结构的不同变量,那么也可能会出现交叉缓存行,那么在修改其中一个时,您需要使另一个无效. 尤其是在多核方案中,此问题很明显.
但是这里还有一个折衷,就是我们将结构的结构设计得尽可能紧凑,以便更有效地利用cpu缓存行. 但是在这里添加了填充之后,结构并不紧凑,那么如何称量什么呢?
在读取次数较多而写入次数较少的情况下,您无需担心缓存冲突. 更重要的是存储器的紧凑性或局部性. 由于很少有多个内核修改同一个变量,因此需要填充设计. CPU读取两次.
但是在写入次数较多而读取次数较少的方案中,您需要更加注意填充,因为在多核方案中大量的写入很容易导致缓存失效,因此您需要更加注意填充 strong>

当然,仍然需要通过特定的基准来计算struct的特定设计.
典型代码dpdk中的rte_ring结构的设计如下:
struct rte_ring {
/*
* Note: this field kept the RTE_MEMZONE_NAMESIZE size due to ABI
* compatibility requirements, it could be changed to RTE_RING_NAMESIZE
* next time the ABI changes
*/
char name[RTE_MEMZONE_NAMESIZE] __rte_cache_aligned; /**< Name of the ring. */
int flags; /**< Flags supplied at creation. */
const struct rte_memzone *memzone;
/**< Memzone, if any, containing the rte_ring */
uint32_t size; /**< Size of ring. */
uint32_t mask; /**< Mask (size-1) of ring. */
uint32_t capacity; /**< Usable size of ring */
/** Ring producer status. */
struct rte_ring_headtail prod __rte_aligned(PROD_ALIGN);
/** Ring consumer status. */
struct rte_ring_headtail cons __rte_aligned(CONS_ALIGN);
};
linux文件打开文件表的结构
/*
* Open file table structure
*/
struct files_struct {
/*
* read mostly part
*/
atomic_t count;
struct fdtable *fdt;
struct fdtable fdtab;
/*
* written part on a separate cache line in SMP
*/
spinlock_t file_lock ____cacheline_aligned_in_smp;
int next_fd;
struct embedded_fd_set close_on_exec_init;
struct embedded_fd_set open_fds_init;
struct file * fd_array[NR_OPEN_DEFAULT];
};
这里还将频繁读取的部分与写入的部分分开,以便该部分数据位于不同的缓存行中.

尽可能只让一个cpu访问某个变量的内存,以使高速缓存行无效的可能性大大降低. 在代码中,尽可能减少全局变量. 与cpu架构类似,执行批处理,流水线操作. <
/ papers / modernprocessors /
例如,内核代码中也有这样的代码
那么在我们与存储相关的项目中,关注cpu缓存的好处是否很大?
考虑到延迟,实际上并没有那么大.
对cpu缓存更友好并且不关注cpu缓存友好代码的书面代码可能只会使性能提高一倍(数据不是自检数据),但是对于存储服务,通常的瓶颈是在网卡中. 无论在磁盘上,CPU都占整个路径的很小一部分.

那么优化用于内存访问的cpu缓存有意义吗?

例如,从该图可以看出,即使是二级缓存: 主内存也几乎是1:10. 也就是说,如果对l2缓存的特定访问时间为10ns,则内存访问时间为100ns,优化前的总访问时间为10 + 100 = 110ns,优化后的访问时间为10/2 + 100 = 105 ns. 似乎延迟没有太大改善. 性能优化只有5%.
实际上,优化cpu缓存意味着什么?
实际上,我们还没有考虑另一个方面,那就是对CPU的利用.
我们只考虑了延迟,但是我们没有考虑的是,如果cpu从内存访问数据,则cpu实际上正在挂起,这意味着它将增加cpu的利用率,但也会无形地影响它的执行. cpu的其他说明. 也就是说,如果对cpu没有压力,那么对cpu缓存友好的代码可以使内存访问性能提高5%,但是在cpu利用率相对较高的情况下,代码对cpu缓存不友好,它将导致cpu管道中的所有指令变慢,从而进一步增加了对cpu的压力.
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/shoujiruanjian/article-312713-1.html