2018年8月18日土曜日

Linux 上でメモリ帯域幅をベンチマーク

Linux でメモリ帯域幅の確認(計測)を行いたいと思って調べてみると、stream というベンチマークツールが在ることを知りました。
http://www.cs.virginia.edu/stream/
ダウンロードして、gcc でコンパイルするだけで利用できます。計測結果は次のような感じ。
[root@hoge Downloads]# ./stream_c.exe 
-------------------------------------------------------------
STREAM version $Revision: 5.10 $
-------------------------------------------------------------
This system uses 8 bytes per array element.
-------------------------------------------------------------
Array size = 10000000 (elements), Offset = 0 (elements)
Memory per array = 76.3 MiB (= 0.1 GiB).
Total memory required = 228.9 MiB (= 0.2 GiB).
Each kernel will be executed 10 times.
 The *best* time for each kernel (excluding the first iteration)
 will be used to compute the reported bandwidth.
-------------------------------------------------------------
Your clock granularity/precision appears to be 1 microseconds.
Each test below will take on the order of 43741 microseconds.
   (= 43741 clock ticks)
Increase the size of the arrays if this shows that
you are not getting at least 20 clock ticks per test.
-------------------------------------------------------------
WARNING -- The above is only a rough guideline.
For best results, please be sure you know the
precision of your system timer.
-------------------------------------------------------------
Function    Best Rate MB/s  Avg time     Min time     Max time
Copy:            2966.1     0.054175     0.053943     0.054633
Scale:           2920.8     0.054945     0.054779     0.055157
Add:             3518.2     0.068352     0.068217     0.068576
Triad:           3520.2     0.072915     0.068177     0.105948
-------------------------------------------------------------
Solution Validates: avg error less than 1.000000e-13 on all three arrays
-------------------------------------------------------------
実行環境は ThinkPad X301 + CentOS6 x86_64 です。DIMM の種類は DDR3-800 です。

いろいろなマシンで計ることを考えると、標準のツールで代用できるといいので、dd を使ってページキャッシュから読み出す方法を試してみました。
[root@hoge ~]# ls -l test.file 
-rw-r--r-- 1 root root 1024000000 Aug 18 19:05 test.file    ※予め用意しておいたテスト用ファイル
[root@hoge ~]# ls -lh test.file 
-rw-r--r-- 1 root root 977M Aug 18 19:05 test.file
[root@hoge ~]# dd if=test.file of=/dev/null bs=4k
250000+0 records in
250000+0 records out
1024000000 bytes (1.0 GB) copied, 3.89807 s, 263 MB/s    ※1回目はキャッシュに載ってないのでSSDの計測になる

[root@hoge ~]# dd if=test.file of=/dev/null bs=4k
250000+0 records in
250000+0 records out
1024000000 bytes (1.0 GB) copied, 0.558606 s, 1.8 GB/s
[root@hoge ~]# dd if=test.file of=/dev/null bs=4k
250000+0 records in
250000+0 records out
1024000000 bytes (1.0 GB) copied, 0.492317 s, 2.1 GB/s
[root@hoge ~]# dd if=test.file of=/dev/null bs=4k
250000+0 records in
250000+0 records out
1024000000 bytes (1.0 GB) copied, 0.488601 s, 2.1 GB/s

[root@hoge ~]# dd if=test.file of=/dev/null bs=8k    ※以降、ブロックサイズを2倍にして、3回づつ計測
125000+0 records in
125000+0 records out
1024000000 bytes (1.0 GB) copied, 0.39059 s, 2.6 GB/s
[root@hoge ~]# dd if=test.file of=/dev/null bs=8k
125000+0 records in
125000+0 records out
1024000000 bytes (1.0 GB) copied, 0.387988 s, 2.6 GB/s
[root@hoge ~]# dd if=test.file of=/dev/null bs=8k
125000+0 records in
125000+0 records out
1024000000 bytes (1.0 GB) copied, 0.387874 s, 2.6 GB/s

