DPDK基础入门(二):Cache与大页优化

news2024/11/13 19:40:39

Cache简介

目前Cache主要由三级组成: L1 Cache, L2 Cache和Last Level Cache(LLC)。 L1最快,但容量小,可能只有几十KB。LLC慢,但容量大,可能多达几十MB。

L1和L2 Cache一般集成在CPU内部。另外,,L1和L2 Cache是每个处理器核心独有的 ,而LLC是被所有核心所共享的。

Intel处理器对各级Cache的访问时间一直都保持稳定, 见下表所示

在这里插入图片描述

除以上Cache外,现代CPU中还有一个TLB (Translation Look-aside Buffer) Cache,专门用于缓存内存中的页表项。TLB Cache使用虚拟地址进行搜索,直接返回对应的物理地址,相对于内存中的多级页表需要多次访问才能得到最终物理地址。

Cache地址映射与变换

内存容量很大, 一般是GB级,而Cache最大才几十MB。 要把内存数据放到Cache中,需要一个分块机制和映射算法。

Cache和内存以块为单位进行数据交换,块的大小通常以在内存的一个存储周期内能够访问到的数据长度为限,当前主流块的大小为64字节,这也就是Cache line的含义。

而映射算法分为全关联型,直接关联型和组关联型3种。

  • 在全关联型映射中,数据块可以存储在缓存的任何位置,而不受特定的限制。
  • 直接关联型映射将每个主存块映射到缓存中唯一确定的位置。
  • 组关联型映射结合了全关联型和直接关联型映射的优点。缓存被分为多个组(sets),每个组中有多个行(ways)。主存块可以映射到某一组中的任何行,但仅限于这一组内的行。

目前广泛使用组关联型Cache。

Cache的写策略

内存的数据被加载到Cache后,在某个时刻要被写回内存,写回策略有以下几种:

  • 直写(write-through) :处理器写入Cache的同时, 将数据写入内存中
  • 回写(write-back):为cache line设置dirty标志,当处理器改写了某个cache line后,不立即将其写回内存,而是将dirty标志置1。当处理器再次修改该cache line并且写回cache中, 查表发现dirty=1,则先将cache line内容写回内存,再将新数据写到cache。
  • WC(write-combining): 当cache line的数据被批量修改后,一次性将其写到内存。
  • UC(uncacheable) :针对内存不能被缓存到cache的场景,比如硬件需要立即收到指令。

Cache预取

预取原理

cache之所以能够提高系统性能,主要原因是程序运行存在局部性现象,包括时间局部性和空间局部性。这两种情况下处理器会把之后要用到的指令/数据读取到cache中,提高程序性能。而所谓的cache预取,就是预测哪些指令/数据将会被用到,然后采用合理方法将其预先取入到cache中。

一些处理器提供的软件预取指令(只对数据有效):

  • PREFETCH0 将数据存放在所有cache
  • PREFETCH1 将数据存放在L1 Cache之外的cache
  • PREFETCH2 将数据存放在L1,L2 Cache之外的cache
  • PREFETCHNTA 与PREFETCH0类似,但数据是以非临时数据存储,在使用完一次后,cache认为该数据是可以被淘汰出去的。

这些指令都是汇编指令, 一些程序库会提供对应的C语言版本, 如mmintrin.h中的_mm_prefetch()函数:

// p: 要预取的内存地址
// i: 预取指令类型, 与汇编指令对应关系如下
//    _MM_HINT_T0:  PREFETCH0
//    _MM_HINT_T1:  PREFETCH1
//    _MM_HINT_T2:  PREFETCH2
//    _MM_HINT_NTA: PREFETCHNTA
void _mm_prefetch(char* p, int i);
  • p 是要预取的内存地址。
  • i 是预取指令类型,可以是 _MM_HINT_T0、_MM_HINT_T1、_MM_HINT_T2 或 _MM_HINT_NTA 中的一个。

dpdk中的预取

dpdk转发一个报文所需要的基本过程分解:

  • 写接收描述符到内存,填充数据缓冲区指针,网卡收到报文后就会根据这个地址把报文内容填充进去。
  • 从内存中读取接收描述符(当收到报文时, 网卡会更新该结构)(内存读),从而确认是否收到报文。
  • 从接收描述符确认收到报文时,从内存中读取控制结构体的指针(内存读), 再从内存中读取控制结构体(内存读),把从接收描述符读取的信息填充到该控制结构体。
  • 更新接收队列寄存器,表示软件接收到了新的报文。
  • 内存中读取报文头部(内存读),决定转发端口。
    从控制结构体把报文信息填入到发送队列发送描述符,更新发送队列寄存器.
  • 从内存中读取发送描述符(内存读),检查是否有包被硬件传送出去。
  • 如果有的话,从内存中读取相应控制结构体(内存读),释放数据缓冲区。

