如何更加有效的操控内存

内存的物理结构

这次要介绍的内存是DRAM,DRAM的全拼是Dynamic Random Access Memory,这是一种上电使用,断电数据就消失的Memory。先看一下内存的外部结构

如上图照片,内存条上一块黑色的芯片称为一个chip,8个chip组成一个rank。按照常理,一个chip的存储容量是1GB,所以这应该是一个8GB的内存条。

再看一个chip的内部结构

如上图,一个chip内部有8个bank,每个bank可以看成存储数据的最小单元,一个bank内部在寻址上分为row和column,一个确定的row和column就唯一确定8bit数据,8个bank就唯一确定64bit数据。每个bank下有一个row buffer用来缓存读出的数据。

内存读取数据

大家都知道CPU和内存之间是总线相连,CPU会将想要的数据的内存寻址地址(row、column)发送给总线,总线再将该地址发送给内存,内存取到数据再将数据放到总线上,CPU再从总线上获得数据。

CPU     --------BUS-------->  (Address)   RAM

CPU     (Data) <--------BUS------------   RAM

整个上面这个过程,在计算机的体系结构中都是由系统时钟控制的。

在RAM控制器这一边,过程大概是这样的:

1.CPU和BUS通信,物理地址放到总线;

2.BUS和内存芯片通信,总线把地址传给芯片组;

3.芯片组对内存寻址和读取数据

a.内存模块确定chip

b.内存芯片行地址预充电 ,需要tRP个周期(time of Row Precharge)

c.锁定行到等待列地址,需要tRCD个周期(tiime of RAS to CAS Delay)

d.锁定列到响应数据,需要tCL个周期(time of CAS Latency)

4.数据送到BUS上,CPU读取。

读取数据,如下图所示

从图中可以看到,64bit数据,每8bit存储在一个bank上,64bit并不是连续的。

内存对齐

以前在参见面试的时候经常遇到这样的问题,为什么要在编程的时候要做到内存对齐?

从上面的分析过程中,1次内存取数据能够得到64bit的数据,如果我们取0-63bit的数据,1次内存取数据OK,我们取64-127bit的数据,1次内存取数据也OK,但是如果我们想获得60-77bit的数据,那么就要进行2次内存取数据,那这效率可就下来了!

时钟周期

对于时钟再多说一下,在电路设计中,时钟是一个最为基础也是最为重要的概念,电子电路每个模块都有自己的动作,每个元器件完成动作的时间是未知的,如何让他们步调统一的工作,这时候时钟就派上用场了。时钟就是最最基本的节拍器。

像DRAM Controller这样复杂的模块,有时候一个大的动作的同步可能一个时钟周期还不OK,需要多个时钟周期。

对于上面提到几个时钟,如下表

tCLtRCDtRPtRAS
全称CAS LatencyRAS to CAS DelayRAS PrechargeAct-to-Precharge Precharge Delay
描述发送列地址到数据响应的延时行地址到列地址的延迟行地址控制器预充电的时间前后两次IO,如果行地址变化,读数据至少需要的周期数
周期77724

其中:

CAS:列地址选通脉冲

RAS:行地址选通脉冲

有了上面的知识就可以计算:

假设内存的IO频率是533Mhz

case1 :在连续两次的取数据的过程中,行地址(row)不变,列地址(column)改变,则第2次内存取数据的时间

因为连续内存的行地址不变,则在第2次内存取数据的时候不再需要行地址控制预充电的时间(tRP)和行地址到列地址的延迟(tRCD),只需要经历发送列地址到数据响应的延时(tCL)即可。

所以,t = 7 *  1 / 533Mhz = 13.13ns

case2:在连续两次的取数据的过程中,行地址(row)改变,列地址(column)改变,则第2次内存取数据的时间

因为这次行地址也改变,列地址也改变,所以要经历行地址控制器预充电的时间(tRP)、行地址到列地址的延迟(tRCD)和发送列地址到数据响应的延时(tCL),这一共是21个时钟周期。前后两次IO,如果行地址变化,需要重新预充电,读数据至少需要24个周期(tRAS)。

所以,t = 24 *  1 / 533Mhz = 45ns。

所以能够看到即使对于DRAM这种高速的存储器,随机存取和顺序存取的效率也是差了好多,顺序要快3倍以上!

注意,因为CPU有Cache,在现代计算机体系结构中,为了提高效率,CPU和内存的IO在实际中都是以64字节为单位进行的(程序运行的局部原理)!

再说时钟周期

内存的发展,现在也有几代了,从SDR到DDR,DDR到DDR2,再到DDR3,其频率变化如下表所示

这里面我想说两点:

1.可以看到核心频率在几代中并没有明显的提升,那是因为核心频率,是计算机主板的时钟的震荡频率,这个时钟提升是很难的。

2.但是从I/O频率和等效频率来看还是有提升的,这是因为主要在两处做了优化:1.由之前只在时钟上升沿取数据,到改进后上升沿下降沿皆可以取数据;2.预取更多的数据达到的。

测试

下面通过一组测试来检验内存不同的随机IO方式,对性能的影响。

代码如下:

#include <time.h>
#include <iostream>
#include <random>

using namespace std;

const long long  SIZE = 8 * 1000 * 1000;
const long long  LOOP = 1000;
const int STEP = 1;

int main(int argc, char* argv[])
{
    long long *p = new long long[SIZE];

    long long a = 0;

    long long start = clock();

    for (int i = 0; i < LOOP; i++){
        for (int j = 0; j < SIZE; j += STEP){
            a += p[j];
        }
    }

    long long end = clock();

    long long div = LOOP * (SIZE/STEP);

    double t = (double)(end - start) * 1000 / div;

    cout << "One get memory time : " << t << " ns" << endl;
}

a.这里面进行1000次循环试验,取均值,防止一些扰动。

b.在内存new出来的空间为8 * 8 * 1000 * 1000 = 64MB,之所以取这么大,是防止CPU Cache的干扰。

STEP分别取1、8、32、64、128、256、512、1024,得到的实验结果如下表所示

STEP1832641282565121024
Time(ns)1.413.105.666.388.208.719.6510.15

可以看到空间取值随着步长一步步被打乱,取数据的时间也一点点增大。

随机乱序取数据

代码如下:

#include <time.h>
#include <iostream>
#include <random>

using namespace std;

const long long  SIZE = 8  * 1000 * 1000;
const long long  LOOP = 1000;

int main(int argc, char* argv[])
{
    default_random_engine generator;
    uniform_int_distribution<int> distribution(0, SIZE - 1);
    auto dice = bind ( distribution, generator );

    long long *p = new long long[SIZE];

    long long a = 0;

    long long start = clock();

    for (int i = 0; i < LOOP; i++){
        for (int j = 0; j < SIZE; j++){
            a += p[dice()];
            //dice();                   /*单独执行,计算随机数的影响*/
        }
    }

    long long end = clock();

    long long div = LOOP * SIZE;

    double t = (double)(end - start) * 1000 / div;

    cout << "One get memory time : " << t << " ns" << endl;
}

随机取内存数据的时间为163.96 ns,随机数的时间为90.00 ns,那么得到内存的取数据时间为73.96ns

这个和上面计算的45ns有一些差距,我想可能由于以下原因:

1.逻辑地址到物理地址转换的时间(这个是在CPU里面执行的,应该很快);

2.CPU发地址给总线,CPU从总线上接收到数据(这也需要几个时钟周期);

3.tRAS=24是最小的周期数,可能在某些时候在内存中取数据要大于24。

备注:

以上是我对内存这块知识的理解和自己的总结,肯定有些不对的地方,希望大家指正,谢谢。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值