Home | History | Annotate | Download | only in disk_cache
      1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include <string>
      6 
      7 #include "base/basictypes.h"
      8 #include "base/file_path.h"
      9 #include "base/file_util.h"
     10 #include "base/perftimer.h"
     11 #include "base/string_util.h"
     12 #include "base/test/test_file_util.h"
     13 #include "base/timer.h"
     14 #include "net/base/io_buffer.h"
     15 #include "net/base/net_errors.h"
     16 #include "net/disk_cache/block_files.h"
     17 #include "net/disk_cache/disk_cache.h"
     18 #include "net/disk_cache/disk_cache_test_util.h"
     19 #include "net/disk_cache/hash.h"
     20 #include "testing/gtest/include/gtest/gtest.h"
     21 #include "testing/platform_test.h"
     22 
     23 using base::Time;
     24 
     25 extern volatile int g_cache_tests_received;
     26 extern volatile bool g_cache_tests_error;
     27 
     28 typedef PlatformTest DiskCacheTest;
     29 
     30 namespace {
     31 
     32 struct TestEntry {
     33   std::string key;
     34   int data_len;
     35 };
     36 typedef std::vector<TestEntry> TestEntries;
     37 
     38 const int kMaxSize = 16 * 1024 - 1;
     39 
     40 // Creates num_entries on the cache, and writes 200 bytes of metadata and up
     41 // to kMaxSize of data to each entry.
     42 int TimeWrite(int num_entries, disk_cache::Backend* cache,
     43               TestEntries* entries) {
     44   const int kSize1 = 200;
     45   scoped_refptr<net::IOBuffer> buffer1 = new net::IOBuffer(kSize1);
     46   scoped_refptr<net::IOBuffer> buffer2 = new net::IOBuffer(kMaxSize);
     47 
     48   CacheTestFillBuffer(buffer1->data(), kSize1, false);
     49   CacheTestFillBuffer(buffer2->data(), kMaxSize, false);
     50 
     51   CallbackTest callback(true);
     52   g_cache_tests_error = false;
     53   g_cache_tests_received = 0;
     54   int expected = 0;
     55 
     56   MessageLoopHelper helper;
     57 
     58   PerfTimeLogger timer("Write disk cache entries");
     59 
     60   for (int i = 0; i < num_entries; i++) {
     61     TestEntry entry;
     62     entry.key = GenerateKey(true);
     63     entry.data_len = rand() % kMaxSize;
     64     entries->push_back(entry);
     65 
     66     disk_cache::Entry* cache_entry;
     67     if (!cache->CreateEntry(entry.key, &cache_entry))
     68       break;
     69     int ret = cache_entry->WriteData(0, 0, buffer1, kSize1, &callback, false);
     70     if (net::ERR_IO_PENDING == ret)
     71       expected++;
     72     else if (kSize1 != ret)
     73       break;
     74 
     75     ret = cache_entry->WriteData(1, 0, buffer2, entry.data_len, &callback,
     76                                  false);
     77     if (net::ERR_IO_PENDING == ret)
     78       expected++;
     79     else if (entry.data_len != ret)
     80       break;
     81     cache_entry->Close();
     82   }
     83 
     84   helper.WaitUntilCacheIoFinished(expected);
     85   timer.Done();
     86 
     87   return expected;
     88 }
     89 
     90 // Reads the data and metadata from each entry listed on |entries|.
     91 int TimeRead(int num_entries, disk_cache::Backend* cache,
     92              const TestEntries& entries, bool cold) {
     93   const int kSize1 = 200;
     94   scoped_refptr<net::IOBuffer> buffer1 = new net::IOBuffer(kSize1);
     95   scoped_refptr<net::IOBuffer> buffer2 = new net::IOBuffer(kMaxSize);
     96 
     97   CacheTestFillBuffer(buffer1->data(), kSize1, false);
     98   CacheTestFillBuffer(buffer2->data(), kMaxSize, false);
     99 
    100   CallbackTest callback(true);
    101   g_cache_tests_error = false;
    102   g_cache_tests_received = 0;
    103   int expected = 0;
    104 
    105   MessageLoopHelper helper;
    106 
    107   const char* message = cold ? "Read disk cache entries (cold)" :
    108                         "Read disk cache entries (warm)";
    109   PerfTimeLogger timer(message);
    110 
    111   for (int i = 0; i < num_entries; i++) {
    112     disk_cache::Entry* cache_entry;
    113     if (!cache->OpenEntry(entries[i].key, &cache_entry))
    114       break;
    115     int ret = cache_entry->ReadData(0, 0, buffer1, kSize1, &callback);
    116     if (net::ERR_IO_PENDING == ret)
    117       expected++;
    118     else if (kSize1 != ret)
    119       break;
    120 
    121     ret = cache_entry->ReadData(1, 0, buffer2, entries[i].data_len, &callback);
    122     if (net::ERR_IO_PENDING == ret)
    123       expected++;
    124     else if (entries[i].data_len != ret)
    125       break;
    126     cache_entry->Close();
    127   }
    128 
    129   helper.WaitUntilCacheIoFinished(expected);
    130   timer.Done();
    131 
    132   return expected;
    133 }
    134 
    135 int BlockSize() {
    136   // We can use form 1 to 4 blocks.
    137   return (rand() & 0x3) + 1;
    138 }
    139 
    140 }  // namespace
    141 
    142 TEST_F(DiskCacheTest, Hash) {
    143   int seed = static_cast<int>(Time::Now().ToInternalValue());
    144   srand(seed);
    145 
    146   PerfTimeLogger timer("Hash disk cache keys");
    147   for (int i = 0; i < 300000; i++) {
    148     std::string key = GenerateKey(true);
    149     disk_cache::Hash(key);
    150   }
    151   timer.Done();
    152 }
    153 
    154 TEST_F(DiskCacheTest, CacheBackendPerformance) {
    155   MessageLoopForIO message_loop;
    156 
    157   ScopedTestCache test_cache;
    158   disk_cache::Backend* cache =
    159       disk_cache::CreateCacheBackend(test_cache.path(), false, 0,
    160                                      net::DISK_CACHE);
    161   ASSERT_TRUE(NULL != cache);
    162 
    163   int seed = static_cast<int>(Time::Now().ToInternalValue());
    164   srand(seed);
    165 
    166   TestEntries entries;
    167   int num_entries = 1000;
    168 
    169   int ret = TimeWrite(num_entries, cache, &entries);
    170   EXPECT_EQ(ret, g_cache_tests_received);
    171 
    172   MessageLoop::current()->RunAllPending();
    173   delete cache;
    174 
    175   ASSERT_TRUE(file_util::EvictFileFromSystemCache(
    176               test_cache.path().AppendASCII("index")));
    177   ASSERT_TRUE(file_util::EvictFileFromSystemCache(
    178               test_cache.path().AppendASCII("data_0")));
    179   ASSERT_TRUE(file_util::EvictFileFromSystemCache(
    180               test_cache.path().AppendASCII("data_1")));
    181   ASSERT_TRUE(file_util::EvictFileFromSystemCache(
    182               test_cache.path().AppendASCII("data_2")));
    183   ASSERT_TRUE(file_util::EvictFileFromSystemCache(
    184               test_cache.path().AppendASCII("data_3")));
    185 
    186   cache = disk_cache::CreateCacheBackend(test_cache.path(), false, 0,
    187                                          net::DISK_CACHE);
    188   ASSERT_TRUE(NULL != cache);
    189 
    190   ret = TimeRead(num_entries, cache, entries, true);
    191   EXPECT_EQ(ret, g_cache_tests_received);
    192 
    193   ret = TimeRead(num_entries, cache, entries, false);
    194   EXPECT_EQ(ret, g_cache_tests_received);
    195 
    196   MessageLoop::current()->RunAllPending();
    197   delete cache;
    198 }
    199 
    200 // Creating and deleting "entries" on a block-file is something quite frequent
    201 // (after all, almost everything is stored on block files). The operation is
    202 // almost free when the file is empty, but can be expensive if the file gets
    203 // fragmented, or if we have multiple files. This test measures that scenario,
    204 // by using multiple, highly fragmented files.
    205 TEST_F(DiskCacheTest, BlockFilesPerformance) {
    206   MessageLoopForIO message_loop;
    207 
    208   ScopedTestCache test_cache;
    209 
    210   disk_cache::BlockFiles files(test_cache.path());
    211   ASSERT_TRUE(files.Init(true));
    212 
    213   int seed = static_cast<int>(Time::Now().ToInternalValue());
    214   srand(seed);
    215 
    216   const int kNumEntries = 60000;
    217   disk_cache::Addr* address = new disk_cache::Addr[kNumEntries];
    218 
    219   PerfTimeLogger timer1("Fill three block-files");
    220 
    221   // Fill up the 32-byte block file (use three files).
    222   for (int i = 0; i < kNumEntries; i++) {
    223     EXPECT_TRUE(files.CreateBlock(disk_cache::RANKINGS, BlockSize(),
    224                                   &address[i]));
    225   }
    226 
    227   timer1.Done();
    228   PerfTimeLogger timer2("Create and delete blocks");
    229 
    230   for (int i = 0; i < 200000; i++) {
    231     int entry = rand() * (kNumEntries / RAND_MAX + 1);
    232     if (entry >= kNumEntries)
    233       entry = 0;
    234 
    235     files.DeleteBlock(address[entry], false);
    236     EXPECT_TRUE(files.CreateBlock(disk_cache::RANKINGS, BlockSize(),
    237                                   &address[entry]));
    238   }
    239 
    240   timer2.Done();
    241   MessageLoop::current()->RunAllPending();
    242   delete[] address;
    243 }
    244