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