[root@hoge ~]# dd if=test.file of=/dev/null bs=16k
62500+0 records in
62500+0 records out
1024000000 bytes (1.0 GB) copied, 0.3597 s, 2.8 GB/s
[root@hoge ~]# dd if=test.file of=/dev/null bs=16k
62500+0 records in
62500+0 records out
1024000000 bytes (1.0 GB) copied, 0.359784 s, 2.8 GB/s
[root@hoge ~]# dd if=test.file of=/dev/null bs=16k
62500+0 records in
62500+0 records out
1024000000 bytes (1.0 GB) copied, 0.358306 s, 2.9 GB/s

[root@hoge ~]# dd if=test.file of=/dev/null bs=32k
31250+0 records in
31250+0 records out
1024000000 bytes (1.0 GB) copied, 0.343135 s, 3.0 GB/s
[root@hoge ~]# dd if=test.file of=/dev/null bs=32k
31250+0 records in
31250+0 records out
1024000000 bytes (1.0 GB) copied, 0.342479 s, 3.0 GB/s
[root@hoge ~]# dd if=test.file of=/dev/null bs=32k
31250+0 records in
31250+0 records out
1024000000 bytes (1.0 GB) copied, 0.342495 s, 3.0 GB/s

[root@hoge ~]# dd if=test.file of=/dev/null bs=64k
15625+0 records in
15625+0 records out
1024000000 bytes (1.0 GB) copied, 0.32722 s, 3.1 GB/s
[root@hoge ~]# dd if=test.file of=/dev/null bs=64k
15625+0 records in
15625+0 records out
1024000000 bytes (1.0 GB) copied, 0.327119 s, 3.1 GB/s
[root@hoge ~]# dd if=test.file of=/dev/null bs=64k
15625+0 records in
15625+0 records out
1024000000 bytes (1.0 GB) copied, 0.328853 s, 3.1 GB/s

[root@hoge ~]# dd if=test.file of=/dev/null bs=128k
7812+1 records in
7812+1 records out
1024000000 bytes (1.0 GB) copied, 0.319119 s, 3.2 GB/s
[root@hoge ~]# dd if=test.file of=/dev/null bs=128k
7812+1 records in
7812+1 records out
1024000000 bytes (1.0 GB) copied, 0.320496 s, 3.2 GB/s
[root@hoge ~]# dd if=test.file of=/dev/null bs=128k
7812+1 records in
7812+1 records out
1024000000 bytes (1.0 GB) copied, 0.325546 s, 3.1 GB/s

[root@hoge ~]# dd if=test.file of=/dev/null bs=256k
3906+1 records in
3906+1 records out
1024000000 bytes (1.0 GB) copied, 0.314302 s, 3.3 GB/s
[root@hoge ~]# dd if=test.file of=/dev/null bs=256k
3906+1 records in
3906+1 records out
1024000000 bytes (1.0 GB) copied, 0.315943 s, 3.2 GB/s
[root@hoge ~]# dd if=test.file of=/dev/null bs=256k
3906+1 records in
3906+1 records out
1024000000 bytes (1.0 GB) copied, 0.316061 s, 3.2 GB/s

[root@hoge ~]# dd if=test.file of=/dev/null bs=512k
1953+1 records in
1953+1 records out
1024000000 bytes (1.0 GB) copied, 0.313515 s, 3.3 GB/s
[root@hoge ~]# dd if=test.file of=/dev/null bs=512k
1953+1 records in
1953+1 records out
1024000000 bytes (1.0 GB) copied, 0.315126 s, 3.2 GB/s
[root@hoge ~]# dd if=test.file of=/dev/null bs=512k
1953+1 records in
1953+1 records out
1024000000 bytes (1.0 GB) copied, 0.316339 s, 3.2 GB/s

[root@hoge ~]# dd if=test.file of=/dev/null bs=1024k
976+1 records in
976+1 records out
1024000000 bytes (1.0 GB) copied, 0.343852 s, 3.0 GB/s
[root@hoge ~]# dd if=test.file of=/dev/null bs=1024k
976+1 records in
976+1 records out
1024000000 bytes (1.0 GB) copied, 0.338554 s, 3.0 GB/s
[root@hoge ~]# dd if=test.file of=/dev/null bs=1024k
976+1 records in
976+1 records out
1024000000 bytes (1.0 GB) copied, 0.342826 s, 3.0 GB/s

