Ich teste den Durchsatz einer Samsung 950 Pro SSD-Karte, die das NVMe-Protokoll verwendet. Meine aktuelle Testmethode besteht darin, ein Dateisystem auf einer Partition zu mounten und eine Datei mit der Größe X Bytes in das Dateisystem zu schreiben. Indem man die dafür benötigte Zeit aufzeichnet, kann man die Bytes/Sekunde berechnen.
In meinem Test habe ich eine While-Schleife, die bis zu X Bytes in variablen Blockgrößen schreibt, einen Block nach dem anderen, angegeben durch eine For-Schleife auf höherer Ebene. Darüber hinaus habe ich eine weitere Schleife, die N dieser Anwendungen parallel ausführt, wobei jede Anwendung in eine andere Partition der SSD schreibt.
Derzeit sehe ich Geschwindigkeiten, die etwas höher sind als die theoretische maximale Übertragungsgeschwindigkeit, die im Datenblatt der Samsung 950 Pro für Lesen und Schreiben angegeben ist. Samsung hat angegeben, dass die maximale sequentielle Schreibgeschwindigkeit für die 950 Pro 1,5 GB/s und die maximale sequentielle Lesegeschwindigkeit 2,5 GB/s beträgt.
Dies ist die Hauptfunktion des Bash-Skripts, das die Anzahl der auszuführenden Anwendungen und die Blockgrößen durchläuft:
appInstances=1
while [ $appInstances -le 4 ]
do
for blocksize in 4096 32768 131072 524288 1048576 67108864 268435456 1073741824
do
# Run the test
datetime
echo "[$datetime_v]: Test blocksize: $blocksize appInstances: $appInstances"
run_single_perf_test $blocksize
done
appInstances=`expr $appInstances \* 2`
done
exit 0
Hier ist der Schreibabschnitt von run_perf_test. Nach diesem Teil folgt auch ein Leseabschnitt, der den Schreibdurchsatz-Geschwindigkeitstest umfasst. Zwischen den Tests hänge ich alle Partitionen der SSD ab und hänge sie wieder ein, damit alle NVMe-Transaktionen abgeschlossen werden können und um zu verhindern, dass Caching von Schreibvorgängen die Durchsatzmessung für den Lesevorgang beeinflusst.
instCnt=1
childpids=""
while [ $instCnt -le $appInstances ]
do
fsrw -w $blocksize /fsmnt/fs${instCnt}/usernumber1/j.j &
# Save the process ID
childpids="$childpids $!"
# Increment the instace count.
instCnt=`expr $instCnt + 1`
done
fsrw ist eine C++-Anwendung, die basierend auf dem ersten Argument „-r“ oder „-w“, dem zweiten Argument, der Blockgröße und dem dritten Argument, dem Dateinamen, der eine Datei auf der SSD-Partition ist, einen String erstellt und versucht, die Datei auf der SSD-Partition zu öffnen und den String dort hinein zu schreiben. Hier ist die Implementierung für die Schreibfunktion, die aufgerufen wird, wenn „-w“ als erstes Argument angegeben wird.
/*! \fn perform_writeop()
* \brief The function returns true when the write operation completes successfully.
*
* The function will run until the read is complete or a 35 second timeout is reached.
* It will record the time before the write begins, then also record the time afterward.
* If the timeout is reached this should be about 35 seconds
*/
bool perform_writeop ()
{
// File descriptor.
int32_t fd = -1;
// Function status.
bool status = false;
// Zero writes
int zero_writes = 0;
// Buffer fill index.
int32_t bfidx = 0;
// Character value.
int8_t cv = 33;
// Fill the buffer with printable characters.
for (; bfidx < blocksize; bfidx++, cv++)
{
// Verify the character value is in range.
if (cv >= 127)
{
cv = 33;
}
else
{
// Add to the buffer.
buf[bfidx] = cv;
}
}
// Open the file.
fd = open (fname.c_str (), O_WRONLY | O_CREAT, 0660);
// Verify the file has been opened.
if (fd == -1)
{
cout << get_datetime_string() << "Write open of " << fname
<< " failed. Errno: " << errno << endl;
}
else
{
// Total bytes written.
uint64_t written = 0;
// Notify the start of the test.
cout << get_datetime_string() << "Write test started" << endl;
// Elapsed time.
struct timeval tv = { 0 };
get_elapsed_time (&tv);
struct timeval write_tv = tv;
// Run until it is time for the test to stop.
while (written < READ_LIMIT && zero_writes < 10)
{
ssize_t writesize = write (fd, &buf[0], blocksize);
if (writesize == -1)
{
cout << get_datetime_string << "Write failure. Errno: " << errno << endl;
zero_writes = 10;
}
else if (0 == writesize)
{
cout << get_datetime_string() << "Zero bytes written" << endl;
zero_writes++;
}
else
{
written += writesize;
}
}
string flush_command = "nvme flush /dev/nvme0n1p";
flush_command += fname[9];
system(flush_command.c_str());
// Get the elapsed time.
get_elapsed_time (&write_tv);
// Report the number of bytes written.
cout << get_datetime_string() << "Write " << written << " bytes in "
<< write_tv.tv_sec << "." << write_tv.tv_usec
<< " seconds" << endl;
// Close the file.
close (fd);
// Get the elapsed time.
get_elapsed_time (&tv);
// Report the number of bytes read.
cout << get_datetime_string() << "Write closed. " << written
<< " Bytes written in " << tv.tv_sec << "." << tv.tv_usec
<< " seconds" << endl;
// Report the number of bytes per second.
cout << get_datetime_string() << "Bytes per second "
<< bytes_per_second (&tv, written) << endl;
// Report the cache flush time.
struct timeval flush_tv = { 0 };
timersub (&tv, &write_tv, &flush_tv);
cout << get_datetime_string() << "System cache flush completed in "
<< flush_tv.tv_sec << "." << flush_tv.tv_usec << "seconds" << endl;
// Set the function return status when all write operations have
// been successful.
if (zero_writes < 10)
{
status = true;
}
}
return status;
}
Die Daten, die ich zurückbekomme, sehen so aus
Die Zahlen liegen nahe am theoretischen Maximaldurchsatz des Samsung 950 Pro, aber manche sind zu hoch, und das beunruhigt mich. Warum erhalte ich möglicherweise Zahlen, die über dem theoretischen Maximaldurchsatz des Samsung 950 Pro liegen?