forked from masonr/yet-another-bench-script
-
Notifications
You must be signed in to change notification settings - Fork 0
/
yabs.sh
executable file
·804 lines (714 loc) · 33.9 KB
/
yabs.sh
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
#!/bin/bash
# Yet Another Bench Script by Mason Rowe
# Initial Oct 2019; Last update June 2021
#
# Disclaimer: This project is a work in progress. Any errors or suggestions should be
# relayed to me via the GitHub project page linked below.
#
# Purpose: The purpose of this script is to quickly gauge the performance of a Linux-
# based server by benchmarking network performance via iperf3, CPU and
# overall system performance via Geekbench 4/5, and random disk
# performance via fio. The script is designed to not require any dependencies
# - either compiled or installed - nor admin privileges to run.
#
echo -e '# ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## #'
echo -e '# Yet-Another-Bench-Script #'
echo -e '# v2021-06-05 #'
echo -e '# https://github.com/masonr/yet-another-bench-script #'
echo -e '# ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## #'
echo -e
date
# override locale to eliminate parsing errors (i.e. using commas as delimiters rather than periods)
export LC_ALL=C
# determine architecture of host
ARCH=$(uname -m)
if [[ $ARCH = *x86_64* ]]; then
# host is running a 64-bit kernel
ARCH="x64"
elif [[ $ARCH = *i?86* ]]; then
# host is running a 32-bit kernel
ARCH="x86"
elif [[ $ARCH = *aarch* || $ARCH = *arm* ]]; then
KERNEL_BIT=`getconf LONG_BIT`
if [[ $KERNEL_BIT = *64* ]]; then
# host is running an ARM 64-bit kernel
ARCH="aarch64"
else
# host is running an ARM 32-bit kernel
ARCH="arm"
fi
echo -e "\nARM-compatibility is considered *experimental*"
else
# host is running a non-supported kernel
echo -e "Architecture not supported by YABS."
exit 1
fi
# flags to skip certain performance tests
unset SKIP_FIO SKIP_IPERF SKIP_GEEKBENCH PRINT_HELP REDUCE_NET GEEKBENCH_4 GEEKBENCH_5 DD_FALLBACK IPERF_DL_FAIL
GEEKBENCH_5="True" # gb5 test enabled by default
# get any arguments that were passed to the script and set the associated skip flags (if applicable)
while getopts 'fdighr49' flag; do
case "${flag}" in
f) SKIP_FIO="True" ;;
d) SKIP_FIO="True" ;;
i) SKIP_IPERF="True" ;;
g) SKIP_GEEKBENCH="True" ;;
h) PRINT_HELP="True" ;;
r) REDUCE_NET="True" ;;
4) GEEKBENCH_4="True" && unset GEEKBENCH_5 ;;
9) GEEKBENCH_4="True" && GEEKBENCH_5="True" ;;
*) exit 1 ;;
esac
done
# check for local fio/iperf installs
command -v fio >/dev/null 2>&1 && LOCAL_FIO=true || unset LOCAL_FIO
command -v iperf3 >/dev/null 2>&1 && LOCAL_IPERF=true || unset LOCAL_IPERF
# test if the host has IPv4/IPv6 connectivity
IPV4_CHECK=$((ping -4 -c 1 -W 4 ipv4.google.com >/dev/null 2>&1 && echo true) || curl -s -4 -m 4 icanhazip.com 2> /dev/null)
IPV6_CHECK=$((ping -6 -c 1 -W 4 ipv6.google.com >/dev/null 2>&1 && echo true) || curl -s -6 -m 4 icanhazip.com 2> /dev/null)
# print help and exit script, if help flag was passed
if [ ! -z "$PRINT_HELP" ]; then
echo -e
echo -e "Usage: ./yabs.sh [-fdighr49]"
echo -e " curl -sL yabs.sh | bash"
echo -e " curl -sL yabs.sh | bash -s -- -{fdighr49}"
echo -e
echo -e "Flags:"
echo -e " -f/d : skips the fio disk benchmark test"
echo -e " -i : skips the iperf network test"
echo -e " -g : skips the geekbench performance test"
echo -e " -h : prints this lovely message, shows any flags you passed,"
echo -e " shows if fio/iperf3 local packages have been detected,"
echo -e " then exits"
echo -e " -r : reduce number of iperf3 network locations (to only three)"
echo -e " to lessen bandwidth usage"
echo -e " -4 : use geekbench 4 instead of geekbench 5"
echo -e " -9 : use both geekbench 4 AND geekbench 5"
echo -e
echo -e "Detected Arch: $ARCH"
echo -e
echo -e "Detected Flags:"
[[ ! -z $SKIP_FIO ]] && echo -e " -f/d, skipping fio disk benchmark test"
[[ ! -z $SKIP_IPERF ]] && echo -e " -i, skipping iperf network test"
[[ ! -z $SKIP_GEEKBENCH ]] && echo -e " -g, skipping geekbench test"
[[ ! -z $REDUCE_NET ]] && echo -e " -r, using reduced (3) iperf3 locations"
[[ ! -z $GEEKBENCH_4 ]] && echo -e " running geekbench 4"
[[ ! -z $GEEKBENCH_5 ]] && echo -e " running geekbench 5"
echo -e
echo -e "Local Binary Check:"
[[ -z $LOCAL_FIO ]] && echo -e " fio not detected, will download precompiled binary" ||
echo -e " fio detected, using local package"
[[ -z $LOCAL_IPERF ]] && echo -e " iperf3 not detected, will download precompiled binary" ||
echo -e " iperf3 detected, using local package"
echo -e
echo -e "Detected Connectivity:"
[[ ! -z $IPV4_CHECK ]] && echo -e " IPv4 connected" ||
echo -e " IPv4 not connected"
[[ ! -z $IPV6_CHECK ]] && echo -e " IPv6 connected" ||
echo -e " IPv6 not connected"
echo -e
echo -e "Exiting..."
exit 0
fi
# format_size
# Purpose: Formats raw disk and memory sizes from kibibytes (KiB) to largest unit
# Parameters:
# 1. RAW - the raw memory size (RAM/Swap) in kibibytes
# Returns:
# Formatted memory size in KiB, MiB, GiB, or TiB
function format_size {
RAW=$1 # mem size in KiB
RESULT=$RAW
local DENOM=1
local UNIT="KiB"
# ensure the raw value is a number, otherwise return blank
re='^[0-9]+$'
if ! [[ $RAW =~ $re ]] ; then
echo ""
return 0
fi
if [ "$RAW" -ge 1073741824 ]; then
DENOM=1073741824
UNIT="TiB"
elif [ "$RAW" -ge 1048576 ]; then
DENOM=1048576
UNIT="GiB"
elif [ "$RAW" -ge 1024 ]; then
DENOM=1024
UNIT="MiB"
fi
# divide the raw result to get the corresponding formatted result (based on determined unit)
RESULT=$(awk -v a="$RESULT" -v b="$DENOM" 'BEGIN { print a / b }')
# shorten the formatted result to two decimal places (i.e. x.x)
RESULT=$(echo $RESULT | awk -F. '{ printf "%0.1f",$1"."substr($2,1,2) }')
# concat formatted result value with units and return result
RESULT="$RESULT $UNIT"
echo $RESULT
}
# gather basic system information (inc. CPU, AES-NI/virt status, RAM + swap + disk size)
echo -e
echo -e "Basic System Information:"
echo -e "---------------------------------"
if [[ $ARCH = *aarch64* || $ARCH = *arm* ]]; then
CPU_PROC=$(lscpu | grep "Model name" | sed 's/Model name: *//g')
else
CPU_PROC=$(awk -F: '/model name/ {name=$2} END {print name}' /proc/cpuinfo | sed 's/^[ \t]*//;s/[ \t]*$//')
fi
echo -e "Processor : $CPU_PROC"
if [[ $ARCH = *aarch64* || $ARCH = *arm* ]]; then
CPU_CORES=$(lscpu | grep "^[[:blank:]]*CPU(s):" | sed 's/CPU(s): *//g')
CPU_FREQ=$(lscpu | grep "CPU max MHz" | sed 's/CPU max MHz: *//g')
[[ -z "$CPU_FREQ" ]] && CPU_FREQ="???"
CPU_FREQ="${CPU_FREQ} MHz"
else
CPU_CORES=$(awk -F: '/model name/ {core++} END {print core}' /proc/cpuinfo)
CPU_FREQ=$(awk -F: ' /cpu MHz/ {freq=$2} END {print freq " MHz"}' /proc/cpuinfo | sed 's/^[ \t]*//;s/[ \t]*$//')
fi
echo -e "CPU cores : $CPU_CORES @ $CPU_FREQ"
CPU_AES=$(cat /proc/cpuinfo | grep aes)
[[ -z "$CPU_AES" ]] && CPU_AES="\xE2\x9D\x8C Disabled" || CPU_AES="\xE2\x9C\x94 Enabled"
echo -e "AES-NI : $CPU_AES"
CPU_VIRT=$(cat /proc/cpuinfo | grep 'vmx\|svm')
[[ -z "$CPU_VIRT" ]] && CPU_VIRT="\xE2\x9D\x8C Disabled" || CPU_VIRT="\xE2\x9C\x94 Enabled"
echo -e "VM-x/AMD-V : $CPU_VIRT"
TOTAL_RAM=$(format_size $(free | awk 'NR==2 {print $2}'))
echo -e "RAM : $TOTAL_RAM"
TOTAL_SWAP=$(format_size $(free | grep Swap | awk '{ print $2 }'))
echo -e "Swap : $TOTAL_SWAP"
# total disk size is calculated by adding all partitions of the types listed below (after the -t flags)
TOTAL_DISK=$(format_size $(df -t simfs -t ext2 -t ext3 -t ext4 -t btrfs -t xfs -t vfat -t ntfs -t swap --total 2>/dev/null | grep total | awk '{ print $2 }'))
echo -e "Disk : $TOTAL_DISK"
# create a directory in the same location that the script is being run to temporarily store YABS-related files
DATE=`date -Iseconds | sed -e "s/:/_/g"`
YABS_PATH=./$DATE
touch $DATE.test 2> /dev/null
# test if the user has write permissions in the current directory and exit if not
if [ ! -f "$DATE.test" ]; then
echo -e
echo -e "You do not have write permission in this directory. Switch to an owned directory and re-run the script.\nExiting..."
exit 1
fi
rm $DATE.test
mkdir -p $YABS_PATH
# trap CTRL+C signals to exit script cleanly
trap catch_abort INT
# catch_abort
# Purpose: This method will catch CTRL+C signals in order to exit the script cleanly and remove
# yabs-related files.
function catch_abort() {
echo -e "\n** Aborting YABS. Cleaning up files...\n"
rm -rf $YABS_PATH
unset LC_ALL
exit 0
}
# format_speed
# Purpose: This method is a convienence function to format the output of the fio disk tests which
# always returns a result in KB/s. If result is >= 1 GB/s, use GB/s. If result is < 1 GB/s
# and >= 1 MB/s, then use MB/s. Otherwise, use KB/s.
# Parameters:
# 1. RAW - the raw disk speed result (in KB/s)
# Returns:
# Formatted disk speed in GB/s, MB/s, or KB/s
function format_speed {
RAW=$1 # disk speed in KB/s
RESULT=$RAW
local DENOM=1
local UNIT="KB/s"
# ensure raw value is not null, if it is, return blank
if [ -z "$RAW" ]; then
echo ""
return 0
fi
# check if disk speed >= 1 GB/s
if [ "$RAW" -ge 1000000 ]; then
DENOM=1000000
UNIT="GB/s"
# check if disk speed < 1 GB/s && >= 1 MB/s
elif [ "$RAW" -ge 1000 ]; then
DENOM=1000
UNIT="MB/s"
fi
# divide the raw result to get the corresponding formatted result (based on determined unit)
RESULT=$(awk -v a="$RESULT" -v b="$DENOM" 'BEGIN { print a / b }')
# shorten the formatted result to two decimal places (i.e. x.xx)
RESULT=$(echo $RESULT | awk -F. '{ printf "%0.2f",$1"."substr($2,1,2) }')
# concat formatted result value with units and return result
RESULT="$RESULT $UNIT"
echo $RESULT
}
# format_iops
# Purpose: This method is a convenience function to format the output of the raw IOPS result
# Parameters:
# 1. RAW - the raw IOPS result
# Returns:
# Formatted IOPS (i.e. 8, 123, 1.7k, 275.9k, etc.)
function format_iops {
RAW=$1 # iops
RESULT=$RAW
# ensure raw value is not null, if it is, return blank
if [ -z "$RAW" ]; then
echo ""
return 0
fi
# check if IOPS speed > 1k
if [ "$RAW" -ge 1000 ]; then
# divide the raw result by 1k
RESULT=$(awk -v a="$RESULT" 'BEGIN { print a / 1000 }')
# shorten the formatted result to one decimal place (i.e. x.x)
RESULT=$(echo $RESULT | awk -F. '{ printf "%0.1f",$1"."substr($2,1,1) }')
RESULT="$RESULT"k
fi
echo $RESULT
}
# disk_test
# Purpose: This method is designed to test the disk performance of the host using the partition that the
# script is being run from using fio random read/write speed tests.
# Parameters:
# - (none)
function disk_test {
if [[ "$ARCH" = "aarch64" || "$ARCH" = "arm" ]]; then
FIO_SIZE=512M
else
FIO_SIZE=2G
fi
# run a quick test to generate the fio test file to be used by the actual tests
echo -en "Generating fio test file..."
$FIO_CMD --name=setup --ioengine=libaio --rw=read --bs=64k --iodepth=64 --numjobs=2 --size=$FIO_SIZE --runtime=1 --gtod_reduce=1 --filename=$DISK_PATH/test.fio --direct=1 --minimal &> /dev/null
echo -en "\r\033[0K"
# get array of block sizes to evaluate
BLOCK_SIZES=("$@")
for BS in "${BLOCK_SIZES[@]}"; do
# run rand read/write mixed fio test with block size = $BS
echo -en "Running fio random mixed R+W disk test with $BS block size..."
DISK_TEST=$(timeout 35 $FIO_CMD --name=rand_rw_$BS --ioengine=libaio --rw=randrw --rwmixread=50 --bs=$BS --iodepth=64 --numjobs=2 --size=$FIO_SIZE --runtime=30 --gtod_reduce=1 --direct=1 --filename=$DISK_PATH/test.fio --group_reporting --minimal 2> /dev/null | grep rand_rw_$BS)
DISK_IOPS_R=$(echo $DISK_TEST | awk -F';' '{print $8}')
DISK_IOPS_W=$(echo $DISK_TEST | awk -F';' '{print $49}')
DISK_IOPS=$(format_iops $(awk -v a="$DISK_IOPS_R" -v b="$DISK_IOPS_W" 'BEGIN { print a + b }'))
DISK_IOPS_R=$(format_iops $DISK_IOPS_R)
DISK_IOPS_W=$(format_iops $DISK_IOPS_W)
DISK_TEST_R=$(echo $DISK_TEST | awk -F';' '{print $7}')
DISK_TEST_W=$(echo $DISK_TEST | awk -F';' '{print $48}')
DISK_TEST=$(format_speed $(awk -v a="$DISK_TEST_R" -v b="$DISK_TEST_W" 'BEGIN { print a + b }'))
DISK_TEST_R=$(format_speed $DISK_TEST_R)
DISK_TEST_W=$(format_speed $DISK_TEST_W)
DISK_RESULTS+=( "$DISK_TEST" "$DISK_TEST_R" "$DISK_TEST_W" "$DISK_IOPS" "$DISK_IOPS_R" "$DISK_IOPS_W" )
echo -en "\r\033[0K"
done
}
# dd_test
# Purpose: This method is invoked if the fio disk test failed. dd sequential speed tests are
# not indiciative or real-world results, however, some form of disk speed measure
# is better than nothing.
# Parameters:
# - (none)
function dd_test {
I=0
DISK_WRITE_TEST_RES=()
DISK_READ_TEST_RES=()
DISK_WRITE_TEST_AVG=0
DISK_READ_TEST_AVG=0
# run the disk speed tests (write and read) thrice over
while [ $I -lt 3 ]
do
# write test using dd, "direct" flag is used to test direct I/O for data being stored to disk
DISK_WRITE_TEST=$(dd if=/dev/zero of=$DISK_PATH/$DATE.test bs=64k count=16k oflag=direct |& grep copied | awk '{ print $(NF-1) " " $(NF)}')
VAL=$(echo $DISK_WRITE_TEST | cut -d " " -f 1)
[[ "$DISK_WRITE_TEST" == *"GB"* ]] && VAL=$(awk -v a="$VAL" 'BEGIN { print a * 1000 }')
DISK_WRITE_TEST_RES+=( "$DISK_WRITE_TEST" )
DISK_WRITE_TEST_AVG=$(awk -v a="$DISK_WRITE_TEST_AVG" -v b="$VAL" 'BEGIN { print a + b }')
# read test using dd using the 1G file written during the write test
DISK_READ_TEST=$(dd if=$DISK_PATH/$DATE.test of=/dev/null bs=8k |& grep copied | awk '{ print $(NF-1) " " $(NF)}')
VAL=$(echo $DISK_READ_TEST | cut -d " " -f 1)
[[ "$DISK_READ_TEST" == *"GB"* ]] && VAL=$(awk -v a="$VAL" 'BEGIN { print a * 1000 }')
DISK_READ_TEST_RES+=( "$DISK_READ_TEST" )
DISK_READ_TEST_AVG=$(awk -v a="$DISK_READ_TEST_AVG" -v b="$VAL" 'BEGIN { print a + b }')
I=$(( $I + 1 ))
done
# calculate the write and read speed averages using the results from the three runs
DISK_WRITE_TEST_AVG=$(awk -v a="$DISK_WRITE_TEST_AVG" 'BEGIN { print a / 3 }')
DISK_READ_TEST_AVG=$(awk -v a="$DISK_READ_TEST_AVG" 'BEGIN { print a / 3 }')
}
# check if disk performance is being tested and the host has required space (2G)
AVAIL_SPACE=`df -k . | awk 'NR==2{print $4}'`
if [[ -z "$SKIP_FIO" && "$AVAIL_SPACE" -lt 2097152 && "$ARCH" != "aarch64" && "$ARCH" != "arm" ]]; then # 2GB = 2097152KB
echo -e "\nLess than 2GB of space available. Skipping disk test..."
elif [[ -z "$SKIP_FIO" && "$AVAIL_SPACE" -lt 524288 && ("$ARCH" = "aarch64" || "$ARCH" = "arm") ]]; then # 512MB = 524288KB
echo -e "\nLess than 512MB of space available. Skipping disk test..."
# if the skip disk flag was set, skip the disk performance test, otherwise test disk performance
elif [ -z "$SKIP_FIO" ]; then
# Perform ZFS filesystem detection and determine if we have enough free space according to spa_asize_inflation
ZFSCHECK="/sys/module/zfs/parameters/spa_asize_inflation"
if [[ -f "$ZFSCHECK" ]];then
mul_spa=$((($(cat /sys/module/zfs/parameters/spa_asize_inflation)*2)))
warning=0
poss=()
for pathls in $(df -Th | awk '{print $7}' | tail -n +2)
do
if [[ "${PWD##$pathls}" != "${PWD}" ]]; then
poss+=($pathls)
fi
done
long=""
m=-1
for x in ${poss[@]}
do
if [ ${#x} -gt $m ];then
m=${#x}
long=$x
fi
done
size_b=$(df -Th | grep -w $long | grep -i zfs | awk '{print $5}' | tail -c 2 | head -c 1)
free_space=$(df -Th | grep -w $long | grep -i zfs | awk '{print $5}' | head -c -2)
if [[ $size_b == 'T' ]]; then
free_space=$(bc <<< "$free_space*1024")
size_b='G'
fi
if [[ $(df -Th | grep -w $long) == *"zfs"* ]];then
if [[ $size_b == 'G' ]]; then
if [[ $(echo "$free_space < $mul_spa" | bc) -ne 0 ]];then
warning=1
fi
else
warning=1
fi
fi
if [[ $warning -eq 1 ]];then
echo -en "\nWarning! You are running YABS on a ZFS Filesystem and your disk space is too low for the fio test. Your test results will be inaccurate. You need at least $mul_spa GB free in order to complete this test accurately. For more information, please see https://github.com/masonr/yet-another-bench-script/issues/13\n"
fi
fi
echo -en "\nPreparing system for disk tests..."
# create temp directory to store disk write/read test files
DISK_PATH=$YABS_PATH/disk
mkdir -p $DISK_PATH
if [ ! -z "$LOCAL_FIO" ]; then # local fio has been detected, use instead of pre-compiled binary
FIO_CMD=fio
else
# download fio binary
if [ ! -z "$IPV4_CHECK" ]; then # if IPv4 is enabled
curl -s -4 --connect-timeout 5 --retry 5 --retry-delay 0 https://raw.githubusercontent.com/masonr/yet-another-bench-script/master/bin/fio_$ARCH -o $DISK_PATH/fio
else # no IPv4, use IPv6 - below is necessary since raw.githubusercontent.com has no AAAA record
curl -s -6 --connect-timeout 5 --retry 5 --retry-delay 0 -k -g --header 'Host: raw.githubusercontent.com' https://[2a04:4e42::133]/masonr/yet-another-bench-script/master/bin/fio_$ARCH -o $DISK_PATH/fio
fi
if [ ! -f "$DISK_PATH/fio" ]; then # ensure fio binary download successfully
echo -en "\r\033[0K"
echo -e "Fio binary download failed. Running dd test as fallback...."
DD_FALLBACK=True
else
chmod +x $DISK_PATH/fio
FIO_CMD=$DISK_PATH/fio
fi
fi
if [ -z "$DD_FALLBACK" ]; then # if not falling back on dd tests, run fio test
echo -en "\r\033[0K"
# init global array to store disk performance values
declare -a DISK_RESULTS
# disk block sizes to evaluate
BLOCK_SIZES=( "4k" "64k" "512k" "1m" )
# execute disk performance test
disk_test "${BLOCK_SIZES[@]}"
fi
if [[ ! -z "$DD_FALLBACK" || ${#DISK_RESULTS[@]} -eq 0 ]]; then # fio download failed or test was killed or returned an error, run dd test instead
if [ -z "$DD_FALLBACK" ]; then # print error notice if ended up here due to fio error
echo -e "fio disk speed tests failed. Run manually to determine cause.\nRunning dd test as fallback..."
fi
dd_test
# format the speed averages by converting to GB/s if > 1000 MB/s
if [ $(echo $DISK_WRITE_TEST_AVG | cut -d "." -f 1) -ge 1000 ]; then
DISK_WRITE_TEST_AVG=$(awk -v a="$DISK_WRITE_TEST_AVG" 'BEGIN { print a / 1000 }')
DISK_WRITE_TEST_UNIT="GB/s"
else
DISK_WRITE_TEST_UNIT="MB/s"
fi
if [ $(echo $DISK_READ_TEST_AVG | cut -d "." -f 1) -ge 1000 ]; then
DISK_READ_TEST_AVG=$(awk -v a="$DISK_READ_TEST_AVG" 'BEGIN { print a / 1000 }')
DISK_READ_TEST_UNIT="GB/s"
else
DISK_READ_TEST_UNIT="MB/s"
fi
# print dd sequential disk speed test results
echo -e
echo -e "dd Sequential Disk Speed Tests:"
echo -e "---------------------------------"
printf "%-6s | %-6s %-4s | %-6s %-4s | %-6s %-4s | %-6s %-4s\n" "" "Test 1" "" "Test 2" "" "Test 3" "" "Avg" ""
printf "%-6s | %-6s %-4s | %-6s %-4s | %-6s %-4s | %-6s %-4s\n"
printf "%-6s | %-11s | %-11s | %-11s | %-6.2f %-4s\n" "Write" "${DISK_WRITE_TEST_RES[0]}" "${DISK_WRITE_TEST_RES[1]}" "${DISK_WRITE_TEST_RES[2]}" "${DISK_WRITE_TEST_AVG}" "${DISK_WRITE_TEST_UNIT}"
printf "%-6s | %-11s | %-11s | %-11s | %-6.2f %-4s\n" "Read" "${DISK_READ_TEST_RES[0]}" "${DISK_READ_TEST_RES[1]}" "${DISK_READ_TEST_RES[2]}" "${DISK_READ_TEST_AVG}" "${DISK_READ_TEST_UNIT}"
else # fio tests completed sucessfully, print results
DISK_RESULTS_NUM=$(expr ${#DISK_RESULTS[@]} / 6)
DISK_COUNT=0
# print disk speed test results
echo -e "fio Disk Speed Tests (Mixed R/W 50/50):"
echo -e "---------------------------------"
while [ $DISK_COUNT -lt $DISK_RESULTS_NUM ] ; do
if [ $DISK_COUNT -gt 0 ]; then printf "%-10s | %-20s | %-20s\n"; fi
printf "%-10s | %-11s %8s | %-11s %8s\n" "Block Size" "${BLOCK_SIZES[DISK_COUNT]}" "(IOPS)" "${BLOCK_SIZES[DISK_COUNT+1]}" "(IOPS)"
printf "%-10s | %-11s %8s | %-11s %8s\n" " ------" "---" "---- " "----" "---- "
printf "%-10s | %-11s %8s | %-11s %8s\n" "Read" "${DISK_RESULTS[DISK_COUNT*6+1]}" "(${DISK_RESULTS[DISK_COUNT*6+4]})" "${DISK_RESULTS[(DISK_COUNT+1)*6+1]}" "(${DISK_RESULTS[(DISK_COUNT+1)*6+4]})"
printf "%-10s | %-11s %8s | %-11s %8s\n" "Write" "${DISK_RESULTS[DISK_COUNT*6+2]}" "(${DISK_RESULTS[DISK_COUNT*6+5]})" "${DISK_RESULTS[(DISK_COUNT+1)*6+2]}" "(${DISK_RESULTS[(DISK_COUNT+1)*6+5]})"
printf "%-10s | %-11s %8s | %-11s %8s\n" "Total" "${DISK_RESULTS[DISK_COUNT*6]}" "(${DISK_RESULTS[DISK_COUNT*6+3]})" "${DISK_RESULTS[(DISK_COUNT+1)*6]}" "(${DISK_RESULTS[(DISK_COUNT+1)*6+3]})"
DISK_COUNT=$(expr $DISK_COUNT + 2)
done
fi
fi
# iperf_test
# Purpose: This method is designed to test the network performance of the host by executing an
# iperf3 test to/from the public iperf server passed to the function. Both directions
# (send and recieve) are tested.
# Parameters:
# 1. URL - URL/domain name of the iperf server
# 2. PORTS - the range of ports on which the iperf server operates
# 3. HOST - the friendly name of the iperf server host/owner
# 4. FLAGS - any flags that should be passed to the iperf command
function iperf_test {
URL=$1
PORTS=$2
HOST=$3
FLAGS=$4
# attempt the iperf send test 5 times, allowing for a slot to become available on the
# server or to throw out any bad/error results
I=1
while [ $I -le 5 ]
do
echo -en "Performing $MODE iperf3 send test to $HOST (Attempt #$I of 5)..."
# select a random iperf port from the range provided
PORT=`shuf -i $PORTS -n 1`
# run the iperf test sending data from the host to the iperf server; includes
# a timeout of 15s in case the iperf server is not responding; uses 8 parallel
# threads for the network test
IPERF_RUN_SEND="$(timeout 15 $IPERF_CMD $FLAGS -c $URL -p $PORT -P 8 2> /dev/null)"
# check if iperf exited cleanly and did not return an error
if [[ "$IPERF_RUN_SEND" == *"receiver"* && "$IPERF_RUN_SEND" != *"error"* ]]; then
# test did not result in an error, parse speed result
SPEED=$(echo "${IPERF_RUN_SEND}" | grep SUM | grep receiver | awk '{ print $6 }')
# if speed result is blank or bad (0.00), rerun, otherwise set counter to exit loop
[[ -z $SPEED || "$SPEED" == "0.00" ]] && I=$(( $I + 1 )) || I=11
else
# if iperf server is not responding, set counter to exit, otherwise increment, sleep, and rerun
[[ "$IPERF_RUN_SEND" == *"unable to connect"* ]] && I=11 || I=$(( $I + 1 )) && sleep 2
fi
echo -en "\r\033[0K"
done
# small sleep necessary to give iperf server a breather to get ready for a new test
sleep 1
# attempt the iperf recieve test 5 times, allowing for a slot to become available on
# the server or to throw out any bad/error results
J=1
while [ $J -le 5 ]
do
echo -n "Performing $MODE iperf3 recv test from $HOST (Attempt #$J of 5)..."
# select a random iperf port from the range provided
PORT=`shuf -i $PORTS -n 1`
# run the iperf test recieving data from the iperf server to the host; includes
# a timeout of 15s in case the iperf server is not responding; uses 8 parallel
# threads for the network test
IPERF_RUN_RECV="$(timeout 15 $IPERF_CMD $FLAGS -c $URL -p $PORT -P 8 -R 2> /dev/null)"
# check if iperf exited cleanly and did not return an error
if [[ "$IPERF_RUN_RECV" == *"receiver"* && "$IPERF_RUN_RECV" != *"error"* ]]; then
# test did not result in an error, parse speed result
SPEED=$(echo "${IPERF_RUN_RECV}" | grep SUM | grep receiver | awk '{ print $6 }')
# if speed result is blank or bad (0.00), rerun, otherwise set counter to exit loop
[[ -z $SPEED || "$SPEED" == "0.00" ]] && J=$(( $J + 1 )) || J=11
else
# if iperf server is not responding, set counter to exit, otherwise increment, sleep, and rerun
[[ "$IPERF_RUN_RECV" == *"unable to connect"* ]] && J=11 || J=$(( $J + 1 )) && sleep 2
fi
echo -en "\r\033[0K"
done
# parse the resulting send and recieve speed results
IPERF_SENDRESULT="$(echo "${IPERF_RUN_SEND}" | grep SUM | grep receiver)"
IPERF_RECVRESULT="$(echo "${IPERF_RUN_RECV}" | grep SUM | grep receiver)"
}
# launch_iperf
# Purpose: This method is designed to facilitate the execution of iperf network speed tests to
# each public iperf server in the iperf server locations array.
# Parameters:
# 1. MODE - indicates the type of iperf tests to run (IPv4 or IPv6)
function launch_iperf {
MODE=$1
[[ "$MODE" == *"IPv6"* ]] && IPERF_FLAGS="-6" || IPERF_FLAGS="-4"
# print iperf3 network speed results as they are completed
echo -e
echo -e "iperf3 Network Speed Tests ($MODE):"
echo -e "---------------------------------"
printf "%-15s | %-25s | %-15s | %-15s\n" "Provider" "Location (Link)" "Send Speed" "Recv Speed"
printf "%-15s | %-25s | %-15s | %-15s\n"
# loop through iperf locations array to run iperf test using each public iperf server
for (( i = 0; i < IPERF_LOCS_NUM; i++ )); do
# test if the current iperf location supports the network mode being tested (IPv4/IPv6)
if [[ "${IPERF_LOCS[i*5+4]}" == *"$MODE"* ]]; then
# call the iperf_test function passing the required parameters
iperf_test "${IPERF_LOCS[i*5]}" "${IPERF_LOCS[i*5+1]}" "${IPERF_LOCS[i*5+2]}" "$IPERF_FLAGS"
# parse the send and recieve speed results
IPERF_SENDRESULT_VAL=$(echo $IPERF_SENDRESULT | awk '{ print $6 }')
IPERF_SENDRESULT_UNIT=$(echo $IPERF_SENDRESULT | awk '{ print $7 }')
IPERF_RECVRESULT_VAL=$(echo $IPERF_RECVRESULT | awk '{ print $6 }')
IPERF_RECVRESULT_UNIT=$(echo $IPERF_RECVRESULT | awk '{ print $7 }')
# if the results are blank, then the server is "busy" and being overutilized
[[ -z $IPERF_SENDRESULT_VAL || "$IPERF_SENDRESULT_VAL" == *"0.00"* ]] && IPERF_SENDRESULT_VAL="busy" && IPERF_SENDRESULT_UNIT=""
[[ -z $IPERF_RECVRESULT_VAL || "$IPERF_RECVRESULT_VAL" == *"0.00"* ]] && IPERF_RECVRESULT_VAL="busy" && IPERF_RECVRESULT_UNIT=""
# print the speed results for the iperf location currently being evaluated
printf "%-15s | %-25s | %-15s | %-15s\n" "${IPERF_LOCS[i*5+2]}" "${IPERF_LOCS[i*5+3]}" "$IPERF_SENDRESULT_VAL $IPERF_SENDRESULT_UNIT" "$IPERF_RECVRESULT_VAL $IPERF_RECVRESULT_UNIT"
fi
done
}
# if the skip iperf flag was set, skip the network performance test, otherwise test network performance
if [ -z "$SKIP_IPERF" ]; then
if [ ! -z "$LOCAL_IPERF" ]; then # local iperf has been detected, use instead of pre-compiled binary
IPERF_CMD=iperf3
else
# create a temp directory to house the required iperf binary and library
IPERF_PATH=$YABS_PATH/iperf
mkdir -p $IPERF_PATH
# download iperf3 binary
if [ ! -z "$IPV4_CHECK" ]; then # if IPv4 is enabled
curl -s -4 --connect-timeout 5 --retry 5 --retry-delay 0 https://raw.githubusercontent.com/masonr/yet-another-bench-script/master/bin/iperf3_$ARCH -o $IPERF_PATH/iperf3
else # no IPv4, use IPv6 - below is necessary since raw.githubusercontent.com has no AAAA record
curl -s -6 --connect-timeout 5 --retry 5 --retry-delay 0 -k -g --header 'Host: raw.githubusercontent.com' https://[2a04:4e42::133]/masonr/yet-another-bench-script/master/bin/iperf3_$ARCH -o $IPERF_PATH/iperf3
fi
if [ ! -f "$IPERF_PATH/iperf3" ]; then # ensure iperf3 binary downloaded successfully
IPERF_DL_FAIL=True
else
chmod +x $IPERF_PATH/iperf3
IPERF_CMD=$IPERF_PATH/iperf3
fi
fi
# array containing all currently available iperf3 public servers to use for the network test
# format: "1" "2" "3" "4" "5" \
# 1. domain name of the iperf server
# 2. range of ports that the iperf server is running on (lowest-highest)
# 3. friendly name of the host/owner of the iperf server
# 4. location and advertised speed link of the iperf server
# 5. network modes supported by the iperf server (IPv4 = IPv4-only, IPv4|IPv6 = IPv4 + IPv6, etc.)
IPERF_LOCS=( \
"lon.speedtest.clouvider.net" "5200-5209" "Clouvider" "London, UK (10G)" "IPv4|IPv6" \
"ping.online.net" "5200-5209" "Online.net" "Paris, FR (10G)" "IPv4" \
"ping6.online.net" "5200-5209" "Online.net" "Paris, FR (10G)" "IPv6" \
"iperf.worldstream.nl" "5201-5201" "WorldStream" "The Netherlands (10G)" "IPv4|IPv6" \
"iperf.biznetnetworks.com" "5201-5203" "Biznet" "Jakarta, Indonesia (1G)" "IPv4" \
"nyc.speedtest.clouvider.net" "5200-5209" "Clouvider" "NYC, NY, US (10G)" "IPv4|IPv6" \
"iperf3.velocityonline.net" "5201-5210" "Velocity Online" "Tallahassee, FL, US (10G)" "IPv4" \
"la.speedtest.clouvider.net" "5200-5209" "Clouvider" "Los Angeles, CA, US (10G)" "IPv4|IPv6" \
"speedtest.iveloz.net.br" "5201-5209" "Iveloz Telecom" "Sao Paulo, BR (2G)" "IPv4" \
)
# if the "REDUCE_NET" flag is activated, then do a shorter iperf test with only three locations
# (Clouvider London, Clouvider NYC, and Online.net France)
if [ ! -z "$REDUCE_NET" ]; then
IPERF_LOCS=( \
"lon.speedtest.clouvider.net" "5200-5209" "Clouvider" "London, UK (10G)" "IPv4|IPv6" \
"ping.online.net" "5200-5209" "Online.net" "Paris, FR (10G)" "IPv4" \
"ping6.online.net" "5200-5209" "Online.net" "Paris, FR (10G)" "IPv6" \
"nyc.speedtest.clouvider.net" "5200-5209" "Clouvider" "NYC, NY, US (10G)" "IPv4|IPv6" \
)
fi
# get the total number of iperf locations (total array size divided by 5 since each location has 5 elements)
IPERF_LOCS_NUM=${#IPERF_LOCS[@]}
IPERF_LOCS_NUM=$((IPERF_LOCS_NUM / 5))
if [ -z "$IPERF_DL_FAIL" ]; then
# check if the host has IPv4 connectivity, if so, run iperf3 IPv4 tests
[ ! -z "$IPV4_CHECK" ] && launch_iperf "IPv4"
# check if the host has IPv6 connectivity, if so, run iperf3 IPv6 tests
[ ! -z "$IPV6_CHECK" ] && launch_iperf "IPv6"
else
echo -e "\niperf3 binary download failed. Skipping iperf network tests..."
fi
fi
# launch_geekbench
# Purpose: This method is designed to run the Primate Labs' Geekbench 4/5 Cross-Platform Benchmark utility
# Parameters:
# 1. VERSION - indicates which Geekbench version to run
function launch_geekbench {
VERSION=$1
# create a temp directory to house all geekbench files
GEEKBENCH_PATH=$YABS_PATH/geekbench_$VERSION
mkdir -p $GEEKBENCH_PATH
if [[ $VERSION == *4* && ($ARCH = *aarch64* || $ARCH = *arm*) ]]; then
echo -e "\nARM architecture not supported by Geekbench 4, use Geekbench 5."
elif [[ $VERSION == *4* && $ARCH != *aarch64* && $ARCH != *arm* ]]; then # Geekbench v4
echo -en "\nRunning GB4 benchmark test... *cue elevator music*"
# download the latest Geekbench 4 tarball and extract to geekbench temp directory
curl -s https://cdn.geekbench.com/Geekbench-4.4.4-Linux.tar.gz | tar xz --strip-components=1 -C $GEEKBENCH_PATH &>/dev/null
if [[ "$ARCH" == *"x86"* ]]; then
# check if geekbench file exists
if test -f "geekbench.license"; then
$GEEKBENCH_PATH/geekbench_x86_32 --unlock `cat geekbench.license` > /dev/null 2>&1
fi
# run the Geekbench 4 test and grep the test results URL given at the end of the test
GEEKBENCH_TEST=$($GEEKBENCH_PATH/geekbench_x86_32 --upload 2>/dev/null | grep "https://browser")
else
# check if geekbench file exists
if test -f "geekbench.license"; then
$GEEKBENCH_PATH/geekbench4 --unlock `cat geekbench.license` > /dev/null 2>&1
fi
# run the Geekbench 4 test and grep the test results URL given at the end of the test
GEEKBENCH_TEST=$($GEEKBENCH_PATH/geekbench4 --upload 2>/dev/null | grep "https://browser")
fi
fi
if [[ $VERSION == *5* ]]; then # Geekbench v5
if [[ $ARCH = *x86* && $GEEKBENCH_4 == *False* ]]; then # don't run Geekbench 5 if on 32-bit arch
echo -e "\nGeekbench 5 cannot run on 32-bit architectures. Re-run with -4 flag to use"
echo -e "Geekbench 4, which can support 32-bit architectures. Skipping Geekbench 5."
elif [[ $ARCH = *x86* && $GEEKBENCH_4 == *True* ]]; then
echo -e "\nGeekbench 5 cannot run on 32-bit architectures. Skipping test."
else
echo -en "\nRunning GB5 benchmark test... *cue elevator music*"
# download the latest Geekbench 5 tarball and extract to geekbench temp directory
if [[ $ARCH = *aarch64* || $ARCH = *arm* ]]; then
curl -s https://cdn.geekbench.com/Geekbench-5.4.1-LinuxARMPreview.tar.gz | tar xz --strip-components=1 -C $GEEKBENCH_PATH &>/dev/null
else
curl -s https://cdn.geekbench.com/Geekbench-5.4.1-Linux.tar.gz | tar xz --strip-components=1 -C $GEEKBENCH_PATH &>/dev/null
fi
# check if geekbench file exists
if test -f "geekbench.license"; then
$GEEKBENCH_PATH/geekbench5 --unlock `cat geekbench.license` > /dev/null 2>&1
fi
GEEKBENCH_TEST=$($GEEKBENCH_PATH/geekbench5 --upload 2>/dev/null | grep "https://browser")
fi
fi
# ensure the test ran successfully
if [ -z "$GEEKBENCH_TEST" ]; then
if [[ -z "$IPV4_CHECK" ]]; then
# Geekbench test failed to download because host lacks IPv4 (cdn.geekbench.com = IPv4 only)
echo -e "\r\033[0KGeekbench releases can only be downloaded over IPv4. FTP the Geekbench files and run manually."
elif [[ $ARCH != *x86* ]]; then
# if the Geekbench test failed for any reason, exit cleanly and print error message
echo -e "\r\033[0KGeekbench $VERSION test failed. Run manually to determine cause."
fi
else
# if the Geekbench test succeeded, parse the test results URL
GEEKBENCH_URL=$(echo -e $GEEKBENCH_TEST | head -1)
GEEKBENCH_URL_CLAIM=$(echo $GEEKBENCH_URL | awk '{ print $2 }')
GEEKBENCH_URL=$(echo $GEEKBENCH_URL | awk '{ print $1 }')
# sleep a bit to wait for results to be made available on the geekbench website
sleep 20
# parse the public results page for the single and multi core geekbench scores
[[ $VERSION == *5* ]] && GEEKBENCH_SCORES=$(curl -s $GEEKBENCH_URL | grep "div class='score'") ||
GEEKBENCH_SCORES=$(curl -s $GEEKBENCH_URL | grep "span class='score'")
GEEKBENCH_SCORES_SINGLE=$(echo $GEEKBENCH_SCORES | awk -v FS="(>|<)" '{ print $3 }')
GEEKBENCH_SCORES_MULTI=$(echo $GEEKBENCH_SCORES | awk -v FS="(>|<)" '{ print $7 }')
# print the Geekbench results
echo -en "\r\033[0K"
echo -e "Geekbench $VERSION Benchmark Test:"
echo -e "---------------------------------"
printf "%-15s | %-30s\n" "Test" "Value"
printf "%-15s | %-30s\n"
printf "%-15s | %-30s\n" "Single Core" "$GEEKBENCH_SCORES_SINGLE"
printf "%-15s | %-30s\n" "Multi Core" "$GEEKBENCH_SCORES_MULTI"
printf "%-15s | %-30s\n" "Full Test" "$GEEKBENCH_URL"
# write the geekbench claim URL to a file so the user can add the results to their profile (if desired)
[ ! -z "$GEEKBENCH_URL_CLAIM" ] && echo -e "$GEEKBENCH_URL_CLAIM" >> geekbench_claim.url 2> /dev/null
fi
}
# if the skip geekbench flag was set, skip the system performance test, otherwise test system performance
if [ -z "$SKIP_GEEKBENCH" ]; then
if [[ $GEEKBENCH_4 == *True* ]]; then
launch_geekbench 4
fi
if [[ $GEEKBENCH_5 == *True* ]]; then
launch_geekbench 5
fi
fi
# finished all tests, clean up all YABS files and exit
echo -e
rm -rf $YABS_PATH
# reset locale settings
unset LC_ALL