处理一个报文的过程,需要6次读取内存(见上“内存读”)。而之前我们讨论过处理器从一级Cache读取数据需要3~5个时钟周期, 二级是十几个时钟周期,三级是几十个时钟周期,而内存则需要几百个时钟周期。从性能数据来说, 每80个时钟周期就要处理一个报文。

因此,dpdk必须保证所有需要读取的数据都在Cache中,否则一旦出现Cache不命中,性能将会严重下降。为了保证这点, dpdk采用了多种技术来进行优化, 预取只是其中的一种。

/*
 * Prefetch a cache line into all cache levels.
 */
#define rte_ixgbe_prefetch(p)   rte_prefetch0(p)

实际例子:

while (nb_rx < nb_pkts) {
    rxdp = &rx_ring[rx_id]; // 读取接收描述符
    staterr = rxdp->wb.upper.status_error;
    // 检查是否有报文收到
    if (!(staterr & rte_cpu_to_le_32(IXGBE_RXDADV_STAT_DD)))
        break;
    rxd = *rxdp;
    // 分配数据缓冲区
    nmb = rte_rxmbuf_alloc(rxq->mb_pool); nb_hold++;
    // 读取控制结构体
    rxe = &sw_ring[rx_id];
    ......
    rx_id++;
    if (rx_id == rxq->nb_rx_desc)
        rx_id = 0;
    // 预取下一个控制结构体mbuf
    rte_ixgbe_prefetch(sw_ring[rx_id].mbuf);
    // 预取接收描述符和控制结构体指针
    if ((rx_id & 0x3) == 0) {
        rte_ixgbe_prefetch(&rx_ring[rx_id]);
        rte_ixgbe_prefetch(&sw_ring[rx_id]);
    }
    ......
    // 预取报文
    rte_packet_prefetch((char *)rxm->buf_addr + rxm->data_off);
    // 把接收描述符读取的信息存储在控制结构体mbuf中
    rxm->nb_segs = 1;
    rxm->next = NULL;
    rxm->pkt_len = pkt_len;
    rxm->data_len = pkt_len;
    rxm->port = rxq->port_id;
    ......
    rx_pkts[nb_rx++] = rxm;
}

Cache一致性

cache一致性问题的根源,是因为存在多个处理器核心各自独占的cache(L1,L2),当多个核心访问内存中同一个cache行的内容时, 就会因为多个cache同时缓存了该内容引起同步的问题。

dpdk使用Cache Line对齐,同时避免多个核心访问同一个内存地址或者数据结构来解决cache一致性问题。

dpdk实现Cache Line对齐

实现很简单,定义该数据结构或者数据缓冲区时就申明对齐,DPDK对很多结构体定义的时候就是如此操作的,以下是宏定义。

rte_common.c

/** Minimum Cache line size. */
#define RTE_CACHE_LINE_MIN_SIZE 64

/** Force alignment to cache line. */
#define __rte_cache_aligned __rte_aligned(RTE_CACHE_LINE_SIZE)

/** Force minimum cache line alignment. */
#define __rte_cache_min_aligned __rte_aligned(RTE_CACHE_LINE_MIN_SIZE)
struct __rte_cache_aligned lcore_params {
	uint16_t port_id;
	uint8_t queue_id;
	uint8_t lcore_id;
};

以上定义了一个简单的结构体 lcore_params,用于存储和管理逻辑核心相关的参数信息,包括端口 ID、队列 ID 和逻辑核心 ID。通过 __rte_cache_aligned 这个宏,确保了结构体在内存中按缓存行对齐。

MESI协议

解决Cache一致性问题的机制有著名的MESI协议。

MESI协议是Cache line四种状态的首字母的缩写,分别是修改(Modified)态、独占(Exclusive)态、共享(Shared)态和失效(Invalid)态。Cache中缓存的每个Cache Line都必须是这四种状态中的一种。

