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 "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"
36 typedef std::vector
<TestEntry
> TestEntries
;
38 const int kMaxSize
= 16 * 1024 - 1;
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
));
48 CacheTestFillBuffer(buffer1
->data(), kSize1
, false);
49 CacheTestFillBuffer(buffer2
->data(), kMaxSize
, false);
53 MessageLoopHelper helper
;
54 CallbackTest
callback(&helper
, true);
56 base::PerfTimeLogger
timer("Write disk cache entries");
58 for (int i
= 0; i
< num_entries
; i
++) {
60 entry
.key
= GenerateKey(true);
61 entry
.data_len
= rand() % kMaxSize
;
62 entries
->push_back(entry
);
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
))
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
)
74 else if (kSize1
!= ret
)
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
)
82 else if (entry
.data_len
!= ret
)
87 helper
.WaitUntilCacheIoFinished(expected
);
90 return (expected
== helper
.callbacks_called());
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
));
100 CacheTestFillBuffer(buffer1
->data(), kSize1
, false);
101 CacheTestFillBuffer(buffer2
->data(), kMaxSize
, false);
105 MessageLoopHelper helper
;
106 CallbackTest
callback(&helper
, true);
108 const char* message
= cold
? "Read disk cache entries (cold)" :
109 "Read disk cache entries (warm)";
110 base::PerfTimeLogger
timer(message
);
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
))
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
)
123 else if (kSize1
!= ret
)
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
)
131 else if (entries
[i
].data_len
!= ret
)
133 cache_entry
->Close();
136 helper
.WaitUntilCacheIoFinished(expected
);
139 return (expected
== helper
.callbacks_called());
143 // We can use form 1 to 4 blocks.
144 return (rand() & 0x3) + 1;
149 TEST_F(DiskCacheTest
, Hash
) {
150 int seed
= static_cast<int>(Time::Now().ToInternalValue());
153 base::PerfTimeLogger
timer("Hash disk cache keys");
154 for (int i
= 0; i
< 300000; i
++) {
155 std::string key
= GenerateKey(true);
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)));
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());
173 ASSERT_EQ(net::OK
, cb
.GetResult(rv
));
175 int seed
= static_cast<int>(Time::Now().ToInternalValue());
179 int num_entries
= 1000;
181 EXPECT_TRUE(TimeWrite(num_entries
, cache
.get(), &entries
));
183 base::MessageLoop::current()->RunUntilIdle();
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")));
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
));
202 EXPECT_TRUE(TimeRead(num_entries
, cache
.get(), entries
, true));
204 EXPECT_TRUE(TimeRead(num_entries
, cache
.get(), entries
, false));
206 base::MessageLoop::current()->RunUntilIdle();
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());
217 disk_cache::BlockFiles
files(cache_path_
);
218 ASSERT_TRUE(files
.Init(true));
220 int seed
= static_cast<int>(Time::Now().ToInternalValue());
223 const int kNumEntries
= 60000;
224 disk_cache::Addr
* address
= new disk_cache::Addr
[kNumEntries
];
226 base::PerfTimeLogger
timer1("Fill three block-files");
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(),
235 base::PerfTimeLogger
timer2("Create and delete blocks");
237 for (int i
= 0; i
< 200000; i
++) {
238 int entry
= rand() * (kNumEntries
/ RAND_MAX
+ 1);
239 if (entry
>= kNumEntries
)
242 files
.DeleteBlock(address
[entry
], false);
243 EXPECT_TRUE(files
.CreateBlock(disk_cache::RANKINGS
, BlockSize(),
248 base::MessageLoop::current()->RunUntilIdle();