mirror of
https://github.com/MPSU/APS.git
synced 2025-09-15 17:20:10 +00:00
349 lines
38 KiB
Markdown
349 lines
38 KiB
Markdown
# Лабораторная работа 16 "Оценка производительности"
|
||
|
||
## Материал для подготовки к лабораторной работе
|
||
|
||
Данная лабораторная работа будет полностью опираться на навыки, полученные в ходе выполнения лабораторных работ:
|
||
|
||
13. [Периферийные устройства](../13.%20Peripheral%20units/)
|
||
14. [Программирование](../14.%20Programming/)
|
||
15. [Программатор](../15.%20Programming%20device/)
|
||
|
||
## Цель
|
||
|
||
Дать количественную оценку, характеризующую производительность реализованной вычислительной системы. На текущий момент мы создали процессорную систему, которая способна взаимодействовать с внешним миром посредством периферийных устройств ввода-вывода и программатора, по сути являющуюся компьютером. Однако встает вопрос, какое место данная система занимает в ряду уже существующих вычислительных систем.
|
||
|
||
<!-- Ближе ли она по производительности к [разностной машине Бэббиджа](https://ru.wikipedia.org/wiki/Разностная_машина_Чарльза_Бэббиджа) или же к [Frontier](https://ru.wikipedia.org/wiki/Frontier_(суперкомпьютер)) (первому в мире [экзафлопсному компьютеру](https://ru.wikipedia.org/wiki/Exascale_computing)). Получится ли на ней запустить Doom или Skyrim? Всего этого мы не узнаем наверняка до тех пор, пока не произведем оценку производительности. -->
|
||
|
||
Для оценки производительности необходимо модифицировать существующую процессорную систему, а после собрать и запустить специализированное ПО, отвечающее за измерение производительности (будет использована программа Coremark).
|
||
|
||
## Теория
|
||
|
||
[Coremark](https://www.eembc.org/coremark/faq.php) (далее кормарк) — это набор синтетических тестов (специальных программ) для измерения производительности процессорной системы. В данный набор входят такие тесты, как работа со связными списками, матричные вычисления, обработка конечных автоматов и подсчет контрольной суммы. Результат выражается в одном числе, которое можно использовать для сравнения с результатами других процессорных систем.
|
||
|
||
Для подсчета производительности, кормарк опирается на функцию, возвращающую текущее время, поэтому для оценки производительности нам потребуется вспомогательное периферийное устройство: таймер.
|
||
|
||
Для вывода результатов тестирования, необходимо описать способ, которым кормарк сможет выводить очередной символ сообщения — для этого мы будем использовать контроллер UART из предыдущих лабораторных работ.
|
||
|
||
Кроме того, скомпилированная без оптимизаций программа будет занимать чуть более 32KiB, поэтому нам потребуется изменить размер памяти инструкций.
|
||
|
||
Таким образом, для того чтобы запустить данную программу, нам необходимо выполнить как аппаратные изменения процессорной системы (добавить таймер и (если отсутствует) контроллер UART), так и программные изменения самого кормарка (для этого в нем предусмотрены специальные платформозависимые файлы, в которых объявлены функции, реализацию которых нам необходимо выполнить).
|
||
|
||
## Задание
|
||
|
||
1. Реализовать модуль-контроллер "таймер".
|
||
2. Подключить этот модуль к системной шине.
|
||
2.1. В случае, если до этого в ЛР13 вашим устройством вывода было не UART TX, вам необходимо подключить к системной шине готовый модуль [uart_tx_sb_ctrl](../Made-up%20modules/lab_13.uart_tx_sb_ctrl.sv).
|
||
3. Добавить реализацию платформозависимых функций программы coremark.
|
||
4. Скомпилировать программу.
|
||
5. Изменить размер памяти инструкций.
|
||
6. Запустить моделирование.
|
||
7. Сравнить результаты измерения производительности с результатами существующих процессорных системам.
|
||
|
||
### Таймер
|
||
|
||
Разберемся с тем, как будет работать наш таймер. По сути, это просто системный счетчик (не путайте с программным счетчиком), непрерывно считающий такты с момента последнего сброса. Системным он называется потому, что работает на системной тактовой частоте. Значения частот, на которых работают процессорные системы сравнимы с 32-битными значениями, поэтому системный счетчик должен быть 64-битным. Для измерения времени мы будем засекать значение счетчика на момент начала отсчета и значение счетчика в конце отсчета. Зная тактовую частоту и разность между значениями счетчика мы с легкостью сможем вычислить прошедшее время. При этом нужно обеспечить счетчик такой разрядностью, чтобы он точно не смог переполниться.
|
||
|
||
Поскольку мы уже назвали данный модуль "таймером", чтобы тот не был слишком простым, давайте добавим ему функциональности: пускай это будет устройство, способное генерировать прерывание через заданное число тактов. Таким образом, процессорная система сможет засекать время без постоянного опроса счетчика.
|
||
|
||
Было бы удобно, чтобы мы могли управлять тем, каким образом данный модуль будет генерировать такое прерывание: однократно, заданное число раз, или же бесконечно, пока тот не остановят.
|
||
|
||
Таким образом, мы сформировали следующее адресное пространство данного контроллера:
|
||
|
||
|Адрес|Режим доступа|Допустимые значения| Функциональное назначение |
|
||
|-----|-------------|-------------------|---------------------------------------------------------------------------------------|
|
||
|0x00 | R | [0:2³²-1] | Значение младших 32 бит системного счетчика, доступное только для чтения |
|
||
|0x04 | R | [0:2³²-1] | Значение старших 32 бит системного счетчика, доступное только для чтения |
|
||
|0x08 | RW | [0:2³²-1] | Указание младших 32 бит задержки, спустя которую таймер будет генерировать прерывание |
|
||
|0x0c | RW | [0:2³²-1] | Указание старших 32 бит задержки, спустя которую таймер будет генерировать прерывание |
|
||
|0x10 | RW | [0:2] | Указание режима генерации прерываний (выключен, заданное число раз, бесконечно) |
|
||
|0x14 | RW | [0:2³²-1] | Указание количества повторений генерации прерываний |
|
||
|0x24 | W | 1 | Программный сброс |
|
||
|
||
Прототип модуля следующий:
|
||
|
||
```SystemVerilog
|
||
module timer_sb_ctrl(
|
||
/*
|
||
Часть интерфейса модуля, отвечающая за подключение к системной шине
|
||
*/
|
||
input logic clk_i,
|
||
input logic rst_i,
|
||
input logic req_i,
|
||
input logic write_enable_i,
|
||
input logic [31:0] addr_i,
|
||
input logic [31:0] write_data_i,
|
||
output logic [31:0] read_data_o,
|
||
output logic ready_o,
|
||
/*
|
||
Часть интерфейса модуля, отвечающая за отправку запросов на прерывание
|
||
процессорного ядра
|
||
*/
|
||
output logic interrupt_request_o
|
||
);
|
||
```
|
||
|
||
Для работы данного контроллера потребуются следующие сигналы:
|
||
|
||
```SystemVerilog
|
||
logic [63:0] system_counter;
|
||
logic [63:0] delay;
|
||
enum logic [1:0] {OFF, NTIMES, FOREVER} mode, next_mode;
|
||
logic [31:0] repeat_counter;
|
||
logic [63:0] system_counter_at_start;
|
||
```
|
||
|
||
- `system_counter` — регистр, ассоциированный с адресами `0x00` (младшие 32 бита) и `0x04` (старшие 32 бита), системный счетчик. Задача регистра заключается в ежетактном увеличении на единицу.
|
||
- `delay` — регистр, ассоциированный с адресами `0x08` (младшие 32 бита) и `0x0c` (старшие 32 бита). Число тактов, спустя которое таймер (когда тот будет включен) сгенерирует прерывание. Данный регистр изменяется только сбросом, либо запросом на запись.
|
||
- `mode` — регистр, ассоциированный с адресом `0x10`. Режим работы таймера:
|
||
- `OFF` — отключен (не генерирует прерывания)
|
||
- `NTIMES` — включен до тех пор, пока не сгенерирует N прерываний (Значение N хранится в регистре `repeat_counter` и обновляется после каждого сгенерированного прерывания). После генерации N прерываний, переходит в режим `OFF`.
|
||
- `FOREVER` — бесконечная генерация прерываний. Не отключится, пока режим работы прерываний не будет изменен.
|
||
- `next_mode` — комбинационный сигнал, который подается на вход записи в регистр `mode` (аналог `next_state` из предыдущей лабораторной работы).
|
||
- `repeat_counter` — регистр, ассоциированный с адресом `0x14`. Количество повторений для режима `NTIMES`. Уменьшается в момент генерации прерывания в этом режиме.
|
||
- `system_counter_at_start` — неархитектурный регистр, хранящий значение системного счетчика на момент начала отсчета таймера. Обновляется при генерации прерывания (если это не последнее прерывание в режиме `NTIMES`) и при запросе на запись в регистр `mode` значения не `OFF`.
|
||
|
||
Для подключения данного таймера к системной шине, мы воспользуемся первым свободным базовым адресом, оставшимся после ЛР13: `0x08`. Таким образом, для обращения к системному счетчику, процессор будет использовать адрес `0x08000000` для обращения к регистру `delay` `0x08000004` и т.п.
|
||
|
||
### Настройка Coremark
|
||
|
||
В первую очередь, необходимо скачать исходный код данной программы, размещенный по адресу: [https://github.com/eembc/coremark](https://github.com/eembc/coremark). На случай возможных несовместимых изменений в будущем, все дальнейшие ссылки будут даваться на слепок репозитория, который был на момент коммита `d5fad6b`.
|
||
|
||
После этого, чтобы добавить поддержку нашей процессорной системы потребуется:
|
||
|
||
1. Реализовать функцию, измеряющую время
|
||
2. Реализовать функцию, выводящую очередной символ сообщения с результатами
|
||
3. Реализовать функцию, выполняющую первичную настройку периферии перед тестом
|
||
4. Выполнить мелкую подстройку, такую как количество итераций в тесте и указание аргументов, с которыми будет скомпилирована программа.
|
||
|
||
Все файлы, содержимое которых мы будем менять расположены в папке [barebones](https://github.com/eembc/coremark/tree/d5fad6bd094899101a4e5fd53af7298160ced6ab/barebones).
|
||
|
||
#### 1. Реализация функции, измеряющей время
|
||
|
||
Не мы первые придумали измерять время путем отсчета системных тактов, поэтому вся логика по измерению времени уже реализована в coremark. От нас требуется только реализовать функцию, которая возвращает текущее значение системного счетчика.
|
||
|
||
Данной функцией является `barebones_clock`, расположенная в файле [`core_portme.c`](https://github.com/eembc/coremark/blob/d5fad6bd094899101a4e5fd53af7298160ced6ab/barebones/core_portme.c). В данный момент, в реализации функции описан вызов ошибки (поскольку реализации как таковой нет). Мы должны **заменить** реализацию функции следующим кодом:
|
||
|
||
```C
|
||
barebones_clock()
|
||
{
|
||
volatile ee_u32 *ptr = (ee_u32*)0x08000000;
|
||
ee_u32 tim = *ptr;
|
||
return tim;
|
||
}
|
||
```
|
||
|
||
После ЛР14 вы уже должны представлять, что здесь происходит. Мы создали указатель с абсолютным адресом `0x08000000` — адресом системного счетчика. Разыменование данного указателя вернет текущее значение системного счетчика, что и должно быть результатом вызова этой функции. Поскольку тест закочнится менее чем за секунду, не обязательно загружать значение старших 32 бит (они будут не равны нулю только спустя 2³²тактов / 10⁶тактов/с ≈ 429c).
|
||
|
||
Для того, чтобы корректно преобразовать тики системного счетчика во время, используется функция [`time_in_secs`](https://github.com/eembc/coremark/blob/d5fad6bd094899101a4e5fd53af7298160ced6ab/barebones/core_portme.c#L117), которая уже реализована, но для работы которой нужно определить макрос `CLOCKS_PER_SEC`, характеризующий тактовую частоту, на которой работает процессор. Давайте определим данный макрос сразу над макросом [`EE_TICKS_PER_SEC`](https://github.com/eembc/coremark/blob/d5fad6bd094899101a4e5fd53af7298160ced6ab/barebones/core_portme.c#L62):
|
||
|
||
```C
|
||
#define CLOCKS_PER_SEC 10000000
|
||
```
|
||
|
||
На этом наша задача по измерению времени завершена. Остальные правки будут не сложнее этих.
|
||
|
||
#### 2. Реализация вывода очередного символа сообщения
|
||
|
||
Для вывода очередного символа во встраиваемых системах используется (какое совпадение!) функция [`uart_send_char`](https://github.com/eembc/coremark/blob/d5fad6bd094899101a4e5fd53af7298160ced6ab/barebones/ee_printf.c#L663), расположенная в файле [`ee_printf.c`](https://github.com/eembc/coremark/blob/d5fad6bd094899101a4e5fd53af7298160ced6ab/barebones/ee_printf.c).
|
||
|
||
В реализации данной функции вам уже предлагают алгоритм, по которому та должна работать. Необходимо:
|
||
|
||
1. дождаться готовности UART к отправке;
|
||
2. передать отправляемый символ;
|
||
3. дождаться готовности UART к отправке (завершения передачи).
|
||
|
||
Давайте так и реализуем эту функцию:
|
||
|
||
```C
|
||
uart_send_char(char c)
|
||
{
|
||
volatile ee_u8 *uart_ptr = (ee_u8 *)0x06000000;
|
||
while(*(uart_ptr+0x08));
|
||
*uart_ptr = c;
|
||
while(*(uart_ptr+0x08));
|
||
}
|
||
```
|
||
|
||
`0x06000000` — базовый адрес контроллера UART TX из ЛР13 (и адрес передаваемых этим контроллером данных).
|
||
`0x08` — смещение до адреса регистра `busy` в адресном пространстве этого контроллера.
|
||
|
||
#### 3. Реализация функции первичной настройки
|
||
|
||
Это функция [`portable_init`](https://github.com/eembc/coremark/blob/d5fad6bd094899101a4e5fd53af7298160ced6ab/barebones/core_portme.c#L130), расположена в уже известном ранее файле [`core_portme`.c]. Данная функция выполняет необходимые нам настройки перед началом теста. Для нас главное — настроить нужным образом контроллер UART.
|
||
Допустим, мы хотим чтобы данные передавались на скорости `115200`, c одним стоповым битом и контролем бита четности. В этом случае, мы должны добавить в начало функции следующий код:
|
||
|
||
```C
|
||
portable_init(core_portable *p, int *argc, char *argv[])
|
||
{
|
||
volatile ee_u32 *uart_tx_ptr = (ee_u32 *)0x06000000;
|
||
*(uart_tx_ptr + 3) = 115200;
|
||
*(uart_tx_ptr + 4) = 1;
|
||
*(uart_tx_ptr + 5) = 1;
|
||
|
||
//...
|
||
}
|
||
```
|
||
|
||
#### 4. Дополнительные настройки
|
||
|
||
Для тонких настроек используется заголовочный файл [`core_portme.h`](https://github.com/eembc/coremark/blob/d5fad6bd094899101a4e5fd53af7298160ced6ab/barebones/core_portme.h), куда также требуется внести несколько изменений. Нам необходимо:
|
||
|
||
1. Объявить в начале файла макрос `ITERATIONS`, влияющий на количество прогонов теста. Нам достаточно выставить значение 1.
|
||
2. Обновить значение макроса `COMPILER_FLAGS`, заменив его значение `FLAGS_STR` на`"-march=rv32i_zicsr -mabi=ilp32"`, именно с этими аргументами мы будем собирать программу. Это опциональная настройка, которая позволит вывести флаги компиляции в итоговом сообщении.
|
||
3. Добавить подключение заголовочного файла `#include <stddef.h>`.
|
||
|
||
### Компиляция
|
||
|
||
Для компиляции программы, вам потребуются предоставленные файлы [Makefile](Makefile) и [linker_script.ld](linker_script.ld), а также файл [startup.S](../14.%20Programming/startup.S) из ЛР14. Эти файлы необходимо скопировать с заменой в корень папки с программой.
|
||
|
||
`Makefile` написан из расчёта, что кросс-компилятор расположен по пути `C:/riscv_cc/`. В случае, если это не так, измените первую строчку данного файла в соответствии с расположением кросс-компилятора.
|
||
|
||
Для запуска компиляции, необходимо выполнить следующую команду, находясь в корне программы coremark:
|
||
|
||
```bash
|
||
make
|
||
```
|
||
|
||
В случае, если на вашем рабочем компьютере не установлена утилита `make`, то вы можете скомпилировать программу вручную, выполнив следующую последовательность команд:
|
||
|
||
```bash
|
||
cp barebones/*.c barebones/*.h ./
|
||
/c/riscv_cc/bin/riscv-none-elf-gcc -c -march=rv32i_zicsr -mabi=ilp32 -I"./" core_main.c -o core_main.o
|
||
/c/riscv_cc/bin/riscv-none-elf-gcc -c -march=rv32i_zicsr -mabi=ilp32 -I"./" startup.S -o startup.o
|
||
/c/riscv_cc/bin/riscv-none-elf-gcc -c -march=rv32i_zicsr -mabi=ilp32 -I"./" core_list_join.c -o core_list_join.o
|
||
/c/riscv_cc/bin/riscv-none-elf-gcc -c -march=rv32i_zicsr -mabi=ilp32 -I"./" core_matrix.c -o core_matrix.o
|
||
/c/riscv_cc/bin/riscv-none-elf-gcc -c -march=rv32i_zicsr -mabi=ilp32 -I"./" core_portme.c -o core_portme.o
|
||
/c/riscv_cc/bin/riscv-none-elf-gcc -c -march=rv32i_zicsr -mabi=ilp32 -I"./" core_state.c -o core_state.o
|
||
/c/riscv_cc/bin/riscv-none-elf-gcc -c -march=rv32i_zicsr -mabi=ilp32 -I"./" core_util.c -o core_util.o
|
||
/c/riscv_cc/bin/riscv-none-elf-gcc -c -march=rv32i_zicsr -mabi=ilp32 -I"./" cvt.c -o cvt.o
|
||
/c/riscv_cc/bin/riscv-none-elf-gcc -c -march=rv32i_zicsr -mabi=ilp32 -I"./" ee_printf.c -o ee_printf.o
|
||
/c/riscv_cc/bin/riscv-none-elf-gcc core_main.o startup.o core_list_join.o core_matrix.o core_portme.o core_state.o core_util.o cvt.o ee_printf.o -Wl,--gc-sections -nostartfiles -T linker_script.ld -march=rv32i_zicsr -mabi=ilp32 -I"./" -o coremark.elf
|
||
/c/riscv_cc/bin/riscv-none-elf-objdump -D coremark.elf > coremark_disasm.S
|
||
/c/riscv_cc/bin/riscv-none-elf-objcopy -O verilog --verilog-data-width=4 -j .data -j .sdata -j .bss coremark.elf coremark_data.mem
|
||
/c/riscv_cc/bin/riscv-none-elf-objcopy -O verilog --verilog-data-width=4 -j .text coremark.elf coremark_instr.mem
|
||
/c/riscv_cc/bin/riscv-none-elf-size coremark.elf
|
||
sed -i '1d' coremark_data.mem
|
||
```
|
||
|
||
В случае успешной компиляции, вам будет выведено сообщение об итоговом размере секций инструкций и данных:
|
||
|
||
```text
|
||
text data bss dec hex filename
|
||
34324 2268 100 36692 8f54 coremark.elf
|
||
```
|
||
|
||
### Изменение размера памяти инструкций
|
||
|
||
Как видите, размер секции инструкций превышает 32KiB на 1556 байт (32768—34324). Поэтому на время оценки моделирования, нам придется увеличить размер памяти инструкций до 64KiB, изменив число **слов** памяти инструкций до 16384. При этом необходимо изменить диапазон бит адреса, используемых для чтения инструкции из памяти с `[11:2]` на `[15:2]`.
|
||
|
||
Обратите внимание, что увеличение размера памяти в 16 раз приведет к значительному увеличению времени синтеза устройства, поэтому данное изменение мы производим исключительно на время поведенческого моделирования.
|
||
|
||
### Запуск моделирования
|
||
|
||
Программирование 32KiB по UART займет ощутимое время, поэтому вам предлагается проинициализировать память инструкций и данных "по старинке" через системные функции `$readmemh`.
|
||
|
||
Если все было сделано без ошибок, то примерно на `276ms` времени моделирования вам начнется выводиться сообщение вида:
|
||
|
||
```text
|
||
CoreMark Size : 666
|
||
Total ticks : 2574834
|
||
Total time (secs): <скрыто то получения результатов моделирования>
|
||
Iterations/Sec : <скрыто то получения результатов моделирования>
|
||
ERROR! Must execute for at least 10 secs for a valid result!
|
||
Iterations : 1
|
||
Compiler version : GCC13.2.0
|
||
Compiler flags : -march=rv32i_zicsr -mabi=ilp32
|
||
Memory location : STACK
|
||
seedcrc : 0x29f4
|
||
[0]crclist : 0x7704
|
||
[0]crcmatrix : 0x1fd7
|
||
[0]crcstate : 0x8e3a
|
||
[0]crcfinal : 0x7704
|
||
Correct operation validated. See README.md for run and reporting rules.
|
||
```
|
||
|
||
(вывод сообщения будет завершен приблизительно на `335ms` времени моделирования).
|
||
|
||
## Порядок выполнения задания
|
||
|
||
1. [Опишите](#таймер) таймер в виде модуля `timer_sb_ctrl`.
|
||
2. Проверьте описанный модуль с помощью тестового окружения [tb_timer](tb_timer.sv).
|
||
3. Подключите `timer_sb_ctrl` к системной шине. Сигнал прерывания этого модуля подключать не нужно.
|
||
2.1 В случае, если до этого в ЛР13 вашим устройством вывода было не UART TX, вам необходимо подключить к системной шине и готовый модуль [uart_tx_sb_ctrl](../Made-up%20modules/lab_13.uart_tx_sb_ctrl.sv).
|
||
4. Получите исходники программы Coremark. Для этого можно либо склонировать репозиторий, либо скачать его в виде архива со страницы: [https://github.com/eembc/coremark](https://github.com/eembc/coremark).
|
||
5. Добавьте реализацию платформозависимых функций программы coremark. Для этого в папке `barebones` необходимо:
|
||
1. в файле `core_portme.c`:
|
||
1. [реализовать](#1-реализация-функции-измеряющей-время) функцию `barebones_clock`, возвращающую текущее значение системного счетчика;
|
||
2. объявить макрос `CLOCKS_PER_SEC`, характеризующий тактовую частоту процессора;
|
||
3. [реализовать](#3-реализация-функции-первичной-настройки) функцию `portable_init`, выполняющую первичную инициализацию периферийных устройств до начала теста;
|
||
2. в файле `ee_printf.c` [реализовать](#2-реализация-вывода-очередного-символа-сообщения) функцию `uart_send_char`, отвечающую за отправку очередного символа сообщения о результате.
|
||
6. Добавьте с заменой в корень программы файлы [Makefile](Makefile), [linker_script.ld](linker_script.ld) и [startup.S](../14.%20Programming/startup.S).
|
||
7. Скомпилируйте программу вызовом `make`.
|
||
1. Если кросскомпилятор расположен не в директории `C:/riscv_cc`, перед вызовом `make` вам необходимо соответствующим образом отредактировать первую строчку в `Makefile`.
|
||
2. В случае отсутствия на компьютере утилиты `make`, вы можете самостоятельно скомпилировать программу вызовом команд, представленных в разделе ["Компиляция"](#компиляция).
|
||
8. Временно измените размер памяти инструкций до 64KiB.
|
||
1. Для этого необходимо изменить размер памяти инструкций с 1024 слов до 16384 слов.
|
||
2. Кроме того, необходимо изменить используемые индексы адреса в памяти с `[11:2]` на `[15:2]`.
|
||
9. Проинициализируйте память инструкций и память данных файлами "coremark_instr.mem" и "coremark_data.mem", полученными в ходе компиляции программы.
|
||
10. Выполните моделирование системы с помощью модуля [tb_coremark](tb_coremark).
|
||
1. Результаты теста будут выведены приблизительно на `335ms` времени моделирования.
|
||
|
||
<details>
|
||
<summary>11. Прочти меня после успешного завершения моделирования </summary>
|
||
|
||
Итак, вы получили сообщение вида:
|
||
|
||
```text
|
||
CoreMark Size : 666
|
||
Total ticks : 2574834
|
||
Total time (secs): 0.257483
|
||
Iterations/Sec : 3.883746
|
||
ERROR! Must execute for at least 10 secs for a valid result!
|
||
Iterations : 1
|
||
Compiler version : GCC13.2.0
|
||
Compiler flags : -march=rv32i_zicsr -mabi=ilp32
|
||
Memory location : STACK
|
||
seedcrc : 0x29f4
|
||
[0]crclist : 0x7704
|
||
[0]crcmatrix : 0x1fd7
|
||
[0]crcstate : 0x8e3a
|
||
[0]crcfinal : 0x7704
|
||
Correct operation validated. See README.md for run and reporting rules.
|
||
```
|
||
|
||
Не обращайте внимание на строчку "ERROR! Must execute for at least 10 secs for a valid result!". Программа считает, что для корректных результатов, необходимо крутить ее по кругу в течении минимум 10 секунд, однако по большей части это требование необходимо для более достоверного результата у систем с кэшем/предсказателями переходов и прочими блоками, которые могут изменить количество тактов на прохождение между итерациями. Наш однотактный процессор будет вести себя одинаково на каждом круге, поэтому нет смысла в дополнительном времени моделирования.
|
||
|
||
Нас интересует строка:
|
||
|
||
```text
|
||
Iterations/Sec : 3.883746
|
||
```
|
||
|
||
Это и есть так называемый "кормарк" — метрика данной программы. Результат нашего процессора: 3.88 кормарка.
|
||
|
||
Обычно, для сравнения между собой нескольких реализаций микроархитектур, более достоверной считается величина "кормарк / МГц", т.е. значение кормарка, поделённое на тактовую частоту процессора. Дело в том, что можно реализовать какую-нибудь очень сложную архитектуру, которая будет выдавать очень хороший кормарк, но при этом будет иметь очень низкую частоту. Более того, при сравнении с другими результатами, необходимо учитывать флаги оптимизации, которые использовались при компиляции программы, поскольку они также влияют на результат.
|
||
|
||
Мы не будем уходить в дебри темных паттернов маркетинга и вместо этого будет оценивать производительность в лоб: сколько кормарков в секунду смог прогнать наш процессор в сравнении с представленными результатами других систем вне зависимости от их оптимизаций.
|
||
|
||
Таблица опубликованных результатов находится по адресу: [https://www.eembc.org/coremark/scores.php](https://www.eembc.org/coremark/scores.php). Нам необходимо отсортировать эту таблицу по столбцу `CoreMark`, кликнув по нему.
|
||
|
||
Мы получим следующий расклад:
|
||
|
||

|
||
|
||
На что мы можем обратить внимание? Ну, во-первых, мы видим, что ближайший к нам микроконтроллер по кормарку — это `ATmega2560` с результатом `4.25` кормарка. Т.е. наш процессор по производительности схож с микроконтроллерами Arduino.
|
||
|
||
Есть ли здесь еще что-нибудь интересное? Посмотрим в верх таблицы, мы можем увидеть производителя Intel с их микропроцессором [Intel 80286](https://ru.wikipedia.org/wiki/Intel_80286). Как написано на вики, данный микропроцессор был в 3-6 раз производительней [Intel 8086](https://ru.wikipedia.org/wiki/Intel_8086), который соперничал по производительности с процессором [Zilog Z80](https://en.wikipedia.org/wiki/Zilog_Z80), который устанавливался в домашний компьютер [TRS-80](https://en.wikipedia.org/wiki/TRS-80).
|
||
|
||
А знаете, с чем был сопоставим по производительности компьютер TRS-80? С бортовым компьютером [Apollo Guidance Computer](https://en.wikipedia.org/wiki/Apollo_Guidance_Computer), который проводил вычисления и контролировал движение, навигацию, управлял командным и лунным модулями в ходе полётов по программе Аполлон.
|
||
|
||
Иными словами, мы разработали процессор, который приблизительно в 7-14 раз производительнее компьютера, управлявшего полетом космического корабля, который доставил человека на Луну!
|
||
|
||
Можно ли как-то улучшить наш результат? Безусловно. Мы можем улучшить его примерно на 5% изменив буквально одну строчку. Дело в том, что для простоты реализации, мы генерировали сигнал `stall` для каждой операции обращения в память. Однако приостанавливать работу процессора было необходимо только для операций чтения из памяти. Если не генерировать сигнал `stall` для операций типа `store`, мы уменьшим время, необходимое на исполнение бенчмарка. Попробуйте сделать это сами.
|
||
|
||
Добавление умножителей, конвейеризация и множество других потенциальных улучшений увеличат производительность в разы.
|
||
|
||
Но это, как говорится, уже другая история.
|
||
|
||
</details>
|