Modified(修改)态:当某个处理器或核心修改了一个缓存行中的数据时,该缓存行处于修改态。

  • 数据在此缓存中被修改过,并且未写回主存。
  • 此状态下的缓存行数据是最新的,并且与主存中的数据不一致(即缓存数据是脏的)。
  • 其他处理器或核心若要读取该缓存行,需要先将其写回到主存或者转换为共享态或失效态。

Exclusive(独占)态 :当某个处理器或核心拥有一个缓存行的唯一访问权限,且此缓存行与主存中的数据一致时,该缓存行处于独占态。

  • 缓存行中的数据与主存中的数据一致,且没有其他处理器或核心缓存了相同的数据。
  • 其他处理器或核心可以读取该缓存行,但必须先将其设置为共享态或者失效态,再读取或修改。

Shared(共享)态: 当多个处理器或核心缓存了同一个缓存行,并且数据与主存中的一致时,该缓存行处于共享态。

  • 多个处理器或核心可以同时缓存并访问该缓存行的数据。
  • 数据与主存中的数据一致,因此无需写回操作。

Invalid(失效)态: 当某个处理器或核心的缓存行无效或者失效时,处于失效态。

  • 处理器或核心的缓存行与主存中的数据不一致,或者缓存行中的数据已经过时。
  • 若有其他处理器或核心修改了相同的缓存行,可能会导致当前缓存行失效。
  • 在失效态的缓存行上的任何访问操作都将导致从主存重新获取最新数据。

dpdk实现缓存一致性

dpdk解决方法很简单,首先就是避免多个核访问同一个内存地址或者数据结构。这样,每个核尽量都避免与其他核共享数据,从而减少因为错误的数据共享(cache line false sharing)导致的Cache一致性的开销。

struct __rte_cache_aligned lcore_conf {
	uint16_t n_rx_queue;
	struct lcore_rx_queue rx_queue_list[ETHDEV_RX_QUEUE_PER_LCORE_MAX];
	struct rte_graph *graph;
	char name[RTE_GRAPH_NAMESIZE];
	rte_graph_t graph_id;
};
struct lcore_conf lcore_conf[RTE_MAX_LCORE];

TLB和大页

TLB和Cache本质上是一样的,都是一个缓存。TLB用于存储最近访问的虚拟地址到物理地址的映射。它存在于CPU中,目的是加速虚拟内存到物理内存的转换过程。

当程序访问内存时,CPU需要将虚拟地址转换为物理地址。TLB 通过缓存这些转换结果来减少每次地址转换所需的时间。

TLB工作原理

每当CPU执行内存访问操作时,它会首先查询TLB,以确定虚拟地址是否已经在缓存中。

如果地址在TLB中找到(称为“TLB命中”),CPU直接使用缓存中的物理地址,从而加速访问。

如果地址不在TLB中(称为“TLB未命中”),CPU需要查找页表(Page Table),然后将转换结果加载到TLB中,以备将来的访问使用。

TLB是非常小的,一般都是几十项到几百项不等,并且为了提高命中率,很多处理器还采用全相连方式。

使用大页的原因

TLB大小是很有限的,随着程序的变大或者程序使用内存的增加,那么势必会增加TLB的使用项,最后导致TLB出现不命中的情
况。

那么,在这种情况下,大页的优势就显现出来了。如果采用2MB作为分页的基本单位,那么只需要一个表项就可以保证不出现TLB不命中的情况;对于消耗内存以GB为单位的大型程序,可以采用1GB为单位作为分页的基本单位,减少TLB不命中的情况。

Linux操作系统采用了基于hugetlbfs的特殊文件系统来加入对2MB或者1GB的大页面支持。编译内核时可以激活。

DDIO

DDIO (Data Direct I/O)主要目的是优化数据包的处理性能,特别是在高吞吐量的网络环境中。通过将数据包直接发送到 CPU 的缓存中,DDIO 减少了数据包在 CPU 内部的处理延迟和开销。

DDIO 利用 CPU 的数据缓存来加速数据访问。数据包被直接写入到 CPU 的数据缓存(如 L2 或 L3 缓存),而不是首先写入主内存。这种方法可以显著减少内存访问延迟。

网卡读写对比

没有DDIO技术

当I/O设备(如网络接口卡、存储设备等)发起读写请求时,数据首先被传输到设备的缓冲区。设备通常通过DMA将数据直接传输到系统内存中的缓冲区。这个过程绕过了CPU,以减少CPU的负担和提高效率。

由于数据直接写入系统内存,可能会导致CPU缓存中存储的数据和系统内存中的数据不一致,需要通过缓存一致性协议来处理这些不一致。

