【96】write combine机制介绍

news/2024/7/7 12:54:16


前言

这篇文章主要介绍了write combine的机制


一、write combine的试验

1.系统配置

(1)、CPU:11th Gen Intel(R) Core(TM) i7-11700 @ 2.50GHz

(2)、GPU:XX

(3)、link status:X16 GEN4(受限于CPU能力)

(4)、主板: TUF GAMING B560M-PLUS

(5)、linux  5.15.0-107-generic,ldd (Ubuntu GLIBC 2.31-0ubuntu9.16) 2.31

注意:CPU write combine受到CPU write combine buffer大小、write combine buffer entry数量、CPU的运行频率以及CPU evict write combine buffer的策略影响非常大,因此,绝对速率和CPU有关,这里只需要关注两种情况下,速率提高比例就可以了。

2、数据对比

    可见使用memcpy 把32M数据从system main memory拷贝的到GDDR,开启write combine后,CPU写的性能提供了13.3倍。这是怎么做到的呢?在讲write combine前需要先讲一下处理器的memory system,以及memory system中的cache。

二、背景知识:X86下的memory system

1、X86下的memory system简介

    上图中的白色框就是memory system的组件。其中main memory在处理器芯片外,是离processor execution units最远的。

    Cache是离processor execution units(处理器中负责执行指令和计算操作的部分)最近的。图中只是画了三种cache(L1 instruction cache、L1 data cache、L2 cache),实际上,对于多CPU core还有L3 cache。不同的微架构,L1/L2/L3的功能可能不太相同,但是基本原理差不多。

    L1 cache和L2 cache一般是每个core都有自己独立的,只是L1 cache通常会被分成L1 data cache和L1 instruction cache ,而L2 cache的data 和instruction是合一的。

2、skylake CPU的cache hierarchy

    下图是intel skylake CPU的 cache hierarchy(缓存层次结构),可看到每个CPU core有自己独立的L1和L2 cache,但是L3 cache(LLC)是share给所有的processors的。

    在L2 cache和L3 cahce之间是L2 Snoop Filter, 是skylake架构中的一个缓存一致性机制。L2 Snoop Filter通过减少不必要的snoop请求(记录缓存数据的位置),来减少的缓存之间的通信开销,提高缓存一致性协议的效率。

    所谓缓存一致性就是在多核处理器系统中,各个核的缓存需要保持一致性,以确保不同核访问同一内存地址时,数据是一致的。当一个核需要访问另一个核的缓存数据时,会发出 snoop 请求。这种请求会在总线上广播,以检查其他缓存中是否有该数据。

    关于snoop,Intel64 andlA-32 Architectures Software Developer's Manual的11.2 CACHING TERMINOLOGY讲得比较清楚了。

    在MP(多处理器)系统中,像Intel486和Intel 64这样的处理器具有snoop(侦听、监视)其他处理器访问system memory和访问处理器internal cache的能力,也就是所谓的“snoop”。 Snoop的功能让处理器的internal cache与system memory和总线上其他处理器中的cache保持一致。

     例如,在Pentium和P6系列处理器中,通过侦听功能,如果处理器A检测到另一个处理器打算写一块memory location,而这块memory location已经自己被cache了,侦听处理器A就会invalidate自己的cache line,强制让自己在下一次访问相同的memory location时执行cache line fill的操作(从system memory来获取最新的数据)。

    从P6系列处理器开始,如果一个处理器A检测到另一个处理器正在尝试访问一块memory location,而这块memory location已经在自己的cache中修改,但尚未写回到system memory,那么侦听处理器A将向其他处理器发出HITM#信号(Hold In Transaction Mode),这个信号告诉另一个处理器该cache line处于modify状态。

    在这种情况下,具有有效修改数据的处理器可能会直接将此数据传递给其他处理器,而不立即将其写回system memory中。负责管理系统内存的memroy controller监视此操作,以确保系统内存最终得到最新的数据。因此,即使数据可能在处理器之间共享而不立即写回内存,内存控制器也确保系统内存最终反映出最新的数据。。

    也就说,任何一个处理器别想偷偷干坏事(让自己的internal cache和其他处理的internal cache不一致),其他处理器一直在偷偷看着呢。