[root@hoge ~]# dd if=test.file of=/dev/null bs=2048k
488+1 records in
488+1 records out
1024000000 bytes (1.0 GB) copied, 0.674414 s, 1.5 GB/s
[root@hoge ~]# dd if=test.file of=/dev/null bs=2048k
488+1 records in
488+1 records out
1024000000 bytes (1.0 GB) copied, 0.68936 s, 1.5 GB/s
[root@hoge ~]# dd if=test.file of=/dev/null bs=2048k
488+1 records in
488+1 records out
1024000000 bytes (1.0 GB) copied, 0.677181 s, 1.5 GB/s

[root@hoge ~]# dd if=test.file of=/dev/null bs=4096k
244+1 records in
244+1 records out
1024000000 bytes (1.0 GB) copied, 0.773668 s, 1.3 GB/s
[root@hoge ~]# dd if=test.file of=/dev/null bs=4096k
244+1 records in
244+1 records out
1024000000 bytes (1.0 GB) copied, 0.76228 s, 1.3 GB/s
[root@hoge ~]# dd if=test.file of=/dev/null bs=4096k
244+1 records in
244+1 records out
1024000000 bytes (1.0 GB) copied, 0.754367 s, 1.4 GB/s
[root@hoge ~]# 
このように、ブロックサイズ (bs) が小さすぎてはダメですが、かと言って、大きすぎてもダメでした。実験は大事ですね。CPU のキャッシュの影響かな? おそらくは
この方法だと、read システムコールの延長のカーネル内コードで、ページキャッシュ上のデータが、ユーザ空間にコピーされる動きになるはずです。/dev/null への write システムコールが余計ですが、カーネル内では何もせず(コピー処理などはなし)にリターンするので、ほぼ無視できるかと思います。/dev/null に対応するコードは、drivers/char/mem.c の中です。

別のやり方(/dev/zero を読み出す方法)だと、次のように stream と乖離した値が見えます。
[root@hoge ~]# dd if=/dev/zero of=/dev/null bs=512k count=10000
10000+0 records in
10000+0 records out
5242880000 bytes (5.2 GB) copied, 0.738394 s, 7.1 GB/s
[root@hoge ~]# 
/dev/zero を read する際のカーネル内コードは、次のとおりです。メモリ間のコピーではなく、レジスタ上の値(ゼロ)をメモリへ転送(ストア)する繰り返し処理になってます。
    677 static ssize_t read_zero(struct file * file, char __user * buf,
    678                          size_t count, loff_t *ppos)
    679 {
    680         size_t written;
    681 
    682         if (!count)
    683                 return 0;
    684 
    685         if (!access_ok(VERIFY_WRITE, buf, count))
    686                 return -EFAULT;
    687 
    688         written = 0;
    689         while (count) {
    690                 unsigned long unwritten;
    691                 size_t chunk = count;
    692 
    693                 if (chunk > PAGE_SIZE)
    694                         chunk = PAGE_SIZE;      /* Just for latency reasons */
    695                 unwritten = __clear_user(buf, chunk);
    696                 written += chunk - unwritten;
    697                 if (unwritten)
    698                         break;
    699                 if (signal_pending(current))
    700                         return written ? written : -ERESTARTSYS;
    701                 buf += chunk;
    702                 count -= chunk;
    703                 cond_resched();
    704         }
    705         return written ? written : -EFAULT;
    706 }
