benchmark-coremark

介绍coremark。

coremark简介

该标准于2009年由EEMBC组织的Shay Gla-On提出,并且试图将其发展成为工业标准,从而代替陈旧的Dhrystone标准。代码使用C语言写成,包含如下的运算法则:列举(寻找并排序),数学矩阵操作(普通矩阵运算)和状态机(用来确定输入流中是否包含有效数字),最后还包括CRC(循环冗余校验)。

coremark移植

coremark源码:

1
https://github.com/eembc/coremark

riscv移植源码:

1
https://github.com/riscv-boom/riscv-coremark

Coremark的代码主要分为两部分,一部分是不能修改的程序主体,另一部分是为了不同平台移植的代码。所以移植的工作主要体现在子目录下的core_portme文件中。

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
├── barebones	         --移植到裸机环境下需要修改的目录
│ ├── core_portme.c --移植的目标平台配置信息
│ ├── core_portme.h --计时以及板级初始化实现
│ ├── core_portme.mak --该子目录的makefile
│ ├── cvt.c
│ └── ee_printf.c --打印函数串口发送实现
├── core_list_join.c --列表操作程序
├── core_main.c --主程序
├── coremark.h --项目配置与数据结构的定义头文件
├── coremark.md5
├── core_matrix.c --矩阵运算程序
├── core_state.c --状态机控制程序
├── core_util.c --CRC计算程序
├── cygwin --x86 cygwin和gcc 3.4(四核,双核和单核系统)的测试代码
│ ├── core_portme.c
│ ├── core_portme.h
│ └── core_portme.mak
├── freebsd --以下同理,是在不同操作系统下的测试代码
│ ├── ...
├── LICENSE.md
├── linux
│ ├── ...
├── linux64
│ ├── ...
├── macos
│ ├── ...
├── Makefile
├── README.md --自述文件,CoreMark项目的基本介绍
├── rtems
│ ├── ...
└── simple
├── ...
└──

baremetal

  • 修改core_portme.c

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
       // Defined for RISCV
    #define NSECS_PER_SEC 1000000000 // TODO: What freq are we assuming?
    #define EE_TIMER_TICKER_RATE 1000 // TODO: What is this?
    #define CORETIMETYPE clock_t
    #define read_csr(reg) ({ unsigned long __tmp; \
    asm volatile ("csrr %0, " #reg : "=r"(__tmp)); \
    __tmp; })
    #define GETMYTIME(_t) (*_t=read_csr(cycle))
    #define MYTIMEDIFF(fin,ini) ((fin)-(ini))
    #define TIMER_RES_DIVIDER 1
    #define SAMPLE_TIME_IMPLEMENTATION 1

    ===>

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
       // Defined for RISCV
    #define NSECS_PER_SEC 1000000 // 假设频率为1MHz
    #define EE_TIMER_TICKER_RATE 1000 // 这个没有用到,不用关心
    #define CORETIMETYPE clock_t
    #define read_csr(reg) ({ unsigned long __tmp; \
    asm volatile ("csrr %0, " #reg : "=r"(__tmp)); \
    __tmp; })
    #define GETMYTIME(_t) (*_t=read_csr(cycle))
    #define MYTIMEDIFF(fin,ini) ((fin)-(ini))
    #define TIMER_RES_DIVIDER 1
    #define SAMPLE_TIME_IMPLEMENTATION 1
  • 修改core_portme.h

    1
    typedef unsigned int ee_u32;

    ===>

    1
    typedef signed int ee_u32; // 由于riscv编译器的原因,改为signed会使指令数更少,从而性能更高
  • 修改core_portme.mak

    1
    2
    3
    RISCVTOOLS=$(RISCV)  // 修改编译器
    PORT_CFLAGS = -O2 -mcmodel=medany -static -std=gnu99 -fno-common -nostdlib -nostartfiles -lm -lgcc -T $(PORT_DIR)/link.ld // 可以修改编译选项
    ITERATIONS=10 // 一般baremetal跑,跑10次就够了
  • 修改syscalls.c

    增加 %f 的支持

    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
    #define CIFRAS 10E3
    static void vprintfmt(void (*putch)(int, void**), void **putdat, const char *fmt, va_list ap)
    {
    register const char* p;
    const char* last_fmt;
    register int ch, err;
    unsigned long long num;
    int base, lflag, width, precision, altflag;
    char padc;

    double numfloat; // support %f
    double f_tmp; // support %f

    .....
    case 'f':
    numfloat = va_arg(ap, double);
    f_tmp = numfloat - ((int)numfloat);
    if(f_tmp>=0.1)
    printf("%d.%d",(int)numfloat, (int)((numfloat-((int)numfloat))*CIFRAS));
    else if(f_tmp>=0.01)
    printf("%d.0%d",(int)numfloat, (int)((numfloat-((int)numfloat))*CIFRAS));
    else if(f_tmp>=0.001)
    printf("%d.00%d",(int)numfloat, (int)((numfloat-((int)numfloat))*CIFRAS));
    else if(f_tmp>=0.0001)
    printf("%d.000%d",(int)numfloat, (int)((numfloat-((int)numfloat))*CIFRAS));
    else
    printf("%d.0000",(int)numfloat);
    break;
    .....
  • 编译命令

    1
    make PORT_DIR=../riscv64-baremetal compile

linux

在linux环境下跑的只需要修改下真实频率,和编译参数,就可以直接跑了。

  • 修改core_portme.c

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
       // Defined for RISCV
    #define NSECS_PER_SEC 1000000000 // 修改为真实频率
    #define EE_TIMER_TICKER_RATE 1000 // TODO: What is this?
    #define CORETIMETYPE clock_t
    #define read_csr(reg) ({ unsigned long __tmp; \
    asm volatile ("csrr %0, " #reg : "=r"(__tmp)); \
    __tmp; })
    #define GETMYTIME(_t) (*_t=read_csr(cycle))
    #define MYTIMEDIFF(fin,ini) ((fin)-(ini))
    #define TIMER_RES_DIVIDER 1
    #define SAMPLE_TIME_IMPLEMENTATION 1
  • 编译命令

    1
    make PORT_DIR=../riscv64 compile