3、memory type(cache type)

    前面说了那么多,就是为了引出cache type(memory type),Intel 64和IA-32架构定义下面的memory type。

    对WC类型的memory的写操作可能会delay并被combine在write combing buffer中,因此来减少memory access。如果WC buffer只是部分填充,write会被delay直到下一个串行事件发生,比如SFENCE或者MFENCE指令,CPUID或者其他串行指令,read或者write uncached memory,中断产生,或者执行LOCK指令。

    WC 类型的cache-control非常适合video frame buffer,因为写order不重要。只要写入操作更新了内存,显示内容能够正确地显示在屏幕上即可。换句话说,即使写入操作的顺序不同,只要最终的内存状态能够正确反映出视频内容,就可以保证画面的正常显示。

4、Write combining buffer

    往WC memory的写的数据会被保存在internal write combining buffer,这个buffer和L1/L2/L3 cache是不同的(见前面的Figure 7-1. Processor and Memory System)。WC buffer没有snoop功能,因此,不能提供数据一致性。

    WC缓冲区的大小和结构在本架构文档上没有定义(也就说是vendor specific的)。对于Intel Core 2 Duo、Intel Atom、Intel Core Duo、Pentium M、Pentium 4和Intel Xeon处理器,WC缓冲区由若干个64字节的WC缓冲区组成。对于P6系列处理器,WC缓冲区由若干个32字节的WC缓冲区组成

    当软件开始向WC内存写入数据时,处理器就开始逐个填充WC缓冲区当一个或多个WC缓冲区被填满后,处理器可以选择将数据从write combining buffer evict到system memory。WC缓冲区evict协议取决于厂商具体的实现,软件不能依赖此协议来确保系统内存的一致性。使用WC内存类型时,软件必须要注意:数据延迟写入系统内存,并在需要系统内存一致性时,需要软件主动清空WC缓冲区

    一旦处理器开始从WC缓冲区将数据evict到系统内存,它将基于缓冲区中包含的有效数据量做bus-transaction style的决策。如果缓冲区已满(例如,所有字节都是有效的),处理器将在总线上执行一个burst-write transaction。一个burst-write transaction会有32字节(P6系列处理器)64字节(奔腾4及更高版本处理器)的数据会传输到data bus上。如果WC缓冲区的数据有一个或多个字节是无效的(例如,尚未被软件写入),处理器将使用“partial”transaction将数据传输到内存(一次一个chunk,其中一个“chunk”是8字节)。

    “partial”transaction将导致将WC缓冲区中的数据发送到内存时最多进行4个(32/8)“partial”transaction(对于P6系列处理器)或8个(64/8)“partial”transaction(对于奔腾4及更高版本处理器)。

    WC内存类型在定义上是weakly order的。一旦开始evict WC缓冲区中的数据,数据就受到weakly order的影响。在连续分配/释放WC缓冲区时不会保持顺序(例如,对WC缓冲区1的写入后,然后对WC缓冲区2的写入,可能在系统总线上看起来是缓冲区2先于缓冲区1)。使用partial write把数据从WC缓冲区中evict到system memory时,连续部分写之间也没有保证的顺序(例如,chunk 2的partial write可能在总线上先于chunk 1的partial write,反之亦然)。

三、Linux中Write combine相关函数

1、使能PAT的debug打印

    关于pat的介绍可以参考

13. PAT (Page Attribute Table) — The Linux Kernel documentation

    在cmdline增加debugpat就可以让dprintk打印

    下面的打印就是我们cmdline增加debugpat,然后调用对应的API接口时,OS的打印:

[28168.324996] x86/PAT: memtype_reserve added [mem 0x6000000000-0x67ffffffff], track write-combining, req write-combining, ret write-combining

[28168.325001] mtgpu 0000:01:00.0: Enable mtrr success,base:0x6000000000,size:0x800000000, mtrr 0.

[28168.325006] x86/PAT: Overlap at 0xa4a00000-0xa4a48000

[28168.325009] x86/PAT: memtype_reserve added [mem 0xa4000000-0xa5ffffff], track uncached-minus, req uncached-minus, ret uncached-minus

[28168.325021] mtgpu 0000:01:00.0: PCIe BAR0 map start:0xa4000000 size:0x2000000, virtal addr:00000000d90d9aa4 (attribution: UC)

[28168.325028] x86/PAT: Overlap at 0x6000000000-0x6800000000

[28168.325030] x86/PAT: memtype_reserve added [mem 0x6000000000-0x67ffffffff], track write-combining, req write-combining, ret write-combining

[28168.325038] mtgpu 0000:01:00.0: PCIe BAR2 map start:0x6000000000 size:0x800000000, virtal addr:00000000542d4401 (attribution: WC)

