NVMe 프로토콜을 사용하는 Samsung 950 Pro SSD 카드의 처리량을 테스트하고 있습니다. 현재 테스트 방법은 파티션에 파일 시스템을 마운트하고 X바이트 크기의 파일을 파일 시스템에 쓰는 것입니다. 이를 수행하는 데 걸리는 시간을 기록하면 바이트/초를 계산할 수 있습니다.
내 테스트에는 더 높은 수준의 for 루프에 의해 지정된 가변 블록 크기로 한 번에 한 블록씩 X 바이트까지 쓰는 while 루프가 있습니다. 게다가 N개의 애플리케이션을 병렬로 실행하는 또 다른 루프도 있습니다. 각 애플리케이션은 SSD의 서로 다른 파티션에 기록됩니다.
현재 읽기 및 쓰기 모두에 대해 Samsung 950 Pro 데이터시트에 지정된 이론적 최대 전송 속도보다 약간 빠른 속도를 확인하고 있습니다. 삼성은 950 Pro의 최대 순차 쓰기 속도가 1.5GB/s이고 최대 순차 읽기 속도가 2.5GB/s라고 명시했습니다.
실행할 애플리케이션 수와 블록 크기를 반복하는 bash 스크립트의 주요 기능은 다음과 같습니다.
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
다음은 run_perf_test의 쓰기 섹션입니다. 이 부분 뒤에는 쓰기 처리량 속도 테스트로 구성된 읽기 섹션도 있습니다. 테스트 사이에 SSD의 모든 파티션을 마운트 해제했다가 다시 마운트하여 모든 NVMe 트랜잭션이 완료되도록 하고 캐싱이 쓰기 작업에서 읽기 작업의 처리량 측정에 영향을 미치는 것을 방지합니다.
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는 첫 번째 인수인 "-r" 또는 "-w", 두 번째 인수인 블록 크기, 세 번째 인수인 SSD 파티션의 파일인 파일 이름을 기반으로 문자열을 작성하는 C++ 애플리케이션입니다. SSD 파티션에서 파일을 열고 여기에 문자열을 쓰려고 시도합니다. 다음은 "-w"가 첫 번째 인수로 제공될 때 호출되는 쓰기 함수에 대한 구현입니다.
/*! \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;
}
수치는 Samsung 950 Pro의 이론적 최대 처리량에 가깝지만 일부는 너무 높아서 문제가 됩니다. Samsung 950 Pro의 이론적 최대 처리량보다 높은 수치가 나타나는 이유는 무엇입니까?