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