完成数据传输后,I/O设备会生成中断信号通知CPU处理数据。CPU需要处理这些中断并从内存中读取数据,进一步将数据处理到应用程序中。

有DDIO技术

DDIO技术允许I/O设备(如网络卡)将数据直接写入CPU的L3缓存(通常是更大的共享缓存)而不是系统内存。这意味着I/O设备可以绕过传统的内存路径,将数据直接写入CPU的缓存层次结构中。

DDIO技术通过将数据直接写入CPU缓存,减少了CPU和系统内存之间的缓存一致性问题。由于数据首先进入L3缓存,CPU可以在缓存中处理数据,减少了对系统内存的读取和写入操作。

CPU处理这些数据时不需要从系统内存中读取数据,能够减少中断处理的延迟。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2101752.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【2024】Datawhale X 李宏毅苹果书 AI夏令营 Task3

本文是关于李宏毅苹果书”第2章 实践方法论“学习内容的记录。 模型在测试集上表现不佳&#xff0c;可能是因为模型没有充分学习训练集。模型不能充分学习训练集的原因&#xff1a; 模型偏差优化问题过拟合不匹配 一、模型偏差 模型偏差是指&#xff1a;由于模型过于简单&a…

网站如何针对不同的DDOS进行防御?

建设网站租用服务器是多数企业及个人的选择&#xff0c;一个安全稳定的服务器对网站的重要性无需再赘述。要保证服务器租用的安全和稳定&#xff0c;除了需要服务器自身有强大的硬、软件基础之外&#xff0c;还需要防范外部的一些因素&#xff0c;常见的就是各种网络攻击&#…

Linux 上如何做MySQL数据备份

目录 SQL备份脚本创建crontabcrontab命令总结查看特定目录中的周期性任务 crontab&#xff08;cron table 的缩写&#xff09;是 Unix/Linux 系统上用于设置周期性被执行的任务的工具。它允许用户定义需要在特定时间&#xff08;比如每天凌晨、每周的某个时间等&#xff09;自动…

驭势科技研究成果入选学术顶会IROS 2024

近日&#xff0c;驭势科技团队关于自动驾驶车辆定位算法的最新研究成果《LiDAR-based HD Map Localization using Semantic Generalized ICP with Road Marking Detection》&#xff0c;创造性地解决了基于LiDAR的实时路标检测和高精地图配准所带来的挑战&#xff0c;成功入选国…

汇川技术|KingIOServer与AC810PLC通过ModbusTCP通讯测试

哈喽&#xff0c;你好啊&#xff0c;我是雷工&#xff01; 最近有个项目用亚控的KingSCADA软件开发SCADA系统&#xff0c;需要和汇川的AC810PLC进行通讯&#xff1b; 本节测试亚控的采集软件KingIOServer与汇川的AC810PLC的通讯测试。 以下为测试笔记。 01 效果演示 测试过程…

3个苹果锁屏密码解锁方法,帮你快速解决密码忘记的烦恼!

苹果手机锁屏密码忘记了是一件很常见的问题&#xff0c;但也是一件让人头疼的事情。如果你遇到了这样的问题&#xff0c;不要着急&#xff0c;因为有很多方法可以帮助你解锁iPhone。下面我们将介绍四种简单的方法来解锁iPhone。 一、使用密码解锁工具 iphone忘记了密码怎么解锁…

iconfont图标字体库详细介绍

概述 图标库在前端开发中应用十分广泛&#xff0c;图标库不仅会丰富美化界面的展示&#xff0c;语义化的图标库更能简洁明了地向用户传达某些信息&#xff0c;比如功能的特性和作用&#xff0c;引导用户&#xff0c;极大提高系统的易用性。在没有 UI 设计师的情况下&#xff0…

【C++】手动实现队列的封装(C++)

目录 源代码&#xff1a; 输出结果如下&#xff1a; 实现以下封装 源代码&#xff1a; #include <iostream>using namespace std;class Queue { private:int* arr; // 队列的动态数组int front; // 队列头部元素的索引int rear; // 队列尾部元素的索引in…

新版某数字壳脱壳,过frida检测,及重打包

目录 脱壳 寻找特征& frida hook 过frida检测 修复dex 重打包 修改smail 去签名校验 正文 大家好&#xff0c;我是小生&#xff0c;这次的app是一个国内某计划app, 功能相当全&#xff0c;界面也很美观&#xff0c;很实用&#xff0c;这个app我很欣赏。总共花了有…

