程序超过理论记忆传输率

时间:2015-07-03 19:16:44

标签: java performance memory hardware benchmarking

我的笔记本电脑配备Intel Core 2 Duo 2.4GHz CPU和2x4Gb DDR3模块1066MHz。

我希望这个内存可以以1067 MiB / sec的速度运行,并且只要有两个通道,最大速度 2134 MiB / sec (如果OS内存调度程序允许的话)

我制作了一个小型Java应用来测试:

private static final int size = 256 * 1024 * 1024; // 256 Mb
private static final byte[] storage = new byte[size];

private static final int s = 1024; // 1Kb
private static final int duration = 10; // 10sec

public static void main(String[] args) {
    long start = System.currentTimeMillis();
    Random rnd = new Random();
    byte[] buf1 = new byte[s];
    rnd.nextBytes(buf1);
    long count = 0;
    while (System.currentTimeMillis() - start < duration * 1000) {
        long begin = (long) (rnd.nextDouble() * (size - s));
        System.arraycopy(buf1, 0, storage, (int) begin, s);
        ++count;
    }
    double totalSeconds = (System.currentTimeMillis() - start) / 1000.0;
    double speed = count * s / totalSeconds / 1024 / 1024;
    System.out.println(count * s + " bytes transferred in " + totalSeconds + " secs (" + speed + " MiB/sec)");

    byte[] buf2 = new byte[s];
    count = 0;
    start = System.currentTimeMillis();
    while (System.currentTimeMillis() - start < duration * 1000) {
        long begin = (long) (rnd.nextDouble() * (size - s));
        System.arraycopy(storage, (int) begin, buf2, 0, s);
        Arrays.fill(buf2, (byte) 0);
        ++count;
    }
    totalSeconds = (System.currentTimeMillis() - start) / 1000.0;
    speed = count * s / totalSeconds / 1024 / 1024;
    System.out.println(count * s + " bytes transferred in " + totalSeconds + " secs (" + speed + " MiB/sec)");
}

我预计结果会低于2134 MiB / sec但是我得到以下结果:

17530212352 bytes transferred in 10.0 secs (1671.811328125 MiB/sec)
31237926912 bytes transferred in 10.0 secs (2979.080859375 MiB/sec)

速度几乎是3 GiB /秒怎么可能?

DDR3 module photo

4 个答案:

答案 0 :(得分:19)

以下是有很多工作要做。

首先:formula for memory transfer rate of DDR3

memory clock rate
× 4  (for bus clock multiplier)
× 2  (for data rate)
× 64 (number of bits transferred)
/ 8  (number of bits/byte)
=    memory clock rate × 64 (in MB/s)

对于DDR3-1066(时钟频率为133⅓ MHz),我们为单通道和8533⅓ MB/s或{{获得理论内存带宽8138.02083333... MiB/s17066⅔ MB/s 1}}用于双通道。

第二:传输一大块数据比传输许多小块数据更快。

第三:你可以忽略缓存效果。

第四:如果你进行时间测量,你应该使用16276.0416666... MiB/s。这种方法更精确。

以下是测试程序 1 的重写版本。

System.nanoTime()

通过这种方式,您可以减轻其他计算量。尽可能通过import java.util.Random; public class Main { public static void main(String... args) { final int SIZE = 1024 * 1024 * 1024; final int RUNS = 8; final int THREADS = 8; final int TSIZE = SIZE / THREADS; assert (TSIZE * THREADS == THREADS) : "TSIZE must divide SIZE!"; byte[] src = new byte[SIZE]; byte[] dest = new byte[SIZE]; Random r = new Random(); long timeNano = 0; Thread[] threads = new Thread[THREADS]; for (int i = 0; i < RUNS; ++i) { System.out.print("Initializing src... "); for (int idx = 0; idx < SIZE; ++idx) { src[idx] = ((byte) r.nextInt(256)); } System.out.println("done!"); System.out.print("Starting test... "); for (int idx = 0; idx < THREADS; ++idx) { final int from = TSIZE * idx; threads[idx] = new Thread(() -> { System.arraycopy(src, from, dest, 0, TSIZE); }); } long start = System.nanoTime(); for (int idx = 0; idx < THREADS; ++idx) { threads[idx].start(); } for (int idx = 0; idx < THREADS; ++idx) { try { threads[idx].join(); } catch (InterruptedException e) { e.printStackTrace(); } } timeNano += System.nanoTime() - start; System.out.println("done!"); } double timeSecs = timeNano / 1_000_000_000d; System.out.println("Transfered " + (long) SIZE * RUNS + " bytes in " + timeSecs + " seconds."); System.out.println("-> " + ((long) SIZE * RUNS / timeSecs / 1024 / 1024 / 1024) + " GiB/s"); } } 测量(几乎)仅内存复制率。该算法可能仍然存在关于高速缓存的问题。

