disk_cache_perftest.cc 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709
  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. #include <limits>
  5. #include <memory>
  6. #include <string>
  7. #include "base/barrier_closure.h"
  8. #include "base/bind.h"
  9. #include "base/files/file_enumerator.h"
  10. #include "base/files/file_path.h"
  11. #include "base/hash/hash.h"
  12. #include "base/memory/raw_ptr.h"
  13. #include "base/process/process_metrics.h"
  14. #include "base/rand_util.h"
  15. #include "base/run_loop.h"
  16. #include "base/strings/string_number_conversions.h"
  17. #include "base/strings/string_util.h"
  18. #include "base/test/scoped_run_loop_timeout.h"
  19. #include "base/test/test_file_util.h"
  20. #include "base/test/test_timeouts.h"
  21. #include "base/threading/thread.h"
  22. #include "base/time/time.h"
  23. #include "base/timer/elapsed_timer.h"
  24. #include "build/build_config.h"
  25. #include "net/base/cache_type.h"
  26. #include "net/base/completion_repeating_callback.h"
  27. #include "net/base/io_buffer.h"
  28. #include "net/base/net_errors.h"
  29. #include "net/base/test_completion_callback.h"
  30. #include "net/disk_cache/backend_cleanup_tracker.h"
  31. #include "net/disk_cache/blockfile/backend_impl.h"
  32. #include "net/disk_cache/blockfile/block_files.h"
  33. #include "net/disk_cache/disk_cache.h"
  34. #include "net/disk_cache/disk_cache_test_base.h"
  35. #include "net/disk_cache/disk_cache_test_util.h"
  36. #include "net/disk_cache/simple/simple_backend_impl.h"
  37. #include "net/disk_cache/simple/simple_index.h"
  38. #include "net/disk_cache/simple/simple_index_file.h"
  39. #include "testing/gtest/include/gtest/gtest.h"
  40. #include "testing/perf/perf_result_reporter.h"
  41. #include "testing/platform_test.h"
  42. using base::Time;
  43. namespace {
  44. const size_t kNumEntries = 10000;
  45. const int kHeadersSize = 2000;
  46. const int kBodySize = 72 * 1024 - 1;
  47. // HttpCache likes this chunk size.
  48. const int kChunkSize = 32 * 1024;
  49. // As of 2017-01-12, this is a typical per-tab limit on HTTP connections.
  50. const int kMaxParallelOperations = 10;
  51. static constexpr char kMetricPrefixDiskCache[] = "DiskCache.";
  52. static constexpr char kMetricPrefixSimpleIndex[] = "SimpleIndex.";
  53. static constexpr char kMetricCacheEntriesWriteTimeMs[] =
  54. "cache_entries_write_time";
  55. static constexpr char kMetricCacheHeadersReadTimeColdMs[] =
  56. "cache_headers_read_time_cold";
  57. static constexpr char kMetricCacheHeadersReadTimeWarmMs[] =
  58. "cache_headers_read_time_warm";
  59. static constexpr char kMetricCacheEntriesReadTimeColdMs[] =
  60. "cache_entries_read_time_cold";
  61. static constexpr char kMetricCacheEntriesReadTimeWarmMs[] =
  62. "cache_entries_read_time_warm";
  63. static constexpr char kMetricCacheKeysHashTimeMs[] = "cache_keys_hash_time";
  64. static constexpr char kMetricFillBlocksTimeMs[] = "fill_sequential_blocks_time";
  65. static constexpr char kMetricCreateDeleteBlocksTimeMs[] =
  66. "create_and_delete_random_blocks_time";
  67. static constexpr char kMetricSimpleCacheInitTotalTimeMs[] =
  68. "simple_cache_initial_read_total_time";
  69. static constexpr char kMetricSimpleCacheInitPerEntryTimeUs[] =
  70. "simple_cache_initial_read_per_entry_time";
  71. static constexpr char kMetricAverageEvictionTimeMs[] = "average_eviction_time";
  72. perf_test::PerfResultReporter SetUpDiskCacheReporter(const std::string& story) {
  73. perf_test::PerfResultReporter reporter(kMetricPrefixDiskCache, story);
  74. reporter.RegisterImportantMetric(kMetricCacheEntriesWriteTimeMs, "ms");
  75. reporter.RegisterImportantMetric(kMetricCacheHeadersReadTimeColdMs, "ms");
  76. reporter.RegisterImportantMetric(kMetricCacheHeadersReadTimeWarmMs, "ms");
  77. reporter.RegisterImportantMetric(kMetricCacheEntriesReadTimeColdMs, "ms");
  78. reporter.RegisterImportantMetric(kMetricCacheEntriesReadTimeWarmMs, "ms");
  79. reporter.RegisterImportantMetric(kMetricCacheKeysHashTimeMs, "ms");
  80. reporter.RegisterImportantMetric(kMetricFillBlocksTimeMs, "ms");
  81. reporter.RegisterImportantMetric(kMetricCreateDeleteBlocksTimeMs, "ms");
  82. reporter.RegisterImportantMetric(kMetricSimpleCacheInitTotalTimeMs, "ms");
  83. reporter.RegisterImportantMetric(kMetricSimpleCacheInitPerEntryTimeUs, "us");
  84. return reporter;
  85. }
  86. perf_test::PerfResultReporter SetUpSimpleIndexReporter(
  87. const std::string& story) {
  88. perf_test::PerfResultReporter reporter(kMetricPrefixSimpleIndex, story);
  89. reporter.RegisterImportantMetric(kMetricAverageEvictionTimeMs, "ms");
  90. return reporter;
  91. }
  92. void MaybeIncreaseFdLimitTo(unsigned int max_descriptors) {
  93. #if BUILDFLAG(IS_POSIX)
  94. base::IncreaseFdLimitTo(max_descriptors);
  95. #endif
  96. }
  97. struct TestEntry {
  98. std::string key;
  99. int data_len;
  100. };
  101. enum class WhatToRead {
  102. HEADERS_ONLY,
  103. HEADERS_AND_BODY,
  104. };
  105. class DiskCachePerfTest : public DiskCacheTestWithCache {
  106. public:
  107. DiskCachePerfTest() { MaybeIncreaseFdLimitTo(kFdLimitForCacheTests); }
  108. const std::vector<TestEntry>& entries() const { return entries_; }
  109. protected:
  110. // Helper methods for constructing tests.
  111. bool TimeWrites(const std::string& story);
  112. bool TimeReads(WhatToRead what_to_read,
  113. const std::string& metric,
  114. const std::string& story);
  115. void ResetAndEvictSystemDiskCache();
  116. // Callbacks used within tests for intermediate operations.
  117. void WriteCallback(net::CompletionOnceCallback final_callback,
  118. scoped_refptr<net::IOBuffer> headers_buffer,
  119. scoped_refptr<net::IOBuffer> body_buffer,
  120. disk_cache::Entry* cache_entry,
  121. int entry_index,
  122. size_t write_offset,
  123. int result);
  124. // Complete perf tests.
  125. void CacheBackendPerformance(const std::string& story);
  126. const size_t kFdLimitForCacheTests = 8192;
  127. std::vector<TestEntry> entries_;
  128. };
  129. class WriteHandler {
  130. public:
  131. WriteHandler(const DiskCachePerfTest* test,
  132. disk_cache::Backend* cache,
  133. net::CompletionOnceCallback final_callback)
  134. : test_(test), cache_(cache), final_callback_(std::move(final_callback)) {
  135. CacheTestFillBuffer(headers_buffer_->data(), kHeadersSize, false);
  136. CacheTestFillBuffer(body_buffer_->data(), kChunkSize, false);
  137. }
  138. void Run();
  139. protected:
  140. void CreateNextEntry();
  141. void CreateCallback(int data_len, disk_cache::EntryResult result);
  142. void WriteDataCallback(disk_cache::Entry* entry,
  143. int next_offset,
  144. int data_len,
  145. int expected_result,
  146. int result);
  147. private:
  148. bool CheckForErrorAndCancel(int result);
  149. raw_ptr<const DiskCachePerfTest> test_;
  150. raw_ptr<disk_cache::Backend> cache_;
  151. net::CompletionOnceCallback final_callback_;
  152. size_t next_entry_index_ = 0;
  153. size_t pending_operations_count_ = 0;
  154. int pending_result_ = net::OK;
  155. scoped_refptr<net::IOBuffer> headers_buffer_ =
  156. base::MakeRefCounted<net::IOBuffer>(kHeadersSize);
  157. scoped_refptr<net::IOBuffer> body_buffer_ =
  158. base::MakeRefCounted<net::IOBuffer>(kChunkSize);
  159. };
  160. void WriteHandler::Run() {
  161. for (int i = 0; i < kMaxParallelOperations; ++i) {
  162. ++pending_operations_count_;
  163. CreateNextEntry();
  164. }
  165. }
  166. void WriteHandler::CreateNextEntry() {
  167. ASSERT_GT(kNumEntries, next_entry_index_);
  168. TestEntry test_entry = test_->entries()[next_entry_index_++];
  169. auto callback =
  170. base::BindRepeating(&WriteHandler::CreateCallback, base::Unretained(this),
  171. test_entry.data_len);
  172. disk_cache::EntryResult result =
  173. cache_->CreateEntry(test_entry.key, net::HIGHEST, callback);
  174. if (result.net_error() != net::ERR_IO_PENDING)
  175. callback.Run(std::move(result));
  176. }
  177. void WriteHandler::CreateCallback(int data_len,
  178. disk_cache::EntryResult result) {
  179. if (CheckForErrorAndCancel(result.net_error()))
  180. return;
  181. disk_cache::Entry* entry = result.ReleaseEntry();
  182. net::CompletionRepeatingCallback callback = base::BindRepeating(
  183. &WriteHandler::WriteDataCallback, base::Unretained(this), entry, 0,
  184. data_len, kHeadersSize);
  185. int new_result = entry->WriteData(0, 0, headers_buffer_.get(), kHeadersSize,
  186. callback, false);
  187. if (new_result != net::ERR_IO_PENDING)
  188. callback.Run(new_result);
  189. }
  190. void WriteHandler::WriteDataCallback(disk_cache::Entry* entry,
  191. int next_offset,
  192. int data_len,
  193. int expected_result,
  194. int result) {
  195. if (CheckForErrorAndCancel(result)) {
  196. entry->Close();
  197. return;
  198. }
  199. DCHECK_LE(next_offset, data_len);
  200. if (next_offset == data_len) {
  201. entry->Close();
  202. if (next_entry_index_ < kNumEntries) {
  203. CreateNextEntry();
  204. } else {
  205. --pending_operations_count_;
  206. if (pending_operations_count_ == 0)
  207. std::move(final_callback_).Run(net::OK);
  208. }
  209. return;
  210. }
  211. int write_size = std::min(kChunkSize, data_len - next_offset);
  212. net::CompletionRepeatingCallback callback = base::BindRepeating(
  213. &WriteHandler::WriteDataCallback, base::Unretained(this), entry,
  214. next_offset + write_size, data_len, write_size);
  215. int new_result = entry->WriteData(1, next_offset, body_buffer_.get(),
  216. write_size, callback, true);
  217. if (new_result != net::ERR_IO_PENDING)
  218. callback.Run(new_result);
  219. }
  220. bool WriteHandler::CheckForErrorAndCancel(int result) {
  221. DCHECK_NE(net::ERR_IO_PENDING, result);
  222. if (result != net::OK && !(result > 0))
  223. pending_result_ = result;
  224. if (pending_result_ != net::OK) {
  225. --pending_operations_count_;
  226. if (pending_operations_count_ == 0)
  227. std::move(final_callback_).Run(pending_result_);
  228. return true;
  229. }
  230. return false;
  231. }
  232. class ReadHandler {
  233. public:
  234. ReadHandler(const DiskCachePerfTest* test,
  235. WhatToRead what_to_read,
  236. disk_cache::Backend* cache,
  237. net::CompletionOnceCallback final_callback)
  238. : test_(test),
  239. what_to_read_(what_to_read),
  240. cache_(cache),
  241. final_callback_(std::move(final_callback)) {
  242. for (auto& read_buffer : read_buffers_) {
  243. read_buffer = base::MakeRefCounted<net::IOBuffer>(
  244. std::max(kHeadersSize, kChunkSize));
  245. }
  246. }
  247. void Run();
  248. protected:
  249. void OpenNextEntry(int parallel_operation_index);
  250. void OpenCallback(int parallel_operation_index,
  251. int data_len,
  252. disk_cache::EntryResult result);
  253. void ReadDataCallback(int parallel_operation_index,
  254. disk_cache::Entry* entry,
  255. int next_offset,
  256. int data_len,
  257. int expected_result,
  258. int result);
  259. private:
  260. bool CheckForErrorAndCancel(int result);
  261. raw_ptr<const DiskCachePerfTest> test_;
  262. const WhatToRead what_to_read_;
  263. raw_ptr<disk_cache::Backend> cache_;
  264. net::CompletionOnceCallback final_callback_;
  265. size_t next_entry_index_ = 0;
  266. size_t pending_operations_count_ = 0;
  267. int pending_result_ = net::OK;
  268. scoped_refptr<net::IOBuffer> read_buffers_[kMaxParallelOperations];
  269. };
  270. void ReadHandler::Run() {
  271. for (int i = 0; i < kMaxParallelOperations; ++i) {
  272. OpenNextEntry(pending_operations_count_);
  273. ++pending_operations_count_;
  274. }
  275. }
  276. void ReadHandler::OpenNextEntry(int parallel_operation_index) {
  277. ASSERT_GT(kNumEntries, next_entry_index_);
  278. TestEntry test_entry = test_->entries()[next_entry_index_++];
  279. auto callback =
  280. base::BindRepeating(&ReadHandler::OpenCallback, base::Unretained(this),
  281. parallel_operation_index, test_entry.data_len);
  282. disk_cache::EntryResult result =
  283. cache_->OpenEntry(test_entry.key, net::HIGHEST, callback);
  284. if (result.net_error() != net::ERR_IO_PENDING)
  285. callback.Run(std::move(result));
  286. }
  287. void ReadHandler::OpenCallback(int parallel_operation_index,
  288. int data_len,
  289. disk_cache::EntryResult result) {
  290. if (CheckForErrorAndCancel(result.net_error()))
  291. return;
  292. disk_cache::Entry* entry = result.ReleaseEntry();
  293. EXPECT_EQ(data_len, entry->GetDataSize(1));
  294. net::CompletionRepeatingCallback callback = base::BindRepeating(
  295. &ReadHandler::ReadDataCallback, base::Unretained(this),
  296. parallel_operation_index, entry, 0, data_len, kHeadersSize);
  297. int new_result =
  298. entry->ReadData(0, 0, read_buffers_[parallel_operation_index].get(),
  299. kChunkSize, callback);
  300. if (new_result != net::ERR_IO_PENDING)
  301. callback.Run(new_result);
  302. }
  303. void ReadHandler::ReadDataCallback(int parallel_operation_index,
  304. disk_cache::Entry* entry,
  305. int next_offset,
  306. int data_len,
  307. int expected_result,
  308. int result) {
  309. if (CheckForErrorAndCancel(result)) {
  310. entry->Close();
  311. return;
  312. }
  313. DCHECK_LE(next_offset, data_len);
  314. if (what_to_read_ == WhatToRead::HEADERS_ONLY || next_offset == data_len) {
  315. entry->Close();
  316. if (next_entry_index_ < kNumEntries) {
  317. OpenNextEntry(parallel_operation_index);
  318. } else {
  319. --pending_operations_count_;
  320. if (pending_operations_count_ == 0)
  321. std::move(final_callback_).Run(net::OK);
  322. }
  323. return;
  324. }
  325. int expected_read_size = std::min(kChunkSize, data_len - next_offset);
  326. net::CompletionRepeatingCallback callback = base::BindRepeating(
  327. &ReadHandler::ReadDataCallback, base::Unretained(this),
  328. parallel_operation_index, entry, next_offset + expected_read_size,
  329. data_len, expected_read_size);
  330. int new_result = entry->ReadData(
  331. 1, next_offset, read_buffers_[parallel_operation_index].get(), kChunkSize,
  332. callback);
  333. if (new_result != net::ERR_IO_PENDING)
  334. callback.Run(new_result);
  335. }
  336. bool ReadHandler::CheckForErrorAndCancel(int result) {
  337. DCHECK_NE(net::ERR_IO_PENDING, result);
  338. if (result != net::OK && !(result > 0))
  339. pending_result_ = result;
  340. if (pending_result_ != net::OK) {
  341. --pending_operations_count_;
  342. if (pending_operations_count_ == 0)
  343. std::move(final_callback_).Run(pending_result_);
  344. return true;
  345. }
  346. return false;
  347. }
  348. bool DiskCachePerfTest::TimeWrites(const std::string& story) {
  349. for (size_t i = 0; i < kNumEntries; i++) {
  350. TestEntry entry;
  351. entry.key = GenerateKey(true);
  352. entry.data_len = base::RandInt(0, kBodySize);
  353. entries_.push_back(entry);
  354. }
  355. net::TestCompletionCallback cb;
  356. auto reporter = SetUpDiskCacheReporter(story);
  357. base::ElapsedTimer write_timer;
  358. WriteHandler write_handler(this, cache_.get(), cb.callback());
  359. write_handler.Run();
  360. auto result = cb.WaitForResult();
  361. reporter.AddResult(kMetricCacheEntriesWriteTimeMs,
  362. write_timer.Elapsed().InMillisecondsF());
  363. return result == net::OK;
  364. }
  365. bool DiskCachePerfTest::TimeReads(WhatToRead what_to_read,
  366. const std::string& metric,
  367. const std::string& story) {
  368. auto reporter = SetUpDiskCacheReporter(story);
  369. base::ElapsedTimer timer;
  370. net::TestCompletionCallback cb;
  371. ReadHandler read_handler(this, what_to_read, cache_.get(), cb.callback());
  372. read_handler.Run();
  373. auto result = cb.WaitForResult();
  374. reporter.AddResult(metric, timer.Elapsed().InMillisecondsF());
  375. return result == net::OK;
  376. }
  377. TEST_F(DiskCachePerfTest, BlockfileHashes) {
  378. auto reporter = SetUpDiskCacheReporter("baseline_story");
  379. base::ElapsedTimer timer;
  380. for (int i = 0; i < 300000; i++) {
  381. std::string key = GenerateKey(true);
  382. // TODO(dcheng): It's unclear if this is sufficient to keep a sufficiently
  383. // smart optimizer from simply discarding the function call if it realizes
  384. // there are no side effects.
  385. base::PersistentHash(key);
  386. }
  387. reporter.AddResult(kMetricCacheKeysHashTimeMs,
  388. timer.Elapsed().InMillisecondsF());
  389. }
  390. void DiskCachePerfTest::ResetAndEvictSystemDiskCache() {
  391. base::RunLoop().RunUntilIdle();
  392. cache_.reset();
  393. // Flush all files in the cache out of system memory.
  394. const base::FilePath::StringType file_pattern = FILE_PATH_LITERAL("*");
  395. base::FileEnumerator enumerator(cache_path_, true /* recursive */,
  396. base::FileEnumerator::FILES, file_pattern);
  397. for (base::FilePath file_path = enumerator.Next(); !file_path.empty();
  398. file_path = enumerator.Next()) {
  399. ASSERT_TRUE(base::EvictFileFromSystemCache(file_path));
  400. }
  401. #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
  402. // And, cache directories, on platforms where the eviction utility supports
  403. // this (currently Linux and Android only).
  404. if (simple_cache_mode_) {
  405. ASSERT_TRUE(
  406. base::EvictFileFromSystemCache(cache_path_.AppendASCII("index-dir")));
  407. }
  408. ASSERT_TRUE(base::EvictFileFromSystemCache(cache_path_));
  409. #endif
  410. DisableFirstCleanup();
  411. InitCache();
  412. }
  413. void DiskCachePerfTest::CacheBackendPerformance(const std::string& story) {
  414. base::test::ScopedRunLoopTimeout default_timeout(
  415. FROM_HERE, TestTimeouts::action_max_timeout());
  416. LOG(ERROR) << "Using cache at:" << cache_path_.MaybeAsASCII();
  417. SetMaxSize(500 * 1024 * 1024);
  418. InitCache();
  419. EXPECT_TRUE(TimeWrites(story));
  420. disk_cache::FlushCacheThreadForTesting();
  421. base::RunLoop().RunUntilIdle();
  422. ResetAndEvictSystemDiskCache();
  423. EXPECT_TRUE(TimeReads(WhatToRead::HEADERS_ONLY,
  424. kMetricCacheHeadersReadTimeColdMs, story));
  425. EXPECT_TRUE(TimeReads(WhatToRead::HEADERS_ONLY,
  426. kMetricCacheHeadersReadTimeWarmMs, story));
  427. disk_cache::FlushCacheThreadForTesting();
  428. base::RunLoop().RunUntilIdle();
  429. ResetAndEvictSystemDiskCache();
  430. EXPECT_TRUE(TimeReads(WhatToRead::HEADERS_AND_BODY,
  431. kMetricCacheEntriesReadTimeColdMs, story));
  432. EXPECT_TRUE(TimeReads(WhatToRead::HEADERS_AND_BODY,
  433. kMetricCacheEntriesReadTimeWarmMs, story));
  434. disk_cache::FlushCacheThreadForTesting();
  435. base::RunLoop().RunUntilIdle();
  436. }
  437. #if BUILDFLAG(IS_FUCHSIA)
  438. // TODO(crbug.com/851083): Fix this test on Fuchsia and re-enable.
  439. #define MAYBE_CacheBackendPerformance DISABLED_CacheBackendPerformance
  440. #else
  441. #define MAYBE_CacheBackendPerformance CacheBackendPerformance
  442. #endif
  443. TEST_F(DiskCachePerfTest, MAYBE_CacheBackendPerformance) {
  444. CacheBackendPerformance("blockfile_cache");
  445. }
  446. #if BUILDFLAG(IS_FUCHSIA)
  447. // TODO(crbug.com/851083): Fix this test on Fuchsia and re-enable.
  448. #define MAYBE_SimpleCacheBackendPerformance \
  449. DISABLED_SimpleCacheBackendPerformance
  450. #else
  451. #define MAYBE_SimpleCacheBackendPerformance SimpleCacheBackendPerformance
  452. #endif
  453. TEST_F(DiskCachePerfTest, MAYBE_SimpleCacheBackendPerformance) {
  454. SetSimpleCacheMode();
  455. CacheBackendPerformance("simple_cache");
  456. }
  457. // Creating and deleting "entries" on a block-file is something quite frequent
  458. // (after all, almost everything is stored on block files). The operation is
  459. // almost free when the file is empty, but can be expensive if the file gets
  460. // fragmented, or if we have multiple files. This test measures that scenario,
  461. // by using multiple, highly fragmented files.
  462. TEST_F(DiskCachePerfTest, BlockFilesPerformance) {
  463. ASSERT_TRUE(CleanupCacheDir());
  464. disk_cache::BlockFiles files(cache_path_);
  465. ASSERT_TRUE(files.Init(true));
  466. const int kNumBlocks = 60000;
  467. disk_cache::Addr address[kNumBlocks];
  468. auto reporter = SetUpDiskCacheReporter("blockfile_cache");
  469. base::ElapsedTimer sequential_timer;
  470. // Fill up the 32-byte block file (use three files).
  471. for (auto& addr : address) {
  472. int block_size = base::RandInt(1, 4);
  473. EXPECT_TRUE(files.CreateBlock(disk_cache::RANKINGS, block_size, &addr));
  474. }
  475. reporter.AddResult(kMetricFillBlocksTimeMs,
  476. sequential_timer.Elapsed().InMillisecondsF());
  477. base::ElapsedTimer random_timer;
  478. for (int i = 0; i < 200000; i++) {
  479. int block_size = base::RandInt(1, 4);
  480. int entry = base::RandInt(0, kNumBlocks - 1);
  481. files.DeleteBlock(address[entry], false);
  482. EXPECT_TRUE(
  483. files.CreateBlock(disk_cache::RANKINGS, block_size, &address[entry]));
  484. }
  485. reporter.AddResult(kMetricCreateDeleteBlocksTimeMs,
  486. random_timer.Elapsed().InMillisecondsF());
  487. base::RunLoop().RunUntilIdle();
  488. }
  489. void VerifyRvAndCallClosure(base::RepeatingClosure* c, int expect_rv, int rv) {
  490. EXPECT_EQ(expect_rv, rv);
  491. c->Run();
  492. }
  493. TEST_F(DiskCachePerfTest, SimpleCacheInitialReadPortion) {
  494. // A benchmark that aims to measure how much time we take in I/O thread
  495. // for initial bookkeeping before returning to the caller, and how much
  496. // after (batched up some). The later portion includes some event loop
  497. // overhead.
  498. const int kBatchSize = 100;
  499. SetSimpleCacheMode();
  500. InitCache();
  501. // Write out the entries, and keep their objects around.
  502. scoped_refptr<net::IOBuffer> buffer1 =
  503. base::MakeRefCounted<net::IOBuffer>(kHeadersSize);
  504. scoped_refptr<net::IOBuffer> buffer2 =
  505. base::MakeRefCounted<net::IOBuffer>(kBodySize);
  506. CacheTestFillBuffer(buffer1->data(), kHeadersSize, false);
  507. CacheTestFillBuffer(buffer2->data(), kBodySize, false);
  508. disk_cache::Entry* cache_entry[kBatchSize];
  509. for (int i = 0; i < kBatchSize; ++i) {
  510. TestEntryResultCompletionCallback cb_create;
  511. disk_cache::EntryResult result = cb_create.GetResult(cache_->CreateEntry(
  512. base::NumberToString(i), net::HIGHEST, cb_create.callback()));
  513. ASSERT_EQ(net::OK, result.net_error());
  514. cache_entry[i] = result.ReleaseEntry();
  515. net::TestCompletionCallback cb;
  516. int rv = cache_entry[i]->WriteData(0, 0, buffer1.get(), kHeadersSize,
  517. cb.callback(), false);
  518. ASSERT_EQ(kHeadersSize, cb.GetResult(rv));
  519. rv = cache_entry[i]->WriteData(1, 0, buffer2.get(), kBodySize,
  520. cb.callback(), false);
  521. ASSERT_EQ(kBodySize, cb.GetResult(rv));
  522. }
  523. // Now repeatedly read these, batching up the waiting to try to
  524. // account for the two portions separately. Note that we need separate entries
  525. // since we are trying to keep interesting work from being on the delayed-done
  526. // portion.
  527. const int kIterations = 50000;
  528. double elapsed_early = 0.0;
  529. double elapsed_late = 0.0;
  530. for (int i = 0; i < kIterations; ++i) {
  531. base::RunLoop event_loop;
  532. base::RepeatingClosure barrier =
  533. base::BarrierClosure(kBatchSize, event_loop.QuitWhenIdleClosure());
  534. net::CompletionRepeatingCallback cb_batch(base::BindRepeating(
  535. VerifyRvAndCallClosure, base::Unretained(&barrier), kHeadersSize));
  536. base::ElapsedTimer timer_early;
  537. for (auto* entry : cache_entry) {
  538. int rv = entry->ReadData(0, 0, buffer1.get(), kHeadersSize, cb_batch);
  539. if (rv != net::ERR_IO_PENDING) {
  540. barrier.Run();
  541. ASSERT_EQ(kHeadersSize, rv);
  542. }
  543. }
  544. elapsed_early += timer_early.Elapsed().InMillisecondsF();
  545. base::ElapsedTimer timer_late;
  546. event_loop.Run();
  547. elapsed_late += timer_late.Elapsed().InMillisecondsF();
  548. }
  549. // Cleanup
  550. for (auto* entry : cache_entry)
  551. entry->Close();
  552. disk_cache::FlushCacheThreadForTesting();
  553. base::RunLoop().RunUntilIdle();
  554. auto reporter = SetUpDiskCacheReporter("early_portion");
  555. reporter.AddResult(kMetricSimpleCacheInitTotalTimeMs, elapsed_early);
  556. reporter.AddResult(kMetricSimpleCacheInitPerEntryTimeUs,
  557. 1000 * (elapsed_early / (kIterations * kBatchSize)));
  558. reporter = SetUpDiskCacheReporter("event_loop_portion");
  559. reporter.AddResult(kMetricSimpleCacheInitTotalTimeMs, elapsed_late);
  560. reporter.AddResult(kMetricSimpleCacheInitPerEntryTimeUs,
  561. 1000 * (elapsed_late / (kIterations * kBatchSize)));
  562. }
  563. #if BUILDFLAG(IS_FUCHSIA)
  564. // TODO(crbug.com/1318120): Fix this test on Fuchsia and re-enable.
  565. #define MAYBE_EvictionPerformance DISABLED_EvictionPerformance
  566. #else
  567. #define MAYBE_EvictionPerformance EvictionPerformance
  568. #endif
  569. // Measures how quickly SimpleIndex can compute which entries to evict.
  570. TEST(SimpleIndexPerfTest, MAYBE_EvictionPerformance) {
  571. const int kEntries = 10000;
  572. class NoOpDelegate : public disk_cache::SimpleIndexDelegate {
  573. void DoomEntries(std::vector<uint64_t>* entry_hashes,
  574. net::CompletionOnceCallback callback) override {}
  575. };
  576. NoOpDelegate delegate;
  577. base::Time start(base::Time::Now());
  578. double evict_elapsed_ms = 0;
  579. int iterations = 0;
  580. while (iterations < 61000) {
  581. ++iterations;
  582. disk_cache::SimpleIndex index(/* io_thread = */ nullptr,
  583. /* cleanup_tracker = */ nullptr, &delegate,
  584. net::DISK_CACHE,
  585. /* simple_index_file = */ nullptr);
  586. // Make sure large enough to not evict on insertion.
  587. index.SetMaxSize(kEntries * 2);
  588. for (int i = 0; i < kEntries; ++i) {
  589. index.InsertEntryForTesting(
  590. i, disk_cache::EntryMetadata(start + base::Seconds(i), 1u));
  591. }
  592. // Trigger an eviction.
  593. base::ElapsedTimer timer;
  594. index.SetMaxSize(kEntries);
  595. index.UpdateEntrySize(0, 1u);
  596. evict_elapsed_ms += timer.Elapsed().InMillisecondsF();
  597. }
  598. auto reporter = SetUpSimpleIndexReporter("baseline_story");
  599. reporter.AddResult(kMetricAverageEvictionTimeMs,
  600. evict_elapsed_ms / iterations);
  601. }
  602. } // namespace