2、debugFS中PAT

    cat /sys/kernel/debug/x86/pat_memtype_list,在我们drv中调用对应API后,debugFS会显示对应的page attribute。

    Linux 代码中的实现如下:

 3、API接口

3.1 arch_io_reserve_memtype_wc

4.9.0增加了arch_io_reserve_memtype_wc

[PATCH 1/2] x86/io: add interface to reserve io memtype for a resource range. - Dave Airlie

memtype.c - arch/x86/mm/pat/memtype.c - Linux source code (v5.15) - Bootlin

3.2 memtype_reserve_io

arch_io_reserve_memtype_wc->memtype_reserve_io

memtype.c - arch/x86/mm/pat/memtype.c - Linux source code (v5.15) - Bootlin

3.3 memtype_reserve

arch_io_reserve_memtype_wc->memtype_reserve_io->memtype_reserve

memtype.c - arch/x86/mm/pat/memtype.c - Linux source code (v5.15) - Bootlin

    memtype_reserve是API调用栈的核心代码,主要实现下面功能

(1)memtype_reserve->pat_x_mttr_type来获取actual_type,

(2)然后memtype_reserve->pat_pagerange_is_ram来检查start和end是否不是RAM

./kprobe 'r:myopen pat_pagerange_is_ram $retval'

    使用kprobe查看pat_pagerange_is_ram的返回值,当传入的start和end是MMIO地址的话,pat_pagerange_is_ram返回是的0。

(3)申请一个entry_new,memtype_reserve->memtype_check_insert,把新的entry插入到memtype_rbroot

./kprobe 'r:myopen pat_pagerange_is_ram $retval'

3.4 memtype_check_insert

arch_io_reserve_memtype_wc->memtype_reserve_io->memtype_reserve->memtype_check_insert

memtype_interval.c - arch/x86/mm/pat/memtype_interval.c - Linux source code (v5.15) - Bootlin

3.5、哪些接口会调用memtype_check_insert

./kprobe -s 'p:myprobe memtype_check_insert'

可以看到涉及到使用MMIO的下面接口都会调用memtype_check_insert

3.6、ioremap和ioremap_wc区别

ioremap.c - arch/x86/mm/ioremap.c - Linux source code (v5.15) - Bootlin

    ioremap和ioremap_wc区别就是传入的page attribute不一样。最终还是会调用memtype_check_insert把对应的entry放到memtype_rbroot。

3.7 arch_phys_wc_add

linux kernel 3.11.0增加了arch_phys_wc_add

如果pat_enable已经enable或者mtrr_enable没有enable,arch_phys_wc_add直接返回了。

四、开启write combine后访问MMIO提升的原因

    回到我们最初的问题,使用memcpy 把32M数据从system main memory拷贝的到GDDR,开启write combine后,CPU写的性能提供了13.3倍。这是怎么做到的呢?在回答这个问题前,我们先看看两种场景下PCIe trace的差异。

1、disable write combine时PCIe trace

    我们从PCIe trace上看到第一笔写到最后一笔写,耗时是90753us,和测试代码抓到的耗时(90707us)一致。

    分析trace可以发现,disable write combine时,所有写请求都是32byte的,32M的数据需要32M/32= 1,048,576笔操作。

    这里为啥每一笔写请求都是32byte,而不是4byte的,和glibc对于memcpy的实现有关系,这里不展开说明,有兴趣的看glibc的memcpy作者马凌的回答。

https://www.zhihu.com/question/35172305

    随机找了两笔相邻的请求,时间间隔93ns,看了一下统计基本上在80-100ns之间,个别可能超过100ns。1,048,576笔传输,耗时90753us,每传输32Byte耗时90753us/1,048,576=86.5ns,每传输64Byte需要耗时173ns。

2、enable write combine时PCIe trace

    我们从PCIe trace上看到第一笔写到最后一笔写,耗时是6836us,和测试代码抓到的耗时(6837us)一致。

    分析trace可以发现,disable write combine时,所有写请求都是64byte的(和前面intel architecture介绍的WC buffer是64Byte是吻合的),32M的数据需要32M/64= 524,288笔操作。

    随机找了两笔相邻的请求,时间间隔8ns,看了一下统计差异比较大,大部分都在10~20ns之间。524,288笔传输,耗时6837us,每传输64Byte耗时6837us/524,288= 13ns,数据是吻合的。

    猜测每次时间间隔比较大的时候,都是处理器在fill WC buffer的时候(只是猜测,没有datasheet没法证明),只是这种猜测符合WC memory delay and combine的设计。


