37

I want to benchmark a ssd (possibly with encrypted filesystems) and compare it to benchmarks done by crystaldiskmark on windows.

CrystalDiskMark on Windows

So how can I measure approximately the same things as crystaldiskmark does?

For the first row (Seq) I think I could do something like

LC_ALL=C dd if=/dev/zero of=tempfile bs=1M count=1024 conv=fdatasync,notrunc

sudo su -c "echo 3 > /proc/sys/vm/drop_caches"
LC_ALL=C dd if=tempfile of=/dev/null bs=1M count=1024

But I am not sure about the dd parameters.

For the random 512KB, 4KB, 4KB (Queue Depth=32) reads/writes speed-tests I don't have any idea how to reproduce the measurements in linux? So how can I do this?

For testing reading speeds something like sudo hdparm -Tt /dev/sda doesn't seem to make sense for me since I want for example benchmark something like encfs mounts.

Edit

@Alko, @iain

Perhaps I should write something about the motivation about this question: I am trying to benchmark my ssd and compare some encryption solutions. But that's another question (Best way to benchmark different encryption solutions on my system). While surfing in the web about ssd's and benchmarking I have often seen users posting their CrystelDiskMark results in forums. So this is the only motivation for the question. I just want to do the same on linux. For my particular benchmarking see my other question.

3
  • Why don't you use a benchmarking tool, that works on both systems? – Alko Oct 8 '13 at 14:51
  • Found this, seems very useful and in my brief testing on three separate drives gave very similar numbers to the actual crystaldiskmark... github.com/buty4649/fio-cdm – ljwobker Dec 14 '17 at 18:29
  • 2019: This Q deserves a fresh start -- important topic, but has a bit lost its focus here. "Accept and archive". – user373503 Oct 18 '19 at 5:48
31

I'd say fio would have no trouble producing those workloads. Note that despite its name CrystalDiskMark is actually a benchmark of a filesysystem on a particular disk - it can't do I/O raw to the disk alone. As such it will always have filesystem overhead in it (not necessarily a bad thing but something to be aware of e.g. because the filesystems being compared might not be the same).

