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