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/strings/string_util.h" 12 #include "base/test/perf_time_logger.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 base::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 base::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 base::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 base::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 base::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