Home | History | Annotate | Download | only in ext4
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 /* A simple test of emmc random read and write performance.  When testing write
     18  * performance, try it twice, once with O_SYNC compiled in, and once with it commented
     19  * out.  Without O_SYNC, the close(2) blocks until all the dirty buffers are written
     20  * out, but the numbers tend to be higher.
     21  */
     22 
     23 #define _LARGEFILE64_SOURCE
     24 #include <string.h>
     25 #include <stdio.h>
     26 #include <sys/types.h>
     27 #include <sys/stat.h>
     28 #include <fcntl.h>
     29 #include <sys/time.h>
     30 #include <stdlib.h>
     31 #include <unistd.h>
     32 #include <math.h>
     33 
     34 #define TST_BLK_SIZE 4096
     35 /* Number of seconds to run the test */
     36 #define TEST_LEN 10
     37 
     38 struct stats {
     39     struct timeval start;
     40     struct timeval end;
     41     off64_t offset;
     42 };
     43 
     44 static void usage(void) {
     45         fprintf(stderr, "Usage: rand_emmc_perf [ -r | -w ] [-o] [-s count] [-f full_stats_filename] <size_in_mb> <block_dev>\n");
     46         exit(1);
     47 }
     48 
     49 static void print_stats(struct stats *stats_buf, int stats_count,
     50                         char * full_stats_file)
     51 {
     52     int i;
     53     struct timeval t;
     54     struct timeval sum = { 0, 0 };
     55     struct timeval max = { 0, 0 };
     56     long long total_usecs;
     57     long long avg_usecs;
     58     long long max_usecs;
     59     long long variance = 0;;
     60     long long x;
     61     double sdev;
     62     FILE *full_stats = NULL;
     63 
     64     if (full_stats_file) {
     65         full_stats = fopen(full_stats_file, "w");
     66         if (full_stats == NULL) {
     67             fprintf(stderr, "Cannot open full stats output file %s, ignoring\n",
     68                     full_stats_file);
     69         }
     70     }
     71 
     72     for (i = 0; i < stats_count; i++) {
     73         timersub(&stats_buf[i].end, &stats_buf[i].start, &t);
     74         if (timercmp(&t, &max, >)) {
     75             max = t;
     76         }
     77         if (full_stats) {
     78             fprintf(full_stats, "%lld\n", (t.tv_sec * 1000000LL) + t.tv_usec);
     79         }
     80         timeradd(&sum, &t, &sum);
     81     }
     82 
     83     if (full_stats) {
     84         fclose(full_stats);
     85     }
     86 
     87     max_usecs = (max.tv_sec * 1000000LL) + max.tv_usec;
     88     total_usecs = (sum.tv_sec * 1000000LL) + sum.tv_usec;
     89     avg_usecs = total_usecs / stats_count;
     90     printf("average random %d byte iop time = %lld usecs\n",
     91            TST_BLK_SIZE, avg_usecs);
     92     printf("maximum random %d byte iop time = %lld usecs\n",
     93            TST_BLK_SIZE, max_usecs);
     94 
     95     /* Now that we have the average (aka mean) go through the data
     96      * again and compute the standard deviation.
     97      * The formula is sqrt(sum_1_to_n((Xi - avg)^2)/n)
     98      */
     99     for (i = 0; i < stats_count; i++) {
    100         timersub(&stats_buf[i].end, &stats_buf[i].start, &t);  /* Xi */
    101         x = (t.tv_sec * 1000000LL) + t.tv_usec;                /* Convert to long long */
    102         x = x - avg_usecs;                                     /* Xi - avg */
    103         x = x * x;                                             /* (Xi - avg) ^ 2 */
    104         variance += x;                                         /* Summation */
    105     }
    106     sdev = sqrt((double)variance/(double)stats_count);
    107     printf("standard deviation of iops is %.2f\n", sdev);
    108 }
    109 
    110 static void stats_test(int fd, int write_mode, off64_t max_blocks, int stats_count,
    111                        char *full_stats_file)
    112 {
    113     struct stats *stats_buf;
    114     char buf[TST_BLK_SIZE] = { 0 };
    115     int i;
    116 
    117     stats_buf = malloc(stats_count * sizeof(struct stats));
    118     if (stats_buf == NULL) {
    119         fprintf(stderr, "Cannot allocate stats_buf\n");
    120         exit(1);
    121     }
    122 
    123     for (i = 0; i < stats_count; i++) {
    124         gettimeofday(&stats_buf[i].start, NULL);
    125 
    126         if (lseek64(fd, (rand() % max_blocks) * TST_BLK_SIZE, SEEK_SET) < 0) {
    127             fprintf(stderr, "lseek64 failed\n");
    128         }
    129 
    130         if (write_mode) {
    131             if (write(fd, buf, sizeof(buf)) != sizeof(buf)) {
    132                 fprintf(stderr, "Short write\n");
    133             }
    134         } else {
    135             if (read(fd, buf, sizeof(buf)) != sizeof(buf)) {
    136                 fprintf(stderr, "Short read\n");
    137             }
    138         }
    139 
    140         gettimeofday(&stats_buf[i].end, NULL);
    141     }
    142 
    143     print_stats(stats_buf, stats_count, full_stats_file);
    144 }
    145 
    146 static void perf_test(int fd, int write_mode, off64_t max_blocks)
    147 {
    148     struct timeval start, end, res;
    149     char buf[TST_BLK_SIZE] = { 0 };
    150     long long iops = 0;
    151     int msecs;
    152 
    153     res.tv_sec = 0;
    154     gettimeofday(&start, NULL);
    155     while (res.tv_sec < TEST_LEN) {
    156         if (lseek64(fd, (rand() % max_blocks) * TST_BLK_SIZE, SEEK_SET) < 0) {
    157             fprintf(stderr, "lseek64 failed\n");
    158         }
    159         if (write_mode) {
    160             if (write(fd, buf, sizeof(buf)) != sizeof(buf)) {
    161                 fprintf(stderr, "Short write\n");
    162             }
    163         } else {
    164             if (read(fd, buf, sizeof(buf)) != sizeof(buf)) {
    165                 fprintf(stderr, "Short read\n");
    166             }
    167         }
    168         iops++;
    169         gettimeofday(&end, NULL);
    170         timersub(&end, &start, &res);
    171     }
    172     close(fd);
    173 
    174     /* The close can take a while when in write_mode as buffers are flushed.
    175      * So get the time again. */
    176     gettimeofday(&end, NULL);
    177     timersub(&end, &start, &res);
    178 
    179     msecs = (res.tv_sec * 1000) + (res.tv_usec / 1000);
    180     printf("%.0f %dbyte iops/sec\n", (float)iops * 1000 / msecs, TST_BLK_SIZE);
    181 }
    182 
    183 int main(int argc, char *argv[])
    184 {
    185     int fd, fd2;
    186     int write_mode = 0;
    187     int o_sync = 0;
    188     int stats_mode = 0;
    189     int stats_count;
    190     char *full_stats_file = NULL;
    191     off64_t max_blocks;
    192     unsigned int seed;
    193     int c;
    194 
    195     while ((c = getopt(argc, argv, "+rwos:f:")) != -1) {
    196         switch (c) {
    197           case '?':
    198           default:
    199             usage();
    200             break;
    201 
    202           case 'r':
    203             /* Do nothing, read mode is the default */
    204             break;
    205 
    206           case 'w':
    207             write_mode = 1;
    208             break;
    209 
    210           case 'o':
    211             o_sync = O_SYNC;
    212             break;
    213 
    214           case 's':
    215             stats_mode = 1;
    216             stats_count = atoi(optarg);
    217             break;
    218 
    219           case 'f':
    220             free(full_stats_file);
    221             full_stats_file = strdup(optarg);
    222             if (full_stats_file == NULL) {
    223                 fprintf(stderr, "Cannot get full stats filename\n");
    224             }
    225             break;
    226         }
    227     }
    228 
    229     if (o_sync && !write_mode) {
    230         /* Can only specify o_sync in write mode.  Probably doesn't matter,
    231          * but clear o_sync if in read mode */
    232         o_sync = 0;
    233     }
    234 
    235     if ((argc - optind) != 2) {
    236         usage();
    237     }
    238 
    239     /* Size is given in megabytes, so compute the number of TST_BLK_SIZE blocks. */
    240     max_blocks = atoll(argv[optind]) * ((1024*1024) / TST_BLK_SIZE);
    241 
    242     if ((fd = open(argv[optind + 1], O_RDWR | o_sync)) < 0) {
    243         fprintf(stderr, "Cannot open block device %s\n", argv[optind + 1]);
    244         exit(1);
    245     }
    246 
    247     fd2 = open("/dev/urandom", O_RDONLY);
    248     if (fd2 < 0) {
    249         fprintf(stderr, "Cannot open /dev/urandom\n");
    250     }
    251     if (read(fd2, &seed, sizeof(seed)) != sizeof(seed)) {
    252         fprintf(stderr, "Cannot read /dev/urandom\n");
    253     }
    254     close(fd2);
    255     srand(seed);
    256 
    257     if (stats_mode) {
    258         stats_test(fd, write_mode, max_blocks, stats_count, full_stats_file);
    259     } else {
    260         perf_test(fd, write_mode, max_blocks);
    261     }
    262     free(full_stats_file);
    263 
    264     exit(0);
    265 }
    266 
    267