| Index: chrome/browser/experiments/memory_ablation_experiment.cc
|
| diff --git a/chrome/browser/experiments/memory_ablation_experiment.cc b/chrome/browser/experiments/memory_ablation_experiment.cc
|
| index b5abe0ab7b3c4f9bfecfa08d83e1a3e105538cc5..7eaf2b4c682382aefe979d39334e175671348ad3 100644
|
| --- a/chrome/browser/experiments/memory_ablation_experiment.cc
|
| +++ b/chrome/browser/experiments/memory_ablation_experiment.cc
|
| @@ -4,6 +4,7 @@
|
|
|
| #include "chrome/browser/experiments/memory_ablation_experiment.h"
|
|
|
| +#include <algorithm>
|
| #include <limits>
|
|
|
| #include "base/bind.h"
|
| @@ -21,7 +22,17 @@ const char kMemoryAblationFeatureSizeParam[] = "Size";
|
| const char kMemoryAblationFeatureMinRAMParam[] = "MinRAM";
|
| const char kMemoryAblationFeatureMaxRAMParam[] = "MaxRAM";
|
|
|
| -constexpr size_t kMemoryAblationDelaySeconds = 5;
|
| +// Since MaybeStart() is called during startup, we delay the allocation
|
| +// to avoid slowing things down.
|
| +constexpr size_t kAllocationDelaySeconds = 5;
|
| +
|
| +// We need to touch allocated memory to "dirty" it. However, touching
|
| +// large chunks of memory (even a single byte per page) can be costly
|
| +// (~60ms per 10MiB on Nexus 5). So we touch in chunks to allow other
|
| +// things to happen in between. With the following values we'll touch
|
| +// 2MiB per second, spending ~10% of 250ms time slice per chunk.
|
| +constexpr size_t kTouchDelayMilliseconds = 250;
|
| +constexpr size_t kTouchChunkSize = 512 * 1024;
|
|
|
| MemoryAblationExperiment::MemoryAblationExperiment() {}
|
|
|
| @@ -56,26 +67,42 @@ void MemoryAblationExperiment::MaybeStart(
|
| void MemoryAblationExperiment::Start(
|
| scoped_refptr<base::SequencedTaskRunner> task_runner,
|
| size_t memory_size) {
|
| - task_runner->PostDelayedTask(
|
| + DCHECK(task_runner_ == nullptr) << "Already started";
|
| + task_runner_ = task_runner;
|
| + // This class is a singleton, so "Unretained(this)" below is fine.
|
| + task_runner_->PostDelayedTask(
|
| FROM_HERE,
|
| base::BindOnce(&MemoryAblationExperiment::AllocateMemory,
|
| base::Unretained(this), memory_size),
|
| - base::TimeDelta::FromSeconds(kMemoryAblationDelaySeconds));
|
| + base::TimeDelta::FromSeconds(kAllocationDelaySeconds));
|
| }
|
|
|
| void MemoryAblationExperiment::AllocateMemory(size_t size) {
|
| memory_size_ = size;
|
| memory_.reset(new uint8_t[size]);
|
| - TouchMemory();
|
| + ScheduleTouchMemory(0);
|
| }
|
|
|
| -void MemoryAblationExperiment::TouchMemory() {
|
| +void MemoryAblationExperiment::TouchMemory(size_t offset) {
|
| if (memory_) {
|
| size_t page_size = base::GetPageSize();
|
| auto* memory = static_cast<volatile uint8_t*>(memory_.get());
|
| - for (size_t i = 0; i < memory_size_; i += page_size) {
|
| - memory[i] = i;
|
| + size_t max_offset = std::min(offset + kTouchChunkSize, memory_size_);
|
| + for (; offset < max_offset; offset += page_size) {
|
| + memory[offset] = static_cast<uint8_t>(offset);
|
| }
|
| base::debug::Alias(memory_.get());
|
| + if (offset < memory_size_) {
|
| + ScheduleTouchMemory(offset);
|
| + }
|
| }
|
| }
|
| +
|
| +void MemoryAblationExperiment::ScheduleTouchMemory(size_t offset) {
|
| + // This class is a singleton, so "Unretained(this)" below is fine.
|
| + task_runner_->PostDelayedTask(
|
| + FROM_HERE,
|
| + base::BindOnce(&MemoryAblationExperiment::TouchMemory,
|
| + base::Unretained(this), offset),
|
| + base::TimeDelta::FromMilliseconds(kTouchDelayMilliseconds));
|
| +}
|
|
|