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