OLD | NEW |
| (Empty) |
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 } | |
OLD | NEW |