I wanted to know how good (or bad) my WD Caviar Green 1TB WD10EARS hard disks perform and did some tests:
# hdparm -T /dev/sdb
/dev/sdb:
Timing cached reads: 9914 MB in 2.00 seconds = 4960.19 MB/sec
# hdparm -t /dev/sdb
/dev/sdb:
Timing buffered disk reads: 260 MB in 3.02 seconds = 86.16 MB/sec
I used dd for testing write speed:
# dd if=/dev/zero of=/tmp/testimg.img bs=4k count=256k
262144+0 records in
262144+0 records out
1073741824 Bytes (1.1 GB) copied, 8.23401 s, 130 MB/s
Interesting enough, writes seem to be faster than reads. But this can be due to the fact that these drives write physical 4KB sectors and hdparm does not know about this. WD claims a throughput of 111 MB/s on the spec sheet.
Finally I wanted to test the seek times and found a nice little tool on the internet. The source will be given at the end of this post for those who are interested. Note: seeker works for me on my hardware with 11.3. It may destroy your HDD. Use at your own risk and have backups ready (as always). My result:
# ./seeker /dev/sdb 30
Seeker v3.0, 2009-06-17
http://www.linuxinsight.com/how_fast_is_your_disk.html
Benchmarking /dev/sdb
[1953525168 blocks, 1000204886016 bytes,
931 GB, 953869 MB, 1000 GiB, 1000204 MiB]
[512 logical sector size, 512 physical sector size]
[1 threads]
Wait 30 seconds
..............................
Results: 67 seeks/second, 14.866 ms random access time
(78973440 < offsets < 1000138063872)
# ./seeker /dev/sdb 30 24
Seeker v3.0, 2009-06-17
http://www.linuxinsight.com/how_fast_is_your_disk.html
Benchmarking /dev/sdb
[1953525168 blocks, 1000204886016 bytes,
931 GB, 953869 MB, 1000 GiB, 1000204 MiB]
[512 logical sector size, 512 physical sector size]
[24 threads]
Wait 30 seconds
..............................
Results: 118 seeks/second, 8.460 ms random access time
(294353920 < offsets < 1000069917184)
This is not overwhelming at all, but had to be expected due to the slow rotational speed. I do not want to start the general HDD speed contest here, but want to share my results. I think they are as good as they can be and no further tweaking of HDD parameters is indicated. Have fun doing your own testing.
C-source for program seeker. You have to compile your own. Use at your own risk.
/* seeker.c
========
Version: 3.0
About: Tool for testing hard disk seek times
Author: admin LinuxInsight
Changed by: Witold Baryluk and mindbender
Source: http://www.linuxinsight.com/how_fast_is_your_disk.html
Licence: GPLv2
Compile: gcc -o seeker -O2 -march=x86-64 seeker.c -pthread
Run: seeker <device> <timeout_seconds> [number_of_threads]
(as root)
Example: ./seeker /dev/sda 30 4
*/
#define _LARGEFILE64_SOURCE
#ifndef _REENTRANT
#define _REENTRANT
#endif
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>
#include <signal.h>
#include <sys/fcntl.h>
#include <sys/ioctl.h>
#include <linux/fs.h>
#define BLOCKSIZE 512
#define O_DIRECT 00040000
pthread_mutex_t muteks = PTHREAD_MUTEX_INITIALIZER;
int count;
int TIMEOUT;
time_t start;
off64_t maxoffset = 0;
off64_t minoffset = 249994674176000uLL;
int threads;
typedef struct {
int id;
int fd;
int run;
char* filename;
unsigned int seed;
unsigned long long numbytes;
char* buffer;
int count;
off64_t maxoffset;
off64_t minoffset;
} parm;
parm *p;
void done() {
int i;
time_t end;
time(&end);
if (end < start + TIMEOUT) {
printf(".");
alarm(1);
return;
}
for (i = 0; i < threads; i++) {
p*.run = 0;
}
}
void report() {
if (count) {
printf(".
Results: %d seeks/second, %.3f ms random access "
"time
(%llu < offsets < %llu)
",
count / TIMEOUT, 1000.0 * TIMEOUT / count,
(unsigned long long)minoffset,
(unsigned long long)maxoffset);
}
exit(EXIT_SUCCESS);
}
void handle(const char *string, int error) {
if (error) {
perror(string);
exit(EXIT_FAILURE);
}
}
void* f(void *arg) {
int retval;
off64_t offset;
parm *p = (parm*)arg;
srand(p->seed);
/* wait for all processes */
pthread_mutex_lock(&muteks);
pthread_mutex_unlock(&muteks);
while (p->run) {
offset = (off64_t) ( (unsigned long long) (p->numbytes *
(rand_r(&(p->seed)) / (RAND_MAX + 1.0) )));
/* do blocksize aligned seeks */
offset = offset & (~((off64_t) 0) & (~(BLOCKSIZE-1))) ;
/* printf("%d %x
", p->id, (unsigned long long )offset); */
retval = lseek64(p->fd, offset, SEEK_SET);
handle("lseek64", retval == (off64_t) -1);
retval = read(p->fd, p->buffer, BLOCKSIZE);
handle("read", retval < 0);
p->count++;
if (offset > p->maxoffset) {
p->maxoffset = offset;
} else if (offset < p->minoffset) {
p->minoffset = offset;
}
}
return NULL;
}
int main(int argc, char **argv) {
int fd, retval;
int physical_sector_size = 0;
size_t logical_sector_size = 0ULL;
unsigned long long numblocks, numbytes;
unsigned long long ull;
unsigned long ul;
pthread_t *t_id;
pthread_attr_t pthread_custom_attr;
int i;
setvbuf(stdout, NULL, _IONBF, 0);
printf("Seeker v3.0, 2009-06-17
"
"http://www.linuxinsight.com/how_fast_is_your_disk.html
");
if (!(argc == 3 || argc == 4)) {
printf("Usage: %s device seconds [threads]
", argv[0]);
exit(1);
}
TIMEOUT = atoi(argv[2]);
threads = 1;
if (argc == 4) {
threads = atoi(argv[3]);
}
fd = open(argv[1], O_RDONLY | O_LARGEFILE | O_DIRECT);
handle("open", fd < 0);
#ifdef BLKGETSIZE64
retval = ioctl(fd, BLKGETSIZE64, &ull);
numbytes = (unsigned long long)ull;
#else
retval = ioctl(fd, BLKGETSIZE, &ul);
numbytes = (unsigned long long)ul;
#endif
handle("ioctl", retval == -1);
retval = ioctl(fd, BLKBSZGET, &logical_sector_size);
handle("ioctl", retval == -1 && logical_sector_size > 0);
retval = ioctl(fd, BLKSSZGET, &physical_sector_size);
handle("ioctl", retval == -1 && physical_sector_size > 0);
numblocks = ((unsigned long long) numbytes) /
(unsigned long long)BLOCKSIZE;
printf("Benchmarking %s
%llu blocks, %llu bytes,
"
"%llu GB, %llu MB, %llu GiB, %llu MiB]
",
argv[1], numblocks, numbytes,
numbytes/(1024uLL*1024uLL*1024uLL),
numbytes / (1024uLL*1024uLL),
numbytes/(1000uLL*1000uLL*1000uLL),
numbytes / (1000uLL*1000uLL));
printf("%d logical sector size, %d physical sector size]
",
physical_sector_size, physical_sector_size);
printf("%d threads]
", threads);
printf("Wait %d seconds
", TIMEOUT);
t_id = (pthread_t *)malloc(threads*sizeof(pthread_t));
handle("malloc", t_id == NULL);
pthread_attr_init(&pthread_custom_attr);
p = (parm *)malloc(sizeof(parm)*threads);
handle("malloc", p == NULL);
time(&start);
pthread_mutex_lock(&muteks);
srand((unsigned int)start*(unsigned int)getpid());
for (i = 0; i < threads; i++) {
p*.id = i;
p*.filename = argv[1];
p*.seed = rand()+i;
p*.fd = dup(fd);
handle("dup", p*.fd < 0);
p*.buffer = (char *) memalign(512*8, sizeof(char)*BLOCKSIZE);
/* printf("%x
", (int)(&(p*.buffer))); */
p*.numbytes = numbytes;
handle("malloc", p*.buffer == NULL);
p*.run = 1;
p*.count = 0;
p*.minoffset = minoffset;
p*.maxoffset = maxoffset;
retval = pthread_create(&(t_id*), NULL, f, (void*)(p+i));
handle("pthread_create", retval != 0);
}
sleep(1);
time(&start);
signal(SIGALRM, &done);
alarm(1);
pthread_mutex_unlock(&muteks);
for (i = 0; i < threads; i++) {
pthread_join(t_id*, NULL);
}
for (i = 0; i < threads; i++) {
count += p*.count;
if (p*.maxoffset > maxoffset) {
maxoffset = p*.maxoffset;
}
if (p*.minoffset < minoffset) {
minoffset = p*.minoffset;
}
}
report();
/* notreached */
return 0;
}