【SQL】Delete使用

目录 语法 需求 示例 分析 代码 语法 DELETE删除表中所需内容 删除表中满足特点条件的行&#xff1a;DELETE FROM 表名 WHERE 条件; 删除表中所有行&#xff1a;DELETE FROM 表名; WHERE子句 WHERE子句用于指定从表中选取记录的条件。允许筛选数据&#xff0c;只返回满足…

【文献精读】基于驱动力表的无人车终端无约束预测纵向控制(TVT)

写在前面&#xff1a; &#x1f31f; 欢迎光临 清流君 的博客小天地&#xff0c;这里是我分享技术与心得的温馨角落。&#x1f4dd; 个人主页&#xff1a;清流君_CSDN博客&#xff0c;期待与您一同探索 移动机器人 领域的无限可能。 &#x1f50d; 本文系 清流君 原创之作&…

ElasticSearch学习笔记(六)自动补全、拼音分词器、RabbitMQ实现数据同步

文章目录 前言11 自动补全11.1 拼音分词器11.2 自定义分词器11.3 自动补全查询 12 数据同步12.1 实现方案12.1.1 同步调用12.1.2 异步通知12.1.3 监听binlog 12.2 异步通知实现数据同步12.2.1 声明交换机和队列12.2.2 发送MQ消息12.2.3 接收MQ消息并操作ES 前言 ElasticSearch…

数据结构————单向链表

头插&#xff1a; 尾插&#xff1a; 头删&#xff1a; 尾删&#xff1a;

一种常用嵌入式开发代码库

链接&#xff1a;https://gitee.com/zhangxinyuanqi/varch 使用开源协议&#xff1a;GPL-2.0 varch简介 varch&#xff08;we-architecture&#xff0c;意为我们的框架库&#xff09;是嵌入式C语言常用代码模块库&#xff0c;包含了嵌入式中常用的算法库, 数据结构&#xff…

JPA关联MyBatis

3.1 JPA 多表查询 多表查询在 Spring Data JPA 中有两种实现方式&#xff0c;第一种是创建一个结果集的接口来接受多表连接查询后的结果&#xff0c;第二种是利用 JPA 的关联映射来实现 3.1.1 数据库表及关系 CRM 数据库中除 sys_user(用户)表外&#xff0c;还包括sys_role(角…

触想内嵌式工业一体机应用于智能检票机改善旅游体验

一、行业发展背景 每年下半年&#xff0c;暑假、中秋、国庆总是接踵而至&#xff0c;随之而来的出游高峰一波接一波。凶猛需求之下&#xff0c;各地景区、游乐园客流压力加大&#xff0c;特别在检票环节&#xff0c;人工检票效率低、秩序混乱&#xff0c;导致常常出现检票口人山…

POL(Point-of-Load)负载点电源

负载点&#xff08;POL&#xff09;电源在靠近负载处单独放置电源调节器(线性稳压器或DC-DC)&#xff0c;解决了高性能半导体器件&#xff0c;例如&#xff1a;微控制器、ASIC等&#xff0c;所面临的高峰值电流、低噪声裕量等设计挑战。 一般我们会把负载点电源尽量靠近负载放…

乾元通多卡聚合技术在无人配送车应用领域通信保障方案

在无人驾驶公交车、安防车、售卖车、清扫车相继亮相后&#xff0c;无人配送车在全国各地也陆续“上岗”&#xff0c;为我们的城市带来了与众不同的“智慧体验”&#xff0c;让城市有了“科技温度”。 无人配送车在营业部装载好快递后&#xff0c;会按照提前规划好的路线出发&a…

sqli-labs靶场通关攻略 61-65

主页有sqli-labs靶场通关攻略 1-60 第六一关 less-61 步骤一&#xff1a;闭合方式&#xff1a;?id1)) -- 步骤二&#xff1a;查询数据库 ?id1)) and updatexml(1,concat(1,database()),1) -- 步骤三&#xff1a;查出网站的数据库表名 ?id1)) and updatexml(1,concat(0x7e…

15 用户管理

如果我们只能使用root用户&#xff0c;这样存在安全隐患。这时&#xff0c;就需要使用mysql的用户管理 张三只能操纵mytest这个库&#xff0c;李四只能操纵msg这个库&#xff0c;如果给他们root账户&#xff0c;就可以操纵所有库&#xff0c;风险太大 用户 用户信息 用户都存…