"drivers/char/mem.c"

     60 /*
     61  * Zero Userspace
     62  */
     63 
     64 unsigned long __clear_user(void __user *addr, unsigned long size)
     65 {
     66         long __d0;
     67         might_fault();
     68         /* no memory constraint because it doesn't change any memory gcc knows
     69            about */
     70         asm volatile(
     71                 "       testq  %[size8],%[size8]\n"
     72                 "       jz     4f\n"
     73                 "0:     movq %[zero],(%[dst])\n"
     74                 "       addq   %[eight],%[dst]\n"
     75                 "       decl %%ecx ; jnz   0b\n"
     76                 "4:     movq  %[size1],%%rcx\n"
     77                 "       testl %%ecx,%%ecx\n"
     78                 "       jz     2f\n"
     79                 "1:     movb   %b[zero],(%[dst])\n"
     80                 "       incq   %[dst]\n"
     81                 "       decl %%ecx ; jnz  1b\n"
     82                 "2:\n"
     83                 ".section .fixup,\"ax\"\n"
     84                 "3:     lea 0(%[size1],%[size8],8),%[size8]\n"
     85                 "       jmp 2b\n"
     86                 ".previous\n"
     87                 _ASM_EXTABLE(0b,3b)
     88                 _ASM_EXTABLE(1b,2b)
     89                 : [size8] "=&c"(size), [dst] "=&D" (__d0)
     90                 : [size1] "r"(size & 7), "[size8]" (size / 8), "[dst]"(addr),
     91                   [zero] "r" (0UL), [eight] "r" (8UL));
     92         return size;
     93 }
     94 EXPORT_SYMBOL(__clear_user);
"arch/x86/lib/usercopy_64.c"

なお、MB/s の MB は、stream も dd も 1000*1000 単位で計算されています。GB/s も同様。1024*1024単位ではないか?と迷うことがあるので、備忘録。

2018年8月4日土曜日

7z で .lzh を解凍する

姫野ベンチ を使おうとして、ソースをダウンロードしたのですが、どういう意図なのか zip の中に lzh ファイルが入っているという二重包装状態でした。
[root@hoge tmp]# ls -l *.zip
-rw-r--r--. 1 root root 2729  8月  3 00:01 cc_himenobmtxp_m.zip
[root@hoge tmp]# unzip cc_himenobmtxp_m.zip 
Archive:  cc_himenobmtxp_m.zip
 extracting: cc_himenobmtxp_m.lzh    
[root@hoge tmp]# ls -l cc_hime*
-rw-r--r--. 1 root root 2555  6月 10  2015 cc_himenobmtxp_m.lzh
-rw-r--r--. 1 root root 2729  8月  3 00:01 cc_himenobmtxp_m.zip
lzh なんて久しぶりに見ました。解凍は lha でできるとは思いましたが、いまどき lha なんて収録されてませんでした。環境は Fedora です。
[root@hoge tmp]# dnf install lha
メタデータの期限切れの確認は、0:52:24 時間前の 2018年08月04日 07時58分59秒 に実施しました。
一致した引数がありません: lha
エラー: 一致するものが見つかりません
他に頭に浮かんだのは 7z コマンド。しかし、man を見ても lzh の記述はなく、ダメ元で試してみたのですが、解凍できました。
[root@hoge tmp]# 7z x cc_himenobmtxp_m.lzh 

7-Zip [64] 16.02 : Copyright (c) 1999-2016 Igor Pavlov : 2016-05-21
p7zip Version 16.02 (locale=ja_JP.UTF-8,Utf16=on,HugeFiles=on,64 bits,4 CPUs Intel(R) Core(TM) i7-7500U CPU @ 2.70GHz (806E9),ASM,AES-NI)

Scanning the drive for archives:
1 file, 2555 bytes (3 KiB)

Extracting archive: cc_himenobmtxp_m.lzh
--
Path = cc_himenobmtxp_m.lzh
Type = Lzh
Physical Size = 2555

Everything is Ok

Files: 2
Size:       7237
Compressed: 2555
[root@hoge tmp]# ls -l *.[ch] Makefile 
-rw-r--r--. 1 root root  237  2月 21  2002 Makefile
-rw-r--r--. 1 root root 7000  2月 21  2002 himenoBMTxps.c
以上、自分と誰かのために備忘録でした。

2018年8月3日金曜日

likwid で 姫野ベンチ を計測してみた