对于我的系统(双通道DDR3-1600),我得到System.arraycopy(...)左右,而理论上的限制大约是6 GiB/s(包括DualChannel)。

As pointed out by MagicM18,JVM引入了一些开销。因此,预计您无法达到理论极限。

1 旁注:要运行程序,必须为JVM提供更多的堆空间。就我而言,4096 MB就足够了。

答案 1 :(得分:8)

您的测试方法在很多方面都设计不合理,以及您对RAM评级的解释。

让我们从评级开始;自SDRam推出以来,营销人员在其总线规范之后命名模块 - 即总线时钟频率,与突发传输速率配对。这是最好的情况,在实践中它不能持续持续。

该标签省略的参数是实际访问时间(又称延迟)和总循环时间(又称预充电时间)。这些可以通过实际查看“时间”规格(2-3-3的东西)来计算出来。查找一篇详细解释这些内容的文章。实际上,CPU通常不传输单个字节,而是整个高速缓存行(例如,每8个字节8个条目= 64个字节)。

您的测试代码设计不合理,因为您正在使用与实际数据边界不对齐的相对较小的块进行随机访问。这种随机访问还会在MMU中引起频繁的页面未命中(了解TLB是什么/做什么)。因此,您正在测量不同系统方面的混合物。

答案 2 :(得分:1)

在维基百科中有一个table of transfer rates。这台笔记本电脑有以下规格:

  • 模块类型:PC3-8500 DDR3 SDRAM
  • 芯片类型:DDR3-1066
  • 内存时钟:133 MHz
  • 公交车速度:1.066GT / s
  • 传输速率(位/秒):64 Gbit/s
  • 传输速率(十进制字节/秒):8 GB/s

这是每个单通道的单个DDR3模块。

答案 3 :(得分:1)

这可能是硬件配置问题。根据提供的信息,有两个内核和两个内存模块,但内存通道的数量不清楚。虽然我从未见过在笔记本电脑上完成测试,但在大型系统上,内存通道中DIMM的配置会对内存传输速率产生重大影响。

例如,在现代服务器上,可以使用每个通道一个DIMM(ODPC)或每个通道两个DIMM(TDPC)内存配置。每个物理CPU可以有多个内存通道,分配在所述CPU上的物理内核中,每个服务器可能有多个物理CPU(现代服务器通常为2-4个)。

内存如何在这些通道,内核和CPU /芯片之间分配,可能会对内存性能产生重大影响,具体取决于所测量的内容。例如,与具有TDPC配置的系统相比,具有ODPC配置的系统将具有显着改善的传输时间(以每秒传输速率或每秒传输兆次数,MT / s),在具有内存量(以GB为单位)的情况下TDPC系统等于或大于ODPC配置中的内存量。

基于这些知识,可以想象,在ODPC中设置2个存储信道并且每个核心方式设置一个信道的笔记本电脑理论上可以实现所描述的性能。

尽管如此,有许多预先打包的内存分析和分析工具可以非侵入式运行,以获取有关系统内存性能的信息。 Memtest是一个非常强大,易于理解且记录良好的测试内存的工具。它可以下载到某种类型的可引导磁盘(USB,DVD,软盘等)上,可以安全地用于压缩系统内存,而不会损坏或干扰操作系统。它也包含在安装DVD中,用于某些Linux发行版以及救援DVD /图像。它是一个非常强大的工具,我曾多次用它来调试和分析内存的性能,但通常是在服务器上。