• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/blockfile/backend_impl.h"
21 #include "net/disk_cache/blockfile/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.
TimeWrite(int num_entries,disk_cache::Backend * cache,TestEntries * entries)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|.
TimeRead(int num_entries,disk_cache::Backend * cache,const TestEntries & entries,bool cold)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 
BlockSize()142 int BlockSize() {
143   // We can use form 1 to 4 blocks.
144   return (rand() & 0x3) + 1;
145 }
146 
147 }  // namespace
148 
TEST_F(DiskCacheTest,Hash)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 
TEST_F(DiskCacheTest,CacheBackendPerformance)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(net::DISK_CACHE,
170                                           net::CACHE_BACKEND_BLOCKFILE,
171                                           cache_path_,
172                                           0,
173                                           false,
174                                           cache_thread.task_runner(),
175                                           NULL,
176                                           &cache,
177                                           cb.callback());
178 
179   ASSERT_EQ(net::OK, cb.GetResult(rv));
180 
181   int seed = static_cast<int>(Time::Now().ToInternalValue());
182   srand(seed);
183 
184   TestEntries entries;
185   int num_entries = 1000;
186 
187   EXPECT_TRUE(TimeWrite(num_entries, cache.get(), &entries));
188 
189   base::MessageLoop::current()->RunUntilIdle();
190   cache.reset();
191 
192   ASSERT_TRUE(base::EvictFileFromSystemCache(
193               cache_path_.AppendASCII("index")));
194   ASSERT_TRUE(base::EvictFileFromSystemCache(
195               cache_path_.AppendASCII("data_0")));
196   ASSERT_TRUE(base::EvictFileFromSystemCache(
197               cache_path_.AppendASCII("data_1")));
198   ASSERT_TRUE(base::EvictFileFromSystemCache(
199               cache_path_.AppendASCII("data_2")));
200   ASSERT_TRUE(base::EvictFileFromSystemCache(
201               cache_path_.AppendASCII("data_3")));
202 
203   rv = disk_cache::CreateCacheBackend(net::DISK_CACHE,
204                                       net::CACHE_BACKEND_BLOCKFILE,
205                                       cache_path_,
206                                       0,
207                                       false,
208                                       cache_thread.task_runner(),
209                                       NULL,
210                                       &cache,
211                                       cb.callback());
212   ASSERT_EQ(net::OK, cb.GetResult(rv));
213 
214   EXPECT_TRUE(TimeRead(num_entries, cache.get(), entries, true));
215 
216   EXPECT_TRUE(TimeRead(num_entries, cache.get(), entries, false));
217 
218   base::MessageLoop::current()->RunUntilIdle();
219 }
220 
221 // Creating and deleting "entries" on a block-file is something quite frequent
222 // (after all, almost everything is stored on block files). The operation is
223 // almost free when the file is empty, but can be expensive if the file gets
224 // fragmented, or if we have multiple files. This test measures that scenario,
225 // by using multiple, highly fragmented files.
TEST_F(DiskCacheTest,BlockFilesPerformance)226 TEST_F(DiskCacheTest, BlockFilesPerformance) {
227   ASSERT_TRUE(CleanupCacheDir());
228 
229   disk_cache::BlockFiles files(cache_path_);
230   ASSERT_TRUE(files.Init(true));
231 
232   int seed = static_cast<int>(Time::Now().ToInternalValue());
233   srand(seed);
234 
235   const int kNumEntries = 60000;
236   disk_cache::Addr* address = new disk_cache::Addr[kNumEntries];
237 
238   base::PerfTimeLogger timer1("Fill three block-files");
239 
240   // Fill up the 32-byte block file (use three files).
241   for (int i = 0; i < kNumEntries; i++) {
242     EXPECT_TRUE(files.CreateBlock(disk_cache::RANKINGS, BlockSize(),
243                                   &address[i]));
244   }
245 
246   timer1.Done();
247   base::PerfTimeLogger timer2("Create and delete blocks");
248 
249   for (int i = 0; i < 200000; i++) {
250     int entry = rand() * (kNumEntries / RAND_MAX + 1);
251     if (entry >= kNumEntries)
252       entry = 0;
253 
254     files.DeleteBlock(address[entry], false);
255     EXPECT_TRUE(files.CreateBlock(disk_cache::RANKINGS, BlockSize(),
256                                   &address[entry]));
257   }
258 
259   timer2.Done();
260   base::MessageLoop::current()->RunUntilIdle();
261   delete[] address;
262 }
263