精华内容
下载资源
问答
  • 由于 CPU 和主存在速度上的存在着巨大差 异, 现代计算机都在 CPU 和主存之间设置一个高速、 小容量的缓冲存储器 cache。 Cache 最重要的技术指标是它的命中率。 本文简单讨论了影响 Cache 命中率的几 个因素。 ...

    简述影响 Cache 命中率的因素

    摘要: 存储器是计算机的核心部件之一。由于 CPU 和主存在速度上的存在着巨大差 异 现代计算机都在 CPU 和主存之间设置一个高速、 小容量的缓冲存储器 cache。 Cache 最重要的技术指标是它的命中率。

    本文简单讨论了影响 Cache 命中率的几 个因素。

    关键字:cache 容量块大小替换算法映射方式。

    一、引言 在计算机技术发展过程中,主存储器存取速度一直比中央处理器操作速度慢 得多 使中央处理器的高速处理能力不能充分发挥整个计算机系统的工作效率 受到影响。有很多方法可用来缓和中央处理器和主存储器之间速度不匹配的矛 盾如采用多个通用寄存器、多存储体交叉存取等在存储层次上采用高速缓冲 存储器也是常用的方法之一。很多大、中型计算机以及新近的一些小型机、微型 机也都采用高速缓冲存储器。 高速缓冲存储器的容量一般只有主存储器的几百分之一但它的存取速度能 与中央处理器相匹配。 根据程序局部性原理正在使用的主存储器某一单元邻近 的那些单元将被用到的可能性很大。因而当中央处理器存取主存储器某一单元 时 计算机硬件就自动地将包括该单元在内的那一组单元内容调入高速缓冲存储 器 中央处理器即将存取的主存储器单元很可能就在刚刚调入到高速缓冲存储器 的那一组单元内。于是中央处理器就可以直接对高速缓冲存储器进行存取。在 整个处理过程中 如果中央处理器绝大多数存取主存储器的操作能为存取高速缓 冲存储器所代替 计算机系统处理速度就能显著提高。 高速缓冲存储器最重要的 技术指标是它的命中率。

    二、简述影响 cache 命中率的因素

    1、Cache 容量对命中率的影响 Cache 的命中率随它的容量的增加而提高它们之间的关系曲线如图所示。在 Cache 容 量比较小的时候命中率提高得非常快但根据边际效应递减原理随着 Cache 容量的增加 命中率提高的速度逐渐降低。当 Cache 的容量增加到无穷大时命中率可望达到 100%但 是这在实际是做不到的。 在一般情况下图中的关系曲线可以近似地表示为 H=1-S-0.5。因此当 Cache 的容量 达到一定值之后再增加 Cache 容量命中率的提高很少。 Cache 命中率 H 与容量 S 的关系

    2、Cache 块大小对命中率的影响 当 Cache 的容量一定时 在采用组相联映象和变换方式的 Cache 中 块的大小对命中率 的影响非常敏感。我们可以从下面的 Cache 映象逻辑表达式分析: 设 Cache 的总量为 S组数为 M组内块数为 N块大小为 A。所以有: S = M*N*A 由上式可知在 Cache 的容量 S 和 Cache 组内块数 N(也可换成是组数 M)固定不变时 Cache 块大小与组数成反比。 因此当 Cache 的块容量很小组的数目就多主存中的某一块可以映象到 Cache 中的 块数就少所以此时Cache 的命中率低。 随着块大小的增加 由于程序的空间局部性起主要作用 同一块中数据的利用率比较高。 因此Cache 的命中率开始升高。但如果块变得过大的话会减少装入 Cache 的总行数 而且也会使得离所访问的位置较远的块被再次使用的概率变小。因此这种增加趋势在某 一个“最佳块大小”处使 Cache 命中率达到最大值。在这一点以后命中率随着块大小的 增加反而减小。因为实际上当块的容量变得比较大时(此时还是在组相联映象中并没有 蜕变成全相联映象)进入 Cache 中的许多数据可能根本用不上。而且随着块大小的增 加程序时间局部性的作用就会逐渐减弱。最后当块大小等于整个 Cache 的容量时(此 时主存块应该是按成组的方式被调入 Cache)命中率将趋近于零。 所以根据 Cache 块的容量由小到大的变化

         (1) 对于给定的 Cache 容量当块大小增加时命中率开始时处于上升趋势后来反而 会下降。

         (2) Cache 容量越大会使命中率达到最高的拐点的块大小增大。 在这里 导致命中率先上升后下降的原因在于增加块大小产生双重作用。 一方面会减少 强制性失效因为程序局部性原理增加块大小增加了利用空间局部性的机会;另一方面 在容量一定情况下 增加块大小会减少总的块数目 会增加冲突失效 在 Cache 容量较小时 还可能增加容量失效。刚开始增加块大小时由于块大小还不是很大上述第一种作用超过 第二种作用使命中率上升。当块大小增加到一定程度时第二种作用会超过第一种作用 使命中率下降。 综上所述块的大小对 Cache 命中率的影响是显而易见的;而 Cache 命中率对整个 Cache 存储系统的存取效率的影响又是至关重要的。所以对于系统体系结构的设计人员 甚至是软件开发人员来说关于块大小的最佳选择都是值得研究的。

    3、Cache 中与主存映射方式对命中率的影响

         3-1.全相联方式 地址映象规则:主存的任意一块可以映象到 Cache 中的任意一块 (1) 主存与缓存分成相同大小的数据块。 (2) 主存的某一数据块可以装入缓存的任意一块空间中。 全相联方式的对应关系如图所示。如果 Cache 的块数为 Cb主存的块数为 Mb则映象关系 共有 Cb×Mb 种。 应用全相联的方式命中率比较高Cache 存储空间利用率高。但是访问相关存储器时每次 都要与全部内容比较速度低成本高因而应用少。

         3-2.直接相联方式 地址映象规则: 主存储器中一块只能映象到 Cache 的一个特定的块中。 (1) 主存与缓存分成相同大小的数据块。 (2) 主存容量应是缓存容量的整数倍将主存空间按缓存的容量分成区主存中每 一区的块数与缓存的总块数相等。 (3) 主存中某区的一块存入缓存时只能存入缓存中块号相同的位置。 图示出了直接相联映象规则。 可见主存中各区内相同块号的数据块都可以分别调入 缓存中块号相同的地址中但同时只能有一个区的块存入缓存。由于主、缓存块号相同因 此目录登记时只记录调入块的区号即可。 应用直接相联的方式地址映象方式简单数据访问时只需检查区号是否相等即可因 而可以得到比较快的访问速度硬件设备简单。 但是使得替换操作频繁命中率比较低。

          3-3.组相联映象方式 组相联的映象规则: (1) 主存和 Cache 按同样大小划分成块。 (2) 主存和 Cache 按同样大小划分成组。 (3) 主存容量是缓存容量的整数倍将主存空间按缓冲区的大小分成区主存 中每一区的组数与缓存的组数相同。 (4) 当主存的数据调入缓存时主存与缓存的组号应相等也就是各区中的某 一块只能存入缓存的同组号的空间内但组内各块地址之间则可以任意存放 即 从主存的组到 Cache 的组之间采用直接映象方式; 在两个对应的组内部采用全相联 映象方式。 应用组相联的方式块的冲突概率比较低块的利用率大幅度提高块失效率明显降低。 但是实现难度和造价要比直接映象方式高。

    4、Cache 的替换算法对命中率的影响 当新的主存块需要调入 Cache 并且它的可用空间位置又被占满时需要替换掉 Cache 的数据这就产生了替换策略(算法)问题。根据程序局部性规律可知:程序在运行中总 是频繁地使用那些最近被使用过的指令和数据。这就提供了替换策略的理论依据。 替换算法目标就是使 Cache 获得最高的命中率。Cache 替换算法是影响代理缓存系统性 能的一个重要因素一个好的 Cache 替换算法可以产生较高的命中率。

    常用算法如下:

    (1)随机法(RAND 法) 随机替换算法就是用随机数发生器产生一个要替换的块号 将该块替换出去 此算法简 单、易于实现而且它不考虑 Cache 块过去、现在及将来的使用情况但是没有利用上层存 储器使用的“历史信息”、没有根据访存的局部性原理故不能提高 Cache 的命中率命中 率较低。

    (2)先进先出法(FIFO 法) 先进先出(First-In-First-OutFIFO)算法。就是将最先进入 Cache 的信息块替换出 去。FIFO 算法按调入 Cache 的先后决定淘汰的顺序选择最早调入 Cache 的字块进行替换 它不需要记录各字块的使用情况比较容易实现系统开销小其缺点是可能会把一些需要 经常使用的程序块(如循环程序)也作为最早进入 Cache 的块替换掉而且没有根据访存的 局部性原理故不能提高 Cache 的命中率。因为最早调入的信息可能以后还要用到或者经 常要用到如循环程序。此法简单、方便利用了主存的“历史信息” 但并不能说最先 进入的就不经常使用其缺点是不能正确反映程序局部性原理命中率不高可能出现一种 异常现象。

    (3)近期最少使用法(LRU 法) 近期最少使用 (Least Recently Used LRU) 算法。 这种方法是将近期最少使用的 Cache 中的信息块替换出去。 该算法较先进先出算法要好一些。 但此法也不能保证过去不常用将来 也不常用。 LRU 法是依据各块使用的情况总是选择那个最近最少使用的块被替换。这种方法虽然 比较好地反映了程序局部性规律但是这种替换方法需要随时记录 Cache 中各块的使用情 况以便确定哪个块是近期最少使用的块。LRU 算法相对合理但实现起来比较复杂系统 开销较大。 通常需要对每一块设置一个称为计数器的硬件或软件模块用以记录其被使用的 情况。

    结论: 在 Cache 容量较小的情况下随机策略相对较好而随着 Cache 容量的增加最近最少 使用(LRU)和先进先出(FIFO)策略的效果较好。所以一般的计算机默认的采用 LRU 替换策 略。 由于主存中的块比 Cache 中的要多 所以当要从主存调入一个块到 Cache 中时 会出现 该快所映像到的一组 Cache 块已被占用的情况。这是需要强制其中的一块移出 Cache以接 纳新的 Cache 块。这就需要替换策略选择替换的块。 替换策略主要是在对 Cache 块进行淘汰时 如何选择要替换的块的策略。 目前主要又三 种替换策略:最近最少使用(LRU)策略、先进先出(FIFO)策略、随机(RANDOM)策略。这三种 替换策略各有优劣。 好的替换策略会将使用率高的 Cache 块更长时间的驻留在 Cache 中 从 而降低 Cache 失效率提高 Cache 性能。

    展开全文
  • 利用仿真得到不同条件下影响缓冲容量大小的因素,得出在无数据丢失的前提下,缓冲区大小与数据输入流和输出流两者差值的关系:在其他条件不变的情况下,输入流速率与输出流速率的差值越大,所需要的环形缓冲区容量就越...
  • 【高并发】18 缓冲

    2020-07-05 00:02:42
    一、介绍 1、为什么需要缓冲? 缓解数据库压力、提高用户体验。 浏览器APP网路转发linux服务器...缓冲容量和基础设施 4、缓冲分类和应用场景 本地缓冲: 编程实现(成员变量、局部变量、静态变量)、Guava Cache

    一、介绍

    1、为什么需要缓冲?

    1. 缓解数据库压力、提高用户体验。
    浏览器APP
    网路转发linux
    服务器集群
    数据库 存储

    2、缓冲特征

    1. 命中率: 命中数/(命中数 + 没有命中数)
    1. 最大元素(空间)
    1. 清空策略:FIFO 、LFU 、LRU 、过期时间、随机等

    3、缓冲命中率影响因素

    1. 业务场景和业务需求
    1. 缓冲的设计(粒度 和 策略)
    1. 缓冲容量和基础设施

    4、缓冲分类和应用场景

    1. 本地缓冲: 编程实现(成员变量、局部变量、静态变量)、Guava Cache
    1. 分布式缓冲:Memcache 、Redis

    5、并发下常见缓冲问题

    1. 缓冲一致性
    更新数据成功
    更新缓冲失败
    数据不一致
    更新缓冲成功
    更新数据库失败
    更新数据成功
    淘汰缓冲失败
    淘汰缓冲成功
    更新缓冲失败
    查询缓冲miss
    1. 缓冲并发问题
    Mon 06Mon 13Mon 20Mon 27Mon 03T1T2T3T3获取缓冲 lock查询数据库重建缓冲输出 unlick
    1. 缓冲穿透问题

    a: null 对象缓冲 、缓冲对象时效性、单独过滤处理

    1. 缓冲的雪崩现象

    Hash 一致性解决

    req
    res
    get
    res
    res
    client
    Cache
    存储
    x
    x
    x
    x
    res
    client
    Cache
    存储
    展开全文
  • 当然,每次调用成本与缓冲当前大小成线性关系,但我在这里简化并计算它们:expandCapacity()数量(时间)默认配置(16个字符容量)>在60%情况下,StringBuilder将扩展0次>在39%情况下,Stri...

    这里有两个因素:时间和内存消耗.时间主要受调用java.lang.AbstractStringBuilder.expandCapacity()的次数的影响.当然,每次调用的成本与缓冲区的当前大小成线性关系,但我在这里简化并计算它们:

    expandCapacity()的数量(时间)

    默认配置(16个字符容量)

    >在60%的情况下,StringBuilder将扩展0次

    >在39%的情况下,StringBuilder将扩展8次

    >在1%的情况下,StringBuilder将扩展11次

    预期的expandCapacity数量为3,23.

    初始容量为4096个字符

    >在99%的情况下,StringBuilder将扩展0次

    >在1%的情况下,StringBuilder将扩展3次

    预期的expandCapacity数为0,03.

    正如您所看到的,第二种情况似乎要快得多,因为它很少需要扩展StringBuilder(每100个输入三次).但请注意,第一次扩展不太重要(复制少量内存);此外,如果您以巨大的块为构建器添加字符串,它将在更少的迭代中更加热切地扩展.

    另一方面,内存消耗增长:

    内存消耗

    默认配置(16个字符容量)

    >在60%的情况下,StringBuilder将占用16个字符

    >在39%的情况下,StringBuilder将占用4K个字符

    >在1%的情况下,StringBuilder将占用32K字符

    预期的平均内存消耗为:1935个字符.

    初始容量为4096个字符

    >在99%的情况下,StringBuilder将占用32K字符

    预期的平均内存消耗为:4383个字符.

    TL; DR

    这让我相信将初始缓冲区扩大到4K会使内存消耗增加两倍以上,同时将程序加速两个数量级.

    底线是:试试!编写一个能够处理具有不同初始容量的不同长度的百万字符串的基准并不难.但我相信更大的缓冲区可能是一个不错的选择.

    展开全文
  • 影响内存访问速度的因素主要有: 1.内存带宽:每秒读写内存的数据量,由硬件配置决定。 2.CACHE高速缓冲:CPU与内存之间的缓冲器,当命中率比较高时能大大提供内存平均访问速度。 3.TLB转换旁视缓冲:系统虚拟地址向...

    影响内存访问速度的因素主要有:
    1.内存带宽:每秒读写内存的数据量,由硬件配置决定。
    2.CACHE高速缓冲:CPU与内存之间的缓冲器,当命中率比较高时能大大提供内存平均访问速度。
    3.TLB转换旁视缓冲:系统虚拟地址向物理地址转换的高速查表机制,转换速度比普通转换机制要快。

    我们能够优化的只有第2点和第3点。由于CACHE的小容量与SMP的同步竞争,如何最大限度的利用高速缓冲就是我们的明确优化突破口(以常用的数据结构体为例):
    1.压缩结构体大小:针对CACHE的小容量。
    2.对结构体进行对齐:针对内存地址读写特性与SMP上CACHE的同步竞争。
    3.申请地址连续的内存空间:针对TLB的小容量和CACHE命中。
    4.其它优化:综合考虑多种因素

    具体优化方法
    1.压缩结构体大小
    系统CACHE是有限的,并且容量很小,充分压缩结构体大小,使得CACHE能缓存更多的被访问数据,无非是提高内存平均访问速度的有效方法之一。
    压缩结构体大小除了需要我们对应用逻辑做好更合理的设计,尽量去除不必要的字段,还有一些额外针对结构体本身的压缩方法。

    1.1.对结构体字段进行合理的排列
    由于结构体自身对齐的特性,具有同样字段的结构体,不同的字段排列顺序会产生不同大小的结构体。
    大小:12字节

    1

    2

    3

    4

    5

    6

    7

    struct box_a

    {

        char a;

        short b;

        int c; 

        char d;

    }; 

    大小:8字节

    1

    2

    3

    4

    5

    6

    7

    struct box_b

    {

        char a;

        char d;

        short b;

        int c; 

    };

    1.2.利用位域
    实际中,有些结构体字段并不需要那么大的存储空间,比如表示真假标记的flag字段只取两个值之一,0或1,此时用1个bit位即可,如果使用int类型的单一字段就大大的浪费了空间。
    示例:tcp.h

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    33

    34

    struct tcphdr {

        __be16  source;

        __be16  dest;

        __be32  seq;

        __be32  ack_seq;

    #if defined(__LITTLE_ENDIAN_BITFIELD)

        __u16   res1:4,

            doff:4,

            fin:1,

            syn:1,

            rst:1,

            psh:1,

            ack:1,

            urg:1,

            ece:1,

            cwr:1;

    #elif defined(__BIG_ENDIAN_BITFIELD)

        __u16   doff:4,

            res1:4,

            cwr:1,

            ece:1,

            urg:1,

            ack:1,

            psh:1,

            rst:1,

            syn:1,

            fin:1;

    #else

    #error  "Adjust your <asm/byteorder.h> defines"

    #endif 

        __be16  window;

        __sum16 check;

        __be16  urg_ptr;

    };

    1.3.利用union
    union结构体也是压缩结构体大小的方法之一,它允许我们在某些情况下能对结构体的多个字段进行合并或把小字节字段存放到大字节字段内。
    示例:skbuff.h

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    struct sk_buff {

        

        union {

            __wsum      csum;

            struct {

                __u16   csum_start;

                __u16   csum_offset;

            };

        };

        

    };

    2.对结构体进行对齐
    对结构体进行对齐有两层意思,一是指对较小结构体进行机器字对齐,二是指对较大结构体进行CACHE LINE对齐。

    2.1.对较小结构体进行机器字对齐
    我们知道,对于现代计算机硬件来说,内存只能通过特定的对齐地址(比如按照机器字)进行访问。举个例子来说,比如在64位的机器上,不管我们是要读取第0个字节还是要读取第1个字节,在硬件上传输的信号都是一样的。因为它都会把地址0到地址7,这8个字节全部读到CPU,只是当我们是需要读取第0个字节时,丢掉后面7个字节,当我们是需要读取第1个字节,丢掉第1个和后面6个字节。
    当我们要读取的字节刚好落在两个机器字内时,就出现两次访问内存的情况,同时通过一些逻辑计算才能得到最终的结果。
    因此,为了更好的提升性能,我们须尽量将结构体做到机器字(或倍数)对齐,而结构体中一些频繁访问的字段也尽量安排在机器字对齐的位置。
    大小:12字节

    1

    2

    3

    4

    5

    6

    7

    8

    struct box_c

    {

        char a;

        char d;

        short b;

        int c; 

        int e; 

    };

    大小:16字节

    1

    2

    3

    4

    5

    6

    7

    8

    9

    struct box_d

    {

        char a;

        char d;

        short b;

        int c; 

        int e; 

        char padding[4];

    };

    上面表格右边的box_d结构体,通过增加一个填充字段padding将结构体大小增加到16字节,从而与机器字倍数对齐,这在我们申请连续的box_d结构体数组时,仍能保证数组内的每一个结构体都与机器字倍数对齐。
    通过填充字段padding使得结构体大小与机器字倍数对齐是一种常见的做法,在Linux内核源码里随处可见。

    2.2.对较大结构体进行CACHE LINE对齐
    我们知道,CACHE与内存交换的最小单位为CACHE LINE,一个CACHE LINE大小以64字节为例。当我们的结构体大小没有与64字节对齐时,一个结构体可能就要占用比原本需要更多的CACHE LINE。比如,把一个内存中没有64字节长的结构体缓存到CACHE时,即使该结构体本身长度或许没有还没有64字节,但由于其前后搭占在两条CACHE LINE上,那么对其进行淘汰时就会淘汰出去两条CACHE LINE。
    这还不是最严重的问题,非CACHE LINE对齐结构体在SMP机器上容易引发名为错误共享的CACHE问题。比如,结构体T1和T2都没做CACHE LINE对齐,如果它们(T1后半部和T2前半部)在SMP机器上合占了同一条CACHE,如果CPU 0对结构体T1后半部做了修改则将导致CPU 1的CACHE LINE 1失效,同样,如果CPU 1对结构体T2前半部做了修改则也将导致CPU 0的CACHE LINE 1失效。如果CPU 0和CPU 1反复做相应的修改则导致的不良结果显而易见。本来逻辑上没有共享的结构体T1和T2,实际上却共享了CACHE LINE 1,这就是所谓的错误共享。
    Linux源码里提供了利用GCC的__attribute__扩展属性定义的宏来做这种对齐处理,在文件/linux-2.6.xx/include/linux/cache.h内可以找到多个相类似的宏,比如:

    1

    #define ____cacheline_aligned __attribute__((__aligned__(SMP_CACHE_BYTES)))

    该宏可以用来修饰结构体字段,作用是强制该字段地址与CACHE LINE映射起始地址对齐。
    看/linux-2.6.xx/drivers/net/e100.c内结构体nic的实现,三个____cacheline_aligned修饰字段,表示强制这些字段与CACHE LINE映射起始地址对齐。

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    struct nic {

        /* Begin: frequently used values: keep adjacent for cache effect */

        u32 msg_enable              ____cacheline_aligned;

        /* 4字节空洞 */

        struct net_device *netdev;

        struct pci_dev *pdev;

        /* 40字节空洞 */

        struct rx *rxs              ____cacheline_aligned;

        struct rx *rx_to_use;

        struct rx *rx_to_clean;

        struct rfd blank_rfd;

        enum ru_state ru_running;

        /* 20字节空洞 */

        spinlock_t cb_lock          ____cacheline_aligned;

        spinlock_t cmd_lock;

        struct csr __iomem *csr;

        enum scb_cmd_lo cuc_cmd;

        unsigned int cbs_avail;

        struct napi_struct napi;

        

    }

    回到前面的问题,如果我们对结构体T2的第一个字段加上____cacheline_aligned修饰,则该错误共享即可解决。

    2.3.只读字段和读写字段隔离对齐
    只读字段和读写字段隔离对齐的目的就是为了尽量保证那些只读字段和读写字段分别集中在CACHE的不同CACHE LINE中。由于只读字段几乎不需要进行更新,因而能在CACHE中得以稳定的缓存,减少由于混合有读写字段导致的对应CACHE LINE的频繁失效问题,以便提高效率;而读写字段相对集中在一起,这样也能保证当程序读写结构体时,污染的CACHE LINE条数也就相对的较少。

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    typedef struct {

        /* ro data */

        size_t block_count;     // number of total blocks

     

        size_t meta_block_size; // sizeof per skb meta block

        size_t data_block_size; // sizeof per skb data block

         

        u8 *meta_base_addr;     // base address of skb meta buffer

        u8 *data_base_addr;     // base address of skb data buffer

     

        /* rw data */

        size_t current_index    ____cacheline_aligned;  // index

         

    } bc_buff, * bc_buff_t;

    3.申请地址连续的内存空间
    随着地址空间由32位转到64位,页内存管理的目录分级也越来越多,4级的目录地址转换也是一笔不小是开销。硬件产商为我们提供了TLB缓冲,加速虚拟地址到物理地址的换算。但是,毕竟TLB是有限,对地址连续的内存空间进行访问时,TLB能得到更多的命中,同时CACHE高速缓冲命中的几率也更大。
    两段代码,实现同一功能,但第一种方法在实际使用中,内存读写效率就会相对较好,特别是在申请的内存很大时(未考虑malloc异常):
    方法一:

    1

    2

    3

    4

    5

    6

    7

    8

    9

    #define MAX 100

    int i;

    char *p;

    struct box_d *box[MAX];

    p = (char *)malloc(sizeof(struct box_d) * MAX);

    for (i = 0; i < MAX; i ++)

    {

        box[i] = (struct box_d *)(p + sizeof(struct box_d) * i);

    }

    方法二:

    1

    2

    3

    4

    5

    6

    7

    #define MAX 100

    int i;

    struct box_d *box[MAX];

    for (i = 0; i < MAX; i ++)

    {

        box[i] = (struct box_d *)malloc(sizeof(struct box_d));

    }

    另外,如果我们使用更大页面(比如2M或1G)的分页机制,同样能够提升性能;因为相比于原本每页4K大小的分页机制,应用程序申请同样大小的内存,大页面分页机制需要的页面数目更少,从而占用的TLB项目也更少,减少虚拟地址到物理地址的转换次数的同时,提高TLB的命中率,缩短每次转换所需要的时间。因为大多数操作系统在分配内存时候都需要按页对齐,所以大页面分页机制的缺点就是内存浪费相对比较严重。只有在物理内存足够充足的情况下,大页面分页机制才能够体现出优势。

    4.其它优化
    4.1.预读指令读内存
    提前预取内存中数据到CACHE内,提高CACHE的命中率,加速内存读取速度,这是设计预读指令的主要目的。如果当前运算复杂度比较高,那么预取和运算就可同步进行,从而消除下一步内存访问的时延。相应的预读汇编指令有prefetch0、prefetch1、prefetch2、 prefetchnta。
    预取指令只是给CPU一个提示,所以它可被CPU忽略,而且就算预取一段错误的地址也不会导致CPU异常。一般使用prefetchnta预取指令,因为它不会污染CACHE,它把每次取得的数据都存放到L2 CACHE的第一条CACHE LINE,而另外几条指令会替换CACHE中最近最少使用的CACHE LINE。

    4.2.非暂时移动指令写内存
    我们知道为了保证CACHE与内存之间的数据一致性,CPU对CACHE的写操作主要有两种方式同步到内存,写透式(Write Through)和写回式(Write-back)。不管哪种同步方式都是要消耗性能的,而在某些情况下,写CACHE是不必要的:
    有哪些情况不需要写CACHE呢?比如做数据拷贝(高效memcpy函数实现)时,或者我们已经知道写的数据在最近一段时间内(或者永远)都不会再使用了,那么此时就可以不用写CACHE,让对应的CACHE LINE自动失效,以便缓存其它数据。这在某些特殊场景非常有用,相应的汇编指令有movntq、movntsd、movntss、movntps、movntpd、movntdq、movntdqa。
    完整的利用预读指令和非暂时移动指令实现的高速内存拷贝函数:

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    33

    34

    35

    36

    37

    38

    39

    40

    41

    void X_aligned_memcpy_sse2(void* dest, const void* src, const unsigned long size_t)

    {

      __asm

      {

        mov esi, src;    //src pointer

        mov edi, dest;   //dest pointer

        mov ebx, size_t//ebx is our counter

        shr ebx, 7;      //divide by 128 (8 * 128bit registers)

     

        loop_copy:

          prefetchnta 128[ESI]; //SSE2 prefetch

          prefetchnta 160[ESI];

          prefetchnta 192[ESI];

          prefetchnta 224[ESI];

     

          movdqa xmm0, 0[ESI]; //move data from src to registers

          movdqa xmm1, 16[ESI];

          movdqa xmm2, 32[ESI];

          movdqa xmm3, 48[ESI];

          movdqa xmm4, 64[ESI];

          movdqa xmm5, 80[ESI];

          movdqa xmm6, 96[ESI];

          movdqa xmm7, 112[ESI];

     

          movntdq 0[EDI], xmm0; //move data from registers to dest

          movntdq 16[EDI], xmm1;

          movntdq 32[EDI], xmm2;

          movntdq 48[EDI], xmm3;

          movntdq 64[EDI], xmm4;

          movntdq 80[EDI], xmm5;

          movntdq 96[EDI], xmm6;

          movntdq 112[EDI], xmm7;

     

          add esi, 128;

          add edi, 128;

          dec ebx;

     

          jnz loop_copy; //loop please

        loop_copy_end:

      }

    }

    总结
    要高效的访问内存,必须充分利用系统CACHE的缓存功能,因为就目前来说,CACHE的访问速度比内存快太多了。具体优化方法有:
    1.用设计上压缩结构体大小。
    2.结构体尽量做到机器字(倍数)对齐。
    3.结构体中频繁访问的字段尽量放在机器字对齐的位置。
    4.频繁读写的多个结构体变量尽量同时申请,使得它们尽可能的分布在较小的线性空间范围内,这样可利用TLB缓冲。
    5.当结构体比较大时,对结构体字段进行初始化或设置值时最好从第一个字段依次往后进行,这样可保证对内存的访问是顺序进行。
    6.额外的优化可以采用非暂时移动指令(如movntdq)与预读指令(如prefetchnta)。
    7.特殊情况可考虑利用多媒体指令SSE2、SSE4等。
    当然,上面某些步骤之间存在冲突,比如压缩结构体和结构体对齐,这就需要实际综合考虑。

    转载请保留地址:http://www.lenky.info/archives/2011/11/310 或 http://lenky.info/?p=310

    展开全文
  • 如何高效访问内存

    2017-06-27 09:54:40
    影响内存访问速度的因素主要有: 1.内存带宽:每秒读写内存的数据量,由硬件配置决定。 2.CACHE高速缓冲:CPU与内存之间的缓冲器,当命中率比较高时能大大提供内存平均访问速度。 3.TLB转换旁视缓冲:系统虚拟...
  • 使用缓存组件场景

    2020-09-06 21:19:20
    缓存优化一般思路 缓存主要针对时读操作,当你功能遇到下面场景是,就可以用缓存组件实现性能优化: 存在缓存热点,缓存数据能够...缓存最重要指标就是命中率,有以下几个因素影响命中率。 缓存容量
  • 影响内存访问速度的因素主要有: 1.内存带宽:每秒读写内存的数据量,由硬件配置决定。 2.CACHE高速缓冲:CPU与内存之间的缓冲器,当命中率比较高时能大大提供内存平均访问速度。 3.TLB转换旁视缓冲:系统...
  • 浅谈CPU缓存策略

    千次阅读 2004-09-03 15:14:00
    现代台式PCCPU缓存分为L1与L2两级,缓存命中率成为影响CPU性能一个重要因素。?由于容量缺失capacitymiss(因为容量问题而将内存有用块移出)会引起缓存未命中cachemiss,这就需要CPU花费更多时间由位于CPU内部...
  • 对于非交互式应用程序(如Internet电视广播),播放缓冲区可能会相对大一些,因为这并不会影响用户那端使用效果。 <br/> 目前Internet既不能保证分组传输及时性,也不能保证分组时间序列特性—这是因为...
  • 高效访问内存

    2014-09-05 11:31:37
    影响内存访问速度的因素主要有: 1.内存带宽:每秒读写内存的数据量,由硬件配置决定。 2.CACHE高速缓冲:CPU与内存之间的缓冲器,当命中率比较高时能大大提供内存平均访问速度。 3.TLB转换旁视缓冲:系统虚拟...
  • B: 管道是由内核管理一个缓冲区,其容量受多方面因素影响,包括缓冲大小、磁盘容量大小等问题 C: 当管道中没有信息话,从管道中读取进程会等待,直到另一端进程放入信息。当管道被放满信息时候,...
  • 一填空题 1对存储器要求是 速度快 _容量大_价位低_为了解决这方面矛盾计算机采用多级存储体系结构 2指令系统是表征一台计算机_性能_重要因素_格式_和_功能_不仅直接影响到机器硬件结构而且也影响到系统...
  • 影响单片机系统运行稳定性的因素可大体分为外因和内因两部分: 外因:即射频干扰,它是以空间电磁场的形式传递在机器内部的导体(引线 或零件引脚)感生出相应的干扰,可通过电磁屏蔽和合理的布线/器件布局衰减 该类...
  • **影响CPU性能**主要因素: 主频、指令系统、高速缓冲存储器容量和结构、逻辑结构 CPU时钟频率已经超过10GHZ(不是工作频率) **嵌入式系统硬件**: **硬件主体**是 中央处理器和存储器 **软件主体**是 操作...
  • 影响命中率h大小的因素 主要有: 1)程序的行为 2)Cache的容量与结构(如采用多级Cache等) 3)Cache的相联度,包括组织方式、块的大小等。 (15)主存与Cache的地址映射 ①全相联方式:标记+字地址 ②直接方式:标记+...
  • 计算机体系结构试题及答案

    热门讨论 2009-11-18 14:15:01
    1.6 影响计算机体系结构成本和价格因素 1.6.1 集成电路成本 1.6.2 计算机系统成本和价格 1.7 小结习题一第二章 计算机指令集结构设计 2.1 指令集结构分类 2.1.1 指令集结构分类 2.1.2 ...
  • 让Oracle跑得更快完整版目录整理齐全第1章 引起数据库性能问题的因素 1.1 软件设计对数据库的影响 1.1.1 软件架构设计对数据库性能的影响 1.1.2 软件代码的编写对数据库性能的影响 1.2 数据库的设计 1.2.1 0LTP...
  • 9.7 影响jms性能其他因素 小结 …… 第12章 java多线程技术与应用性能优化 12.1 java多线程技术 12.1.1 进程与线程 12.1.2 线程生命周期 12.2 并行任务与性能 12.2.1 并行任务与多线程 12.2.2 并行任务与死锁 ...
  • 事实上,不考虑其他的因素,仅考虑新的硬盘驱动器一项,就能得出上述结论,因为磁盘的记录密度每12~18个月就增加一倍。这些价格的下降使公司能够提供更广泛的商业需要,包括企业资源计划、销售自动化、数据仓库、...
  •  这一章主要讨论一些引起数据库性能问题的因素,包含了系统架构、软件代码、数据库设计、存储设计等话题。  第2章 锁和阻塞  在这一章里,将介绍Oracle数据库中锁的起因及由锁引起的性能问题—阻塞,并将讨论...
  • c语言编写单片机技巧

    2009-04-19 12:15:17
    最近几年高频电路与数位电路共享相同电路板,构成所谓混载电路系统似乎有增加趋势,类似如此设计经常会造成数位电路动作时,高频电路却发生动作不稳定等现象,其中原因之一是数位电路产生噪讯,影响高频电路...
  • 第1章 引起数据库性能问题的因素 1 1.1 软件设计对数据库的影响 1 1.1.1 软件架构设计对数据库性能的影响 1 1.1.2 软件代码的编写对数据库性能的影响 2 1.2 数据库的设计 8 1.2.1 oltp数据库 9 1.2.2 olap数据库 10 ...
  • 第1章 引起数据库性能问题的因素 1 1.1 软件设计对数据库的影响 1 1.1.1 软件架构设计对数据库性能的影响 1 1.1.2 软件代码的编写对数据库性能的影响 2 1.2 数据库的设计 8 1.2.1 oltp数据库 9 1.2.2 olap数据库 10 ...

空空如也

空空如也

1 2 3
收藏数 41
精华内容 16
关键字:

影响缓冲容量的因素