coremark分数

coremark的分数含义是:一定时间内跑了多少次coremark。单位是coremark/MHz

在移植完之后,运行coremark可以得到如下信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2K performance run parameters for coremark.
CoreMark Size : 666
Total ticks : 16415985776
Total time (secs): 16.415986
Iterations/Sec : 6700.785533
Iterations : 110000
Compiler version : GCC10.2.0
Compiler flags : -march=rv64imafdc_zba_zbb_zbc -mabi=lp64d -O3 -fno-common -funroll-loops -finline-functions -funroll-all-loops -falign-jumps=8 -falign-loops=8 -finline-limit=1000 -falign-functions=8 -ffast-math -fno-tree-loop-distribute-patterns --param fsm-scale-path-stmts=3 -mcmodel=medany -fno-builtin-pritnf
Memory location : Please put data memory location here
(e.g. code in flash, data on heap etc)
seedcrc : 0xe9f5
[0]crclist : 0xe714
[0]crcmatrix : 0x1fd7
[0]crcstate : 0x8e3a
[0]crcfinal : 0x33ff
Correct operation validated. See README.md for run and reporting rules.
CoreMark 1.0 : 6700.785533 / GCC10.2.0 -march=rv64imafdc_zba_zbb_zbc -mabi=lp64d -O3 -fno-common -funroll-loops -finline-functions -funroll-all-loops -falign-jumps=8 -falign-loops=8 -finline-limit=1000 -falign-functions=8 -ffast-math -fno-tree-loop-distribute-patterns --param fsm-scale-path-stmts=3 -mcmodel=medany -fno-builtin-pritnf / Heap

从上面Log信息可以看出:

  • CPU频率是1000MHz
  • 跑了16秒,每秒跑了6700次coremark
  • 所以最后的得分是:6700/1000 = 6.7 Coremark/MHz

可以看出,最后的得分和频率没有什么关系,所以在仿真阶段,一般可以把NSECS_PER_SEC 设置为 1000000,也就是1MHz,然后得到的Iteration/sec 就是最后的分数了。

也就是说coremark的分数,基本和IPC是正比关系了。

根据经验,在riscv架构下,coremark iteration一次,指令数大概25W左右,当IPC1.7 左右,coremark分数大概 6.7 左右。