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