An example based on replicating the output in the screenshot above supplemented by information from the CrystalDiskMark manual (this isn't complete but should give the general idea):

fio --loops=5 --size=1000m --filename=/mnt/fs/fiotest.tmp --stonewall --ioengine=libaio --direct=1 \
  --name=Seqread --bs=1m --rw=read \
  --name=Seqwrite --bs=1m --rw=write \
  --name=512Kread --bs=512k --rw=randread \
  --name=512Kwrite --bs=512k --rw=randwrite \
  --name=4kQD32read --bs=4k --iodepth=32 --rw=randread \
  --name=4kQD32write --bs=4k --iodepth=32 --rw=randwrite
rm -f /mnt/fs/fiotest.tmp 

BE CAREFUL - this example permanently destroys the data in /mnt/fs/fiotest.tmp!

A list of fio parameters can be seen on http://fio.readthedocs.io/en/latest/fio_doc.html .

6
  • 3
    I tried fio in Ubuntu 16.04 and CrystalDiskMark in Windows 7. Some numbers match up while others don't. The sequential r/w was off by a factor of 2. Meaning, the Linux values were 50% of those reported by CDM v3.0.4 (note: current version is 6.0.0, but old versions still available for download). To fiddle w/ the disparity I set bs=4m instead of 1m. That made the numbers closer. Trying 8m and 32m made it even closer. Ultimately like Anon said his answer is not complete and like @Alko, we need the same tool on both OSes. Also note the newest CDM 6 uses different tests than OP. Nice info Anon – Vahid Pazirandeh Jan 14 '18 at 10:00
  • 2
    @VahidPazirandeh Interesting, github.com/buty4649/fio-cdm/blob/master/fio-cdm has the same 1m settings, maybe the documentation of cdm is not good enough. – inf3rno Jan 21 '18 at 0:27
  • 2
    @vahid-pazirandeh You're welcome. NB: if you want the same tool on both OSes note that there's a version of fio for Windows too. – Anon Apr 4 '19 at 5:42
  • This command took 46 minutes to run on a Samsung EVO Plus 256GB sdcard. How could this be changed for a more Crystal-like runtime? – Tom Hale Feb 1 '20 at 5:33
  • Crystaldiskmark tries to avoid filesystem overhead by pre-creating a large contiguous file before running tests simply by seeking within that file. No filesystem "operations" like creating files or allocating more space for files happen during tests, though things that slow down file data writes like journaling of file data (not just metadata) will still have effect if you had that enabled for some reason. – trr Mar 22 '20 at 7:07
17

Original Answer: https://unix.stackexchange.com/revisions/480191/12

I created a script that tries to replicate the behavior of crystaldiskmark 6 with fio, and I also added support for older tests (like the 512kb test) this answer is updated now a long while after I initially created it, the script has much needed improvements. A changelog is included for people who used earlier versions.

The script depends on fio and df. If you do not have or want to install df, the script should still work. The script is compatible with dash (if you want to use dash change #!/bin/bash into #!/bin/dash) but please note that with dash there's no support for autocomplete with the read command (so you'll have to spell out the path to wherever you want to test, letter by letter, if you use dash).

This is the full script:

#!/bin/bash
#############################################################################################################
#Changelog                                                                                                  #
#############################################################################################################
#Added prompts for user input to configure script instead of relying on hardcoded settings.
#Added a lot of errorchecking
#The script is now optionally compatible with dash (this is the reason for there being a sed command at the end of every echo -e instance, dash liked to print the -e part when I was testing.)
#Vastly improved compatibility across distributions
#Special thanks to everyone who contributed here: https://gist.github.com/i3v/99f8ef6c757a5b8e9046b8a47f3a9d5b
#Also extra special thanks to BAGELreflex on github for this: https://gist.github.com/BAGELreflex/c04e7a25d64e989cbd9376a9134b8f6d it made a huge difference to this improved version.
#Added optimizations for 512k and 4k tests (they now use QSIZE instead of SIZE, it makes these tests a lot faster and doesn't affect accuracy much, assuming SIZE is appropriately configured for your drive.) 
#Added option to not use legacy (512k and Q1T1 Seq R/W tests) to save time when testing.
#Ensured the script can run fine without df installed now. Some information may be missing but worst case scenario it'll just look ugly.
#Added a save results option that imitates the saved results from crystaldiskmark; the formatting is a little wonky but it checks out. Great for comparing results between operating systems.
#Reconfigured results to use MegaBytes instead of MebiBytes (This is what crystaldiskmark uses so results should now be marginally closer).
#Sequential read/write results (512k, q1t1 seq and q32t1 seq) will now appear as soon as they're finished and can be viewed while the 4k tests are running.
#Note: The legacy test option defaults to no if nothing is selected, the result saving defaults to yes. It's easy to change if you don't like this.
#Observation: When testing, I observed that the read results seemed mostly consistent with the results I got from crystaldiskmark on windows, however there's something off with the write results.
#Sorry for the messy code :)
#############################################################################################################
#User input requests and error checking                                                                     #
#############################################################################################################
if [ -f /usr/bin/fio ]; then #Dependency check
    :
else
    echo -e "\033[1;31mError: This script requires fio to run, please make sure it is installed." | sed 's:-e::g'
    exit
fi

if [ -f /usr/bin/df ]; then #Dependency check
    nodf=0
else
    nodf=1
    echo -e "\033[1;31mWarning: df is not installed, this script relies on df to display certain information, some information may be missing." | sed 's:-e::g'
fi

if [ "$(ps -ocmd= | tail -1)" = "bash" ]; then
    echo "What drive do you want to test? (Default: $HOME on /dev/$(df $HOME | grep /dev | cut -d/ -f3 | cut -d" " -f1) )"
    echo -e "\033[0;33mOnly directory paths (e.g. /home/user/) are valid targets.\033[0;00m"
    read -e TARGET
else #no autocomplete available for dash.
    echo "What drive do you want to test? (Default: $HOME on /dev/$(df $HOME | grep /dev | cut -d/ -f3 | cut -d" " -f1) )"
    echo -e "\033[0;33mOnly directory paths (e.g. /home/user/) are valid targets. Use bash if you want autocomplete.\033[0;00m" | sed 's:-e::g'
    read TARGET
fi

echo "
How many times to run the test? (Default: 5)"
read LOOPS

echo "How large should each test be in MiB? (Default: 1024)"
echo -e "\033[0;33mOnly multiples of 32 are permitted!\033[0;00m" | sed 's:-e::g'

read SIZE

echo "Do you want to write only zeroes to your test files to imitate dd benchmarks? (Default: 0)"
echo -e "\033[0;33mEnabling this setting may drastically alter your results, not recommended unless you know what you're doing.\033[0;00m" | sed 's:-e::g'
read WRITEZERO

echo "Would you like to include legacy tests (512kb & Q1T1 Sequential Read/Write)? [Y/N]"
read LEGACY

if [ -z $TARGET ]; then
    TARGET=$HOME
elif [ -d $TARGET ]; then
    :
else
    echo -e "\033[1;31mError: $TARGET is not a valid path."
    exit
fi

if [ -z $LOOPS ]; then
    LOOPS=5
elif [ "$LOOPS" -eq "$LOOPS" ] 2>/dev/null; then
    :
else
  echo -e "\033[1;31mError: $LOOPS is not a valid number, please use a number to declare how many times to loop tests." | sed 's:-e::g'
  exit
fi

if [ -z $SIZE ]; then
    SIZE=1024
elif [ "$SIZE" -eq "$SIZE" ] 2>/dev/null && ! (( $SIZE % 32 )) 2>/dev/null;then
    :
else
    echo -e "\033[1;31mError: The test size must be an integer set to a multiple of 32. Please write a multiple of 32 for the size setting (Optimal settings: 1024, 2048, 4096, 8192, 16384)."
    exit
fi

if [ -z $WRITEZERO ]; then
    WRITEZERO=0
elif [ "$WRITEZERO" -eq 1 ] 2>/dev/null || [ "$WRITEZERO" -eq 0 ] 2>/dev/null; then
    :
else
    echo -e "\033[1;31mError: WRITEZERO only accepts 0 or 1, $WRITEZERO is not a valid argument." | sed 's:-e::g'
    exit
fi

if [ "$LEGACY" = "Y" ] || [ "$LEGACY" = "y" ]; then
    :
else
    LEGACY=no
fi

if [ $nodf = 1 ]; then
    echo "
    Settings are as follows:
    Target Directory: $TARGET
    Size Of Test: $SIZE MiB
    Number Of Loops: $LOOPS
    Write Zeroes: $WRITEZERO
    Legacy Tests: $LEGACY
    "
    echo "Are you sure these are correct? [Y/N]"
    read REPLY
    if [ $REPLY = Y ] || [ $REPLY = y ]; then
        REPLY=""
    else
        echo ""
        exit
    fi

else
    DRIVE=$(df $TARGET | grep /dev | cut -d/ -f3 | cut -d" " -f1 | rev | cut -c 2- | rev)

    if [ "$(echo $DRIVE | cut -c -4)" = "nvme" ]; then #NVME Compatibility
        echo $DRIVE
        DRIVE=$(df $TARGET | grep /dev | cut -d/ -f3 | cut -d" " -f1 | rev | cut -c 3- | rev)
        echo $DRIVE
    fi
    DRIVEMODEL=$(cat /sys/block/$DRIVE/device/model | sed 's/ *$//g')
    DRIVESIZE=$(($(cat /sys/block/$DRIVE/size)*512/1024/1024/1024))GB
    DRIVEPERCENT=$(df -h $TARGET | cut -d ' ' -f11 | tail -n 1)
    DRIVEUSED=$(df -h $TARGET | cut -d ' ' -f6 | tail -n 1)

    echo "
    Settings are as follows:
    Target Directory: $TARGET
    Target Drive: $DRIVE
    Size Of Test: $SIZE MiB
    Number Of Loops: $LOOPS
    Write Zeroes: $WRITEZERO
    Legacy Tests: $LEGACY
    "
    echo "Are you sure these are correct? [Y/N]"
    read REPLY
    if [ "$REPLY" = "Y" ] || [ "$REPLY" = "y" ]; then
        REPLY=""
    else
        echo ""
        exit
    fi
fi
#############################################################################################################
#Setting the last Variables And Running Sequential R/W Benchmarks                                           #
#############################################################################################################


QSIZE=$(($SIZE / 32)) #Size of Q32Seq tests
SIZE=$(echo $SIZE)m
QSIZE=$(echo $QSIZE)m

if [ $nodf = 1 ]; then
    echo "
Running Benchmark,  please wait...
    "
else
    echo "
Running Benchmark on: /dev/$DRIVE, $DRIVEMODEL ($DRIVESIZE), please wait...
"
fi

if [ $LEGACY = Y ] || [ $LEGACY = y ]; then
    fio --loops=$LOOPS --size=$SIZE --filename="$TARGET/.fiomark.tmp" --stonewall --ioengine=libaio --direct=1 --zero_buffers=$WRITEZERO --output-format=json \
  --name=Bufread --loops=1 --bs=$SIZE --iodepth=1 --numjobs=1 --rw=readwrite \
  --name=Seqread --bs=$SIZE --iodepth=1 --numjobs=1 --rw=read \
  --name=Seqwrite --bs=$SIZE --iodepth=1 --numjobs=1 --rw=write \
  --name=SeqQ32T1read --bs=$QSIZE --iodepth=32 --numjobs=1 --rw=read \
  --name=SeqQ32T1write --bs=$QSIZE --iodepth=32 --numjobs=1 --rw=write \
  > "$TARGET/.fiomark.txt"

    fio --loops=$LOOPS --size=$QSIZE --filename="$TARGET/.fiomark-512k.tmp" --stonewall --ioengine=libaio --direct=1 --zero_buffers=$WRITEZERO --output-format=json \
  --name=512kread --bs=512k --iodepth=1 --numjobs=1 --rw=read \
  --name=512kwrite --bs=512k --iodepth=1 --numjobs=1 --rw=write \
  > "$TARGET/.fiomark-512k.txt"

    SEQR="$(($(cat "$TARGET/.fiomark.txt" | grep -A15 '"name" : "Seqread"' | grep bw | grep -v '_' | cut -d: -f2 | sed s:,::g)/1000))MB/s [   $(cat "$TARGET/.fiomark.txt" | grep -A15 '"name" : "Seqread"' | grep -m1 iops | cut -d: -f2 | cut -d. -f1 | sed 's: ::g') IOPS]"
    SEQW="$(($(cat "$TARGET/.fiomark.txt" | grep -A80 '"name" : "Seqwrite"' | grep bw | grep -v '_' | sed 2\!d | cut -d: -f2 | sed s:,::g)/1000))MB/s [   $(cat "$TARGET/.fiomark.txt" | grep -A80 '"name" : "Seqwrite"' | grep iops | sed '7!d' | cut -d: -f2 | cut -d. -f1 | sed 's: ::g') IOPS]"
    F12KR="$(($(cat "$TARGET/.fiomark-512k.txt" | grep -A15 '"name" : "512kread"' | grep bw | grep -v '_' | cut -d: -f2 | sed s:,::g)/1000))MB/s [   $(cat "$TARGET/.fiomark-512k.txt" | grep -A15 '"name" : "512kread"' | grep -m1 iops | cut -d: -f2 | cut -d. -f1 | sed 's: ::g') IOPS]"
    F12KW="$(($(cat "$TARGET/.fiomark-512k.txt" | grep -A80 '"name" : "512kwrite"' | grep bw | grep -v '_' | sed 2\!d | cut -d: -f2 | sed s:,::g)/1000))MB/s [   $(cat "$TARGET/.fiomark-512k.txt" | grep -A80 '"name" : "512kwrite"' | grep iops | sed '7!d' | cut -d: -f2 | cut -d. -f1 | sed 's: ::g') IOPS]"
    SEQ32R="$(($(cat "$TARGET/.fiomark.txt" | grep -A15 '"name" : "SeqQ32T1read"' | grep bw | grep -v '_' | cut -d: -f2 | sed s:,::g)/1000))MB/s [   $(cat "$TARGET/.fiomark.txt" | grep -A15 '"name" : "SeqQ32T1read"' | grep -m1 iops | cut -d: -f2 | cut -d. -f1 | sed 's: ::g') IOPS]"
    SEQ32W="$(($(cat "$TARGET/.fiomark.txt" | grep -A80 '"name" : "SeqQ32T1write"' | grep bw | grep -v '_' | sed 2\!d | cut -d: -f2 | sed s:,::g)/1000))MB/s [   $(cat "$TARGET/.fiomark.txt" | grep -A80 '"name" : "SeqQ32T1write"' | grep iops | sed '7!d' | cut -d: -f2 | cut -d. -f1 | sed 's: ::g') IOPS]"

    echo -e "
Results:
\033[0;33m
Sequential Read: $SEQR
Sequential Write: $SEQW
\033[0;32m
512KB Read: $F12KR
512KB Write: $F12KW
\033[1;36m
Sequential Q32T1 Read: $SEQ32R
Sequential Q32T1 Write: $SEQ32W" | sed 's:-e::g'

else
    fio --loops=$LOOPS --size=$SIZE --filename="$TARGET/.fiomark.tmp" --stonewall --ioengine=libaio --direct=1 --zero_buffers=$WRITEZERO --output-format=json \
  --name=Bufread --loops=1 --bs=$SIZE --iodepth=1 --numjobs=1 --rw=readwrite \
  --name=SeqQ32T1read --bs=$QSIZE --iodepth=32 --numjobs=1 --rw=read \
  --name=SeqQ32T1write --bs=$QSIZE --iodepth=32 --numjobs=1 --rw=write \
  > "$TARGET/.fiomark.txt"

    SEQ32R="$(($(cat "$TARGET/.fiomark.txt" | grep -A15 '"name" : "SeqQ32T1read"' | grep bw | grep -v '_' | cut -d: -f2 | sed s:,::g)/1000))MB/s [   $(cat "$TARGET/.fiomark.txt" | grep -A15 '"name" : "SeqQ32T1read"' | grep -m1 iops | cut -d: -f2 | cut -d. -f1 | sed 's: ::g') IOPS]"
    SEQ32W="$(($(cat "$TARGET/.fiomark.txt" | grep -A80 '"name" : "SeqQ32T1write"' | grep bw | grep -v '_' | sed 2\!d | cut -d: -f2 | sed s:,::g)/1000))MB/s [   $(cat "$TARGET/.fiomark.txt" | grep -A80 '"name" : "SeqQ32T1write"' | grep iops | sed '7!d' | cut -d: -f2 | cut -d. -f1 | sed 's: ::g') IOPS]"

    echo -e "
Results:
\033[1;36m
Sequential Q32T1 Read: $SEQ32R
Sequential Q32T1 Write: $SEQ32W" | sed 's:-e::g'
fi

#############################################################################################################
#4KiB Tests & Results                                                                                       #
#############################################################################################################

fio --loops=$LOOPS --size=$QSIZE --filename="$TARGET/.fiomark-4k.tmp" --stonewall --ioengine=libaio --direct=1 --zero_buffers=$WRITEZERO --output-format=json \
  --name=4kread --bs=4k --iodepth=1 --numjobs=1 --rw=randread \
  --name=4kwrite --bs=4k --iodepth=1 --numjobs=1 --rw=randwrite \
  --name=4kQ32T1read --bs=4k --iodepth=32 --numjobs=1 --rw=randread \
  --name=4kQ32T1write --bs=4k --iodepth=32 --numjobs=1 --rw=randwrite \
  --name=4kQ8T8read --bs=4k --iodepth=8 --numjobs=8 --rw=randread \
  --name=4kQ8T8write --bs=4k --iodepth=8 --numjobs=8 --rw=randwrite \
  > "$TARGET/.fiomark-4k.txt"

FKR="$(($(cat "$TARGET/.fiomark-4k.txt" | grep -A15 '"name" : "4kread"' | grep bw | grep -v '_' | cut -d: -f2 | sed s:,::g)/1000))MB/s [   $(cat "$TARGET/.fiomark-4k.txt" | grep -A15 '"name" : "4kread"' | grep -m1 iops | cut -d: -f2 | cut -d. -f1 | sed 's: ::g') IOPS]"
FKW="$(($(cat "$TARGET/.fiomark-4k.txt" | grep -A80 '"name" : "4kwrite"' | grep bw | grep -v '_' | sed 2\!d | cut -d: -f2 | sed s:,::g)/1000))MB/s [   $(cat "$TARGET/.fiomark-4k.txt" | grep -A80 '"name" : "4kwrite"' | grep iops | sed '7!d' | cut -d: -f2 | cut -d. -f1 | sed 's: ::g') IOPS]"
FK32R="$(($(cat "$TARGET/.fiomark-4k.txt" | grep -A15 '"name" : "4kQ32T1read"' | grep bw | grep -v '_' | cut -d: -f2 | sed s:,::g)/1000))MB/s [   $(cat "$TARGET/.fiomark-4k.txt" | grep -A15 '"name" : "4kQ32T1read"' | grep -m1 iops | cut -d: -f2 | cut -d. -f1 | sed 's: ::g') IOPS]"
FK32W="$(($(cat "$TARGET/.fiomark-4k.txt" | grep -A80 '"name" : "4kQ32T1write"' | grep bw | grep -v '_' | sed 2\!d | cut -d: -f2 | sed s:,::g)/1000))MB/s [   $(cat "$TARGET/.fiomark-4k.txt" | grep -A80 '"name" : "4kQ32T1write"' | grep iops | sed '7!d' | cut -d: -f2 | cut -d. -f1 | sed 's: ::g') IOPS]"
FK8R="$(($(cat "$TARGET/.fiomark-4k.txt" | grep -A15 '"name" : "4kQ8T8read"' | grep bw | grep -v '_' | sed 's/        "bw" : //g' | sed 's:,::g' | awk '{ SUM += $1} END { print SUM }')/1000))MB/s [   $(cat "$TARGET/.fiomark-4k.txt" | grep -A15 '"name" : "4kQ8T8read"' | grep iops | sed 's/        "iops" : //g' | sed 's:,::g' | awk '{ SUM += $1} END { print SUM }' | cut -d. -f1) IOPS]"
FK8W="$(($(cat "$TARGET/.fiomark-4k.txt" | grep -A80 '"name" : "4kQ8T8write"' | grep bw | sed 's/        "bw" : //g' | sed 's:,::g' | awk '{ SUM += $1} END { print SUM }')/1000))MB/s [   $(cat "$TARGET/.fiomark-4k.txt" | grep -A80 '"name" : "4kQ8T8write"' | grep '"iops" '| sed 's/        "iops" : //g' | sed 's:,::g' | awk '{ SUM += $1} END { print SUM }' | cut -d. -f1) IOPS]"

echo -e "\033[1;35m
4KB Q8T8 Read: $FK8R
4KB Q8T8 Write: $FK8W
\033[1;33m
4KB Q32T1 Read: $FK32R
4KB Q32T1 Write: $FK32W
\033[0;36m
4KB Read: $FKR
4KB Write: $FKW
\033[0m
" | sed 's:-e::g'

echo "Would you like to save these results? [Y/N]"
read REPLY
if [ "$REPLY" = "N" ] || [ "$REPLY" = "n" ]; then
    REPLY=""
else
    DRIVESIZE=$(df -h $TARGET | cut -d ' ' -f3 | tail -n 1)
    echo "
Saving at $HOME/$DRIVE$(date +%F%I%M%S).txt
"
    if [ "$LEGACY" = "Y" ] || [ "$LEGACY" = "y" ]; then
echo "-----------------------------------------------------------------------
Flexible I/O Tester - $(fio --version) (C) axboe
                          Fio Github : https://github.com/axboe/fio
                       Script Source : https://unix.stackexchange.com/a/480191/72554
-----------------------------------------------------------------------
* MB/s = 1,000,000 bytes/s
* KB = 1000 bytes, KiB = 1024 bytes

   Legacy Seq Read (Q=  1,T= 1) :   $SEQR
  Legacy Seq Write (Q=  1,T= 1) :   $SEQW
   512KiB Seq Read (Q=  1,T= 1) :   $F12KR
  512KiB Seq Write (Q=  1,T= 1) :   $F12KW
   Sequential Read (Q= 32,T= 1) :   $SEQ32R
  Sequential Write (Q= 32,T= 1) :   $SEQ32W
  Random Read 4KiB (Q=  8,T= 8) :   $FK8R
 Random Write 4KiB (Q=  8,T= 8) :   $FK8W
  Random Read 4KiB (Q= 32,T= 1) :   $FK32R
 Random Write 4KiB (Q= 32,T= 1) :   $FK32W
  Random Read 4KiB (Q=  1,T= 1) :   $FKR
 Random Write 4KiB (Q=  1,T= 1) :   $FKW

  Test : $(echo $SIZE | rev | cut -c 2- | rev) MiB [$DRIVEMODEL, $DRIVE $DRIVEPERCENT ($(echo $DRIVEUSED | rev | cut -c 2- | rev)/$(echo $DRIVESIZE | rev | cut -c 2- | rev) GiB] (x$LOOPS)  [Interval=0 sec]
  Date : $(date +%F | sed 's:-:/:g') $(date +%T)
    OS : $(uname -srm)
  " > "$HOME/$DRIVE$(date +%F%I%M%S).txt"
    else
echo "-----------------------------------------------------------------------
Flexible I/O Tester - $(fio --version) (C) axboe
                          Fio Github : https://github.com/axboe/fio
                       Script Source : https://unix.stackexchange.com/a/480191/72554
-----------------------------------------------------------------------
* MB/s = 1,000,000 bytes/s
* KB = 1000 bytes, KiB = 1024 bytes

   Sequential Read (Q= 32,T= 1) :   $SEQ32R
  Sequential Write (Q= 32,T= 1) :   $SEQ32W
  Random Read 4KiB (Q=  8,T= 8) :   $FK8R
 Random Write 4KiB (Q=  8,T= 8) :   $FK8W
  Random Read 4KiB (Q= 32,T= 1) :   $FK32R
 Random Write 4KiB (Q= 32,T= 1) :   $FK32W
  Random Read 4KiB (Q=  1,T= 1) :   $FKR
 Random Write 4KiB (Q=  1,T= 1) :   $FKW

  Test : $(echo $SIZE | rev | cut -c 2- | rev) MiB [$DRIVEMODEL, $DRIVE $DRIVEPERCENT ($(echo $DRIVEUSED | rev | cut -c 2- | rev)/$(echo $DRIVESIZE | rev | cut -c 2- | rev) GiB] (x$LOOPS)  [Interval=0 sec]
  Date : $(date +%F | sed 's:-:/:g') $(date +%T)
    OS : $(uname -srm)
  " > "$HOME/$DRIVE$(date +%F%I%M%S).txt"
    fi
fi


rm "$TARGET/.fiomark.txt" "$TARGET/.fiomark-512k.txt" "$TARGET/.fiomark-4k.txt" 2>/dev/null
rm "$TARGET/.fiomark.tmp" "$TARGET/.fiomark-512k.tmp" "$TARGET/.fiomark-4k.tmp" 2>/dev/null

Which will output results like this:

Running Benchmark on: /dev/nvme0n1, KINGSTON RBUSNS8154P3256GJ (238GB), please wait...


Results:

Sequential Read: 1337MB/s [   5 IOPS]
Sequential Write: 266MB/s [   1 IOPS]

512KB Read: 512MB/s [   1000 IOPS]
512KB Write: 273MB/s [   533 IOPS]

Sequential Q32T1 Read: 1394MB/s [   170 IOPS]
Sequential Q32T1 Write: 265MB/s [   32 IOPS]

4KB Q8T8 Read: 487MB/s [   121835 IOPS]
4KB Q8T8 Write: 284MB/s [   71085 IOPS]

4KB Q32T1 Read: 390MB/s [   97523 IOPS]
4KB Q32T1 Write: 315MB/s [   78769 IOPS]

4KB Read: 28MB/s [   7135 IOPS]
4KB Write: 126MB/s [   31507 IOPS]


Would you like to save these results? [Y/N]

(The results are color coded when executed in the terminal.)

If you choose to save the results it will output an imitation of the same save file that crystaldiskmark makes when you save it's results.

Here's an image comparing these files with 3 benchmarks of the same drive, 1 with crystaldiskmark and 2 with the script: Benchmark Comparison Image

When you run the script it will ask you to configure it (options include: Target Directory, Loops, Size, Write zeroes and Legacy tests) every setting has a default if you don't type anything. To test a specific hard drive you need to first mount it and then type a full path to a directory on that hard drive in the prompt that asks what drive you want to test. While running the script creates hidden temporary files in the target directory which it cleans up after it finishes running. Even if you cancel the process while it is running with CTRL+C, it should still clean up the files.

Test results are displayed in two stages, Sequential and 4KB, the sequential read/write tests are done first and once they're finished they are displayed while the 4KB tests are running. If you cancel the process before the tests have been completed, any partial results will be displayed on exit.

Note that you may sometimes get slightly off results, especially if you're doing something in the background while the tests are running, so running the test twice in a row to compare results is advisable.

These tests may take a long time to run (significantly improved in the new version of the script though). The default settings in the script currently are suitable for a regular (SATA) SSD.

Recommended size setting for different drive types:

  • (SATA) SSD: 1024(default)
  • (ANY) HDD: 256
  • (High End NVME) SSD: 4096
  • (Low-Mid End M.2/NVME) SSD: 2048

A High End NVME typically has around ~2GB/s read speeds (Intel Optane and Samsung 960 EVO are examples; but in the latter's case I would recommend 2048 instead due to slower 4kb speeds.), a Low-Mid End can have anywhere between ~500-1800MB/s read speeds.

In the new version of the script the 4KB and 512KB tests will be significantly faster since they no longer use the actual full size the user defines. (Crystaldiskmark will also skimp on size for the 4KB tests or they would always take forever).

Known Issues:

  • There cannot be whitespace in the path used to select a hard drive for testing.
  • Those ntfs-3g benchmarks are looking awfully shady if you ask me.

And there you have it. Enjoy!

5
  • Have you checked how your script behaves with fio on Windows? – Anon Nov 11 '18 at 19:03
  • 2
    (A note to readers other than Cestarian: if you're making a new tool that uses fio then if at all possible don't scrape the human readable fio output - use --output-format=json and parse the JSON. Fio's human readable output is not meant for machines and is not stable between versions fio. See this YouTube video of a case where scraping fio's human output led to an undesirable outcome) – Anon Nov 11 '18 at 19:26
  • Thanks, I'll keep that in mind, sadly I no longer have a windows installation so I can't test that... but, I decided to look up crystaldiskmark results for my ssd and it seems my memory was wrong, since the results add up to what I'm getting on fio after all. It was a misunderstanding on my end that lead me to think the results were slower :/ I will correct it. I will also update this answer soon to use the json output for futureproofing, and perhaps a GUI version (I started working on it, but gtkdialog is poorly documented and zenity is limited so I'm having a hard time) – Cestarian Nov 11 '18 at 20:16
  • 1
    @Cestarian, great script, but not working "out of the box" on CentOS7. I had to modify it a bit. – Igor Dec 30 '18 at 11:54
  • 1
    @Igor Interesting, yeah I made it on manjaro and I'm not really much of an expert on bash :) I mentioned your modified version in the answer just in case someone else has problems. – Cestarian Dec 31 '18 at 5:06
6

You can use iozone and bonnie. They can do what crystal disk mark can do and more.

I personally used iozone a lot while benchmarking and stress testing devices from personal computers to enterprise storage systems. It has an auto mode which does everything but you can tailor it to your needs.

3
  • 5
    How to reproduce with this the crystalmark measurements in detail? – student Oct 8 '13 at 15:51
  • 2
    I'll try my best to write an howto, but I need the list of tests that Crystal Disk Mark conducts. Are there any other tests which the software runs beside the ones visible on the screenshot. – bayindirh Oct 10 '13 at 18:47
  • 1
    Just the ones in the screenshot. – trr May 16 '17 at 4:53
4

Try NetCoreStorageSpeedTest.

It may not be exactly the same as CrystalDiskMark, but it's advantage is in the platforms it supports:

Console Application (Linux, Windows and MacOS - .NET Core 3) and a cross-platform Class Library (C#, .NET Standard 2) for measuring read/write speeds of disks/storage devices.

The project also supports Android as listed on its GitHub project page.

2

I'm not sure the various deeper tests make any real sense when considering what you're doing in detail.

The settings like block size, and queue depth, are parameters for controlling the low level input/output parameters of the ATA interface your SSD is sitting on.

Thats all well and good when you're just running some basic test against a drive fairly directly, like to a large file in a simple partitioned filesystem.

Once you start talking about benchmarking an encfs, these parameters dont particularly apply to your filesystem any more, the filesystem is just an interface into something else that eventually backs onto a filesystem that backs onto a drive.

I think it would be helpful to understand what exactly you're trying to measure, because there are two factors in play here - the raw disk IO speed, which you can test by timing various DD commands (can give examples if this is what you want) /without/ encfs, or the process will be CPU bounded by the encryption and you're trying to test the relative throughput of the encryption algorithm. In which case the parameters for queue depth etc aren't particularly relevant.

In both regards, a timed DD command will give you the basic throughput statistics you seek, but you should consider what you're intending to measure and the relevant parameters for that.

This link seems to provide a good guide to disk speed testing using timed DD commands including the necessary coverage about 'defeating buffers/cache' and so forth. Likely this will provide the information you need. Decide which you're more interested in tho, disk performance or encryption performance, one of the two will be the bottleneck, and tuning the non-bottleneck isn't going to benefit anything.

2

Try KDiskMark, very similar to CrystalDiskmark.

The application is written in C++ with Qt and doesn't have any KDE dependencies.

KDiskMark screenshot

Usage

To start, after select a folder (and optionally other params), click on All.

Install

Ubuntu based distros

sudo add-apt-repository ppa:jonmagon/kdiskmark
sudo apt update
sudo apt install kdiskmark

Arch based distros

KDiskMark is included in the official community repo. You can install it like any other package:

sudo pacman -Syu kdiskmark

Development version can be installed from AUR kdiskmark-git package.

git clone https://aur.archlinux.org/kdiskmark-git.git
cd kdiskmark-git
makepkg -si

Fedora

KDiskMark is included in the official Fedora repo. You can install it like any other package:

sudo dnf install kdiskmark

openSUSE Tumbleweed

sudo zypper install kdiskmark

Not the answer you're looking for? Browse other questions tagged or ask your own question.