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.
7 #include "base/basictypes.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"
35 typedef std::vector
<TestEntry
> TestEntries
;
37 const int kMaxSize
= 16 * 1024 - 1;
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
));
47 CacheTestFillBuffer(buffer1
->data(), kSize1
, false);
48 CacheTestFillBuffer(buffer2
->data(), kMaxSize
, false);
52 MessageLoopHelper helper
;
53 CallbackTest
callback(&helper
, true);
55 base::PerfTimeLogger
timer("Write disk cache entries");
57 for (int i
= 0; i
< num_entries
; i
++) {
59 entry
.key
= GenerateKey(true);
60 entry
.data_len
= rand() % kMaxSize
;
61 entries
->push_back(entry
);
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
))
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
)
73 else if (kSize1
!= ret
)
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
)
81 else if (entry
.data_len
!= ret
)
86 helper
.WaitUntilCacheIoFinished(expected
);
89 return (expected
== helper
.callbacks_called());
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
));
99 CacheTestFillBuffer(buffer1
->data(), kSize1
, false);
100 CacheTestFillBuffer(buffer2
->data(), kMaxSize
, false);
104 MessageLoopHelper helper
;
105 CallbackTest
callback(&helper
, true);
107 const char* message
= cold
? "Read disk cache entries (cold)" :
108 "Read disk cache entries (warm)";
109 base::PerfTimeLogger
timer(message
);
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
))
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
)
122 else if (kSize1
!= ret
)
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
)
130 else if (entries
[i
].data_len
!= ret
)
132 cache_entry
->Close();
135 helper
.WaitUntilCacheIoFinished(expected
);
138 return (expected
== helper
.callbacks_called());
142 // We can use form 1 to 4 blocks.
143 return (rand() & 0x3) + 1;
148 TEST_F(DiskCacheTest
, Hash
) {
149 int seed
= static_cast<int>(Time::Now().ToInternalValue());
152 base::PerfTimeLogger
timer("Hash disk cache keys");
153 for (int i
= 0; i
< 300000; i
++) {
154 std::string key
= GenerateKey(true);
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)));
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
,
173 cache_thread
.task_runner(),
178 ASSERT_EQ(net::OK
, cb
.GetResult(rv
));
180 int seed
= static_cast<int>(Time::Now().ToInternalValue());
184 int num_entries
= 1000;
186 EXPECT_TRUE(TimeWrite(num_entries
, cache
.get(), &entries
));
188 base::MessageLoop::current()->RunUntilIdle();
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")));
202 rv
= disk_cache::CreateCacheBackend(net::DISK_CACHE
,
203 net::CACHE_BACKEND_BLOCKFILE
,
207 cache_thread
.task_runner(),
211 ASSERT_EQ(net::OK
, cb
.GetResult(rv
));
213 EXPECT_TRUE(TimeRead(num_entries
, cache
.get(), entries
, true));
215 EXPECT_TRUE(TimeRead(num_entries
, cache
.get(), entries
, false));
217 base::MessageLoop::current()->RunUntilIdle();
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());
228 disk_cache::BlockFiles
files(cache_path_
);
229 ASSERT_TRUE(files
.Init(true));
231 int seed
= static_cast<int>(Time::Now().ToInternalValue());
234 const int kNumEntries
= 60000;
235 disk_cache::Addr
* address
= new disk_cache::Addr
[kNumEntries
];
237 base::PerfTimeLogger
timer1("Fill three block-files");
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(),
246 base::PerfTimeLogger
timer2("Create and delete blocks");
248 for (int i
= 0; i
< 200000; i
++) {
249 int entry
= rand() * (kNumEntries
/ RAND_MAX
+ 1);
250 if (entry
>= kNumEntries
)
253 files
.DeleteBlock(address
[entry
], false);
254 EXPECT_TRUE(files
.CreateBlock(disk_cache::RANKINGS
, BlockSize(),
259 base::MessageLoop::current()->RunUntilIdle();