likwid 、姫野ベンチ の導入は make するだけなので、省略します。
[root@hoge Downloads]# likwid-perfctr -C 1 -g FLOPS_SP ./bmt 
--------------------------------------------------------------------------------
CPU name: Intel(R) Core(TM) i7-7500U CPU @ 2.70GHz
CPU type: Intel Kabylake processor
CPU clock: 2.90 GHz
--------------------------------------------------------------------------------
mimax = 129 mjmax = 129 mkmax = 257
imax = 128 jmax = 128 kmax =256
 Start rehearsal measurement process.
 Measure the performance in 3 times.

 MFLOPS: 5008.406795 time(s): 0.082125 1.733593e-03

 Now, start the actual measurement process.
 The loop will be excuted in 2191 times
 This will take about one minute.
 Wait for a while

 Loop executed for 2191 times
 Gosa : 4.477886e-04 
 MFLOPS measured : 6004.316730 cpu : 50.030231
 Score based on Pentium III 600MHz : 73.223375
--------------------------------------------------------------------------------
Group 1: FLOPS_SP
+------------------------------------------+---------+--------------+
|                   Event                  | Counter |    Core 1    |
+------------------------------------------+---------+--------------+
|             INSTR_RETIRED_ANY            |  FIXC0  | 244033624741 |
|           CPU_CLK_UNHALTED_CORE          |  FIXC1  | 173945548078 |
|           CPU_CLK_UNHALTED_REF           |  FIXC2  | 144661450175 |
| FP_ARITH_INST_RETIRED_128B_PACKED_SINGLE |   PMC0  |  72415611576 |
|    FP_ARITH_INST_RETIRED_SCALAR_SINGLE   |   PMC1  |  11146222208 |
| FP_ARITH_INST_RETIRED_256B_PACKED_SINGLE |   PMC2  |            0 |
+------------------------------------------+---------+--------------+

+----------------------+-----------+
|        Metric        |   Core 1  |
+----------------------+-----------+
|  Runtime (RDTSC) [s] |   50.2231 |
| Runtime unhalted [s] |   59.9028 |
|      Clock [MHz]     | 3491.6156 |
|          CPI         |    0.7128 |
|      SP MFLOP/s      | 5989.4533 |
|    AVX SP MFLOP/s    |         0 |
|    Packed MUOPS/s    | 1441.8797 |
|    Scalar MUOPS/s    |  221.9344 |
|  Vectorization ratio |   86.6611 |
+----------------------+-----------+
このように、ちゃんと近い値が出ました。-g オプションで指定する group name は、-a オプションで表示できます。
[root@hoge Downloads]# likwid-perfctr -a
 Group name Description
--------------------------------------------------------------------------------
       DATA Load to store ratio
 UOPS_ISSUE UOPs issueing
  TLB_INSTR L1 Instruction TLB miss rate/ratio
   TLB_DATA L2 data TLB miss rate/ratio
  FLOPS_AVX Packed AVX MFLOP/s
UOPS_RETIRE UOPs retirement
     BRANCH Branch prediction miss rate/ratio
  UOPS_EXEC UOPs execution
     ICACHE Instruction cache miss rate/ratio
       UOPS UOPs execution info
         L3 L3 cache bandwidth in MBytes/s
   FLOPS_SP Single Precision MFLOP/s
    L2CACHE L2 cache miss rate/ratio
   RECOVERY Recovery duration
    L3CACHE L3 cache miss rate/ratio
         L2 L2 cache bandwidth in MBytes/s
CYCLE_ACTIVITY Cycle Activities
     ENERGY Power and Energy consumption
FALSE_SHARE False sharing
      CLOCK Power and Energy consumption
   FLOPS_DP Double Precision MFLOP/s
なお、姫野ベンチ(static M)のソースを grep するとわかりますが、float で計算しているので、FLOPS_DP ではなく FLOPS_SP を使いました。
[root@hoge Downloads]# grep -w float himenoBMTxps.c 
float jacobi();
static float  p[MIMAX][MJMAX][MKMAX];
static float  a[4][MIMAX][MJMAX][MKMAX],
static float  bnd[MIMAX][MJMAX][MKMAX];
static float  wrk1[MIMAX][MJMAX][MKMAX],
static float omega;
  float  gosa;
        p[i][j][k]=(float)(i*i)/(float)((imax-1)*(imax-1));
float
  float gosa, s0, ss;
人気ブログランキングへ にほんブログ村 IT技術ブログへ