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