总结

    使用memcpy 把32M数据从system的main memory拷贝的到GDDR,开启write combine后,CPU写的性能提供了13.3倍,主要原因不是写的transaction的combine(transaction数量只少了一半),而在于数据先处理器被从main memory预取到write combine buffer,然后从write combine buffer使用burst 传输方式推送system bus提供传输效率,这个时间(10多ns)比从main memory的时间短的多(100多ns)。

     上图中architecture中的这两段话值得多读几遍,这是为什么write combine可以提高CPU写的原因所在。

    手上没有11th Gen Intel(R) Core(TM) i7-11700 @ 2.50GHz的资料,只有一个skylake的资料,看到从main memory读大概需要167*0.5(2GHz)需要83ns,考虑到server级别的CPU,而11th Gen Intel(R) Core(TM) i7-11700 @ 2.50GHz是消费级别的CPU,会被skylake弱一些,我们抓的数据是符合预期的。


http://lihuaxi.xjx100.cn/news/2196542.html

相关文章

力扣1574.删除最短的子数组使剩余数组有序

力扣1574.删除最短的子数组使剩余数组有序 剩下有序 –> 前面一段 后面一段 有序 前面有序 后面有序 前面最后一项 < 后面第一项先反向遍历找到right的最小值然后正向遍历找left的最大值当nums[left] > nums[right]时 right class Solution {public:int findLen…

HTML label 标签的作用和应用场景

label 标签 作用和语法 label 标签来定义表单控制间的关系&#xff0c;当用户点击该标签时&#xff0c;浏览器会自动将焦点转到和标签相关的表单控件上。 <label for"Name">Number:</label> <input type“text“ name"Name" id"Name…

[AI OpenAI] 提取GPT-4中的概念

总结&#xff1a; 研究人员采用新的可扩展方法&#xff0c;将GPT-4的内部表示分解为1600万个通常可解释的模式&#xff0c;这些模式被称为“特征”&#xff0c;目的是提高语言模型的透明度和可解释性。通过使用稀疏自编码器&#xff0c;研究人员能够识别与特定概念相关的特征&…

《精通ChatGPT:从入门到大师的Prompt指南》第1章:认识ChatGPT

第1章&#xff1a;认识ChatGPT 1.1 ChatGPT是什么 ChatGPT&#xff0c;全称为Chat Generative Pre-trained Transformer&#xff0c;是由OpenAI开发的一种先进的自然语言处理模型。它利用了深度学习中的一种技术——Transformer架构&#xff0c;来生成类人文本。ChatGPT通过对…

Android 车载 Audio 中 有关系统按键无声的问题排查小结

本文简单记录一下&#xff0c;车载中系统按键音的问题排查从 App --> FrameWork --> HAL层 的问题排查。 通过日志分析&#xff1a; AudioStreamOutSink 这个有数据写入到 HAL 中&#xff08;方式一&#xff09; 查看 dump 文件。&#xff08;方式二&#xff09; 先 …

PostgreSQL中有没有类似Oracle的dba_objects系统视图

PostgreSQL中有没有类似Oracle的dba_objects系统视图 在PostgreSQL中&#xff0c;没有一个完全集成了所有对象信息的视图&#xff08;类似于Oracle中的DBA_OBJECTS&#xff09;。但是&#xff0c;PostgreSQL提供了一些系统目录表和视图&#xff0c;可以用来获取数据库对象的信…

鸿蒙轻内核M核源码分析系列十九 Musl LibC

LiteOS-M内核LibC实现有2种&#xff0c;可以根据需求进行二选一&#xff0c;分别是musl libC和newlibc。本文先学习下Musl LibC的实现代码。文中所涉及的源码&#xff0c;均可以在开源站点 https://gitee.com/openharmony/kernel_liteos_m 获取。LiteOS-M内核提供了和内核相关的…

Solon2分布式事件总线的应用价值探讨

随着现代软件系统的复杂性日益增加&#xff0c;微服务架构逐渐成为开发大型应用的主流选择。在这种架构下&#xff0c;服务之间的通信和协同变得至关重要。Solon2作为一个高性能的Java微服务框架&#xff0c;其分布式事件总线&#xff08;Distributed Event Bus&#xff09;为微…