2015-07-03 2 views
18

У меня есть ноутбук с процессором Intel Core 2 Duo с тактовой частотой 2,4 ГГц и 2x4Gb DDR3 модулями 1066 МГц.Программа, превышающая теоретическую скорость передачи памяти

Я ожидаю, что это эта память может работать на скорости 1067 МиБ/с, и до тех пор, пока два канала, максимальная скорость 2134 MiB/сек (в случае, если ОС диспетчеру памяти позволит).

Я сделал крошечный приложение 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 МиБ/с, однако я получил следующее:

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

Как это возможно эта скорость была почти 3 гигабайта/сек?

DDR3 module photo

+5

вы забыли кеш процессора. есть l1, l2 и даже l3-кеш ... просто потому, что вы случайно звоняете, не означает, что вам не удастся получить хит в кеше. –

+0

тактовая частота DDR не интерпретируется так: – HuStmpHrrr

+1

@MarcB Правильно. Вот почему я создаю буферное хранилище 256 Мбайт. – Antonio

ответ

8

Ваш метод тестирования плохо разработан во многих аспектах, а также в качестве интерпретации рейтинга RAM.

Начнем с рейтинга; с момента внедрения SDRam маркетинг называет модули после их спецификации шины, то есть тактовой частоты шины, в сочетании с скоростью передачи пакета. Это лучший случай, и на практике он не может поддерживаться постоянно.

Параметры, опущенные этой меткой, действительны Время доступа (так называемая латентность) и общее время цикла (например, время предварительной зарядки). Их можно понять, фактически посмотрев на «временные» спецификации (2-3-3 вещи). Посмотрите статью, в которой подробно объясняется это. На самом деле CPU обычно не передает одиночные байты, а целые строки кэша (например, 8 записей на 8 байтов = 64 байта).

Ваш тестовый код плохо разработан, так как вы делаете произвольный доступ с относительно небольшим блоком, не привязанным к фактическим границам данных. Этот случайный доступ также приводит к частым странице промахов в MMU (узнайте, что делает TLB). Таким образом, вы измеряете дикую смесь различных аспектов системы.

+1

На самом деле это была моя цель проверить скорость, когда желаемый блок памяти не находится в кеше. Позвольте мне накрутить, что такое тайминги, и я вернусь. Спасибо за ответ. – Antonio

+1

Я добавил фотографию модуля DDR. Насколько я могу видеть, нет таймингов. – Antonio

+1

После некоторых поисковых запросов я нашел CL = 7. Итак, как бы вы вычислили лимит? – Antonio

18

Вот несколько вещей на работе.

Прежде всех: formula for memory transfer rate of DDR3 является

Для DDR3-1066 (который работает на частоте 133⅓ MHz), мы получаем теоретическую пропускную способность памяти 8533⅓ MB/s или 8138.02083333... MiB/s для одноканальной и 17066⅔ MB/s или 16276.0416666... MiB/s для двойного канальное.

Во-вторых: передача одного большого фрагмента данных происходит быстрее, чем передача многих небольших фрагментов данных.

В-третьих: ваши игнорирующие эффекты кеширования, которые могут возникнуть.

В-четвертых: если вы проводите измерения времени, вы должны использовать 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"); 
    } 
} 

Таким образом, вы смягчать, как много «других вычислений», как это возможно, и меры (почти) только скорость копирования памяти через System.arraycopy(...). У этого алгоритма могут быть проблемы с кэшированием.

Для моей системы (двухканальный DDR3-1600), я получаю что-то около 6 GiB/s, тогда как теоретический предел составляет около 25 GiB/s (включая DualChannel).

Как указано MagicM18, JVM вводит некоторые издержки. Поэтому ожидается, что вы не сможете достичь теоретического предела.


Sidenote: Для того, чтобы запустить программу, необходимо дать JVM больше heapspace. В моем случае было достаточно 4096 МБ.

+0

Я пробовал этот тест. Пришлось уменьшить SIZE до 256Mb. Получил: «Отправлено 2147483648 байт за 1.42 секунд. -> 1.41 GiB/s' – Antonio

+1

@ Антонио, почему вы уменьшили 'SIZE'? Чтобы приблизиться к теоретическому пределу, куски должны быть как можно большими. Было бы лучше дать JVM больше места для кучи ('java -Xmx4096m ...'), чем уменьшить размер куска. – Turing85

+0

Я установил размер хранилища до 1 Гб и продолжительностью до 30 секунд. Результаты стали почти идентичными - 1,42 ГБ/сек. ЦП не является узким местом (используется на 60%). – Antonio

1

В Википедии есть table of transfer rates. Данный ноутбук имеет следующие характеристики:

  • Тип модуля: PC3-8500 DDR3 SDRAM
  • типа Chip: DDR3-1066
  • Частота памяти: 133 MHz
  • автобус скорость: 1.066GT/s
  • скорость передачи данных (бит/с): 64 Gbit/s
  • скорость передачи данных (десятичное байт/с): 8 GB/s

Это отдельный модуль DDR3 на один канал.

1

Это может быть вопрос конфигурации оборудования. На основе предоставленной информации есть два ядра и два модуля памяти, но количество каналов памяти неясно. Хотя я никогда не видел тестирования, выполненного в масштабе ноутбука, в более крупных системах конфигурация модулей DIMM в каналах памяти может существенно повлиять на скорость передачи данных.

Например, на современных серверах возможно наличие конфигурации одного DIMM на канал (ODPC) или двух модулей DIMM на канал (TDPC). Каждый физический процессор может иметь несколько каналов памяти, разделенных между физическими ядрами на упомянутом процессоре, и каждый сервер может потенциально иметь несколько физических процессоров (как правило, 2-4 на современных серверах).

Как распределяется память между этими каналами, ядрами и процессорами/чипами могут существенно повлиять на производительность памяти в зависимости от того, что измеряется. Например, системы с конфигурацией ODPC значительно улучшат время передачи (с точки зрения передачи в секунду или MegaTransfers в секунду, MT/s) по сравнению с системами, которые имеют конфигурацию TDPC в случаях, когда объем памяти (в ГБ) в система TDPC равна или превышает объем памяти в конфигурации ODPC.

Основываясь на этих знаниях, можно предположить, что ноутбук, который настроен на 2 канала памяти в ODPC и один канал на один сердечник, теоретически может обеспечить описанную производительность.

С учетом сказанного существует ряд профилированных и анализируемых инструментов профилирования памяти, которые можно запускать неинвазивно для получения информации о производительности памяти в вашей системе.Memtest - очень мощный, хорошо понятный и хорошо документированный инструмент для тестирования памяти. Его можно загрузить на загрузочный диск (USB, DVD, флоппи-дисковод и т. Д.), Который можно безопасно использовать, чтобы подчеркнуть память в системе без возможности повредить или нарушить работу ОС. Он также включен на установочный DVD для некоторых дистрибутивов Linux, а также для спасения DVD/изображений. Это очень мощный инструмент, который я использовал во многих случаях для отладки и анализа производительности памяти, хотя обычно на серверах.

Смежные вопросы