1*6777b538SAndroid Build Coastguard Worker // Copyright 2020 The Chromium Authors
2*6777b538SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*6777b538SAndroid Build Coastguard Worker // found in the LICENSE file.
4*6777b538SAndroid Build Coastguard Worker
5*6777b538SAndroid Build Coastguard Worker #include <stddef.h>
6*6777b538SAndroid Build Coastguard Worker
7*6777b538SAndroid Build Coastguard Worker #include <atomic>
8*6777b538SAndroid Build Coastguard Worker #include <optional>
9*6777b538SAndroid Build Coastguard Worker #include <utility>
10*6777b538SAndroid Build Coastguard Worker #include <vector>
11*6777b538SAndroid Build Coastguard Worker
12*6777b538SAndroid Build Coastguard Worker #include "base/containers/queue.h"
13*6777b538SAndroid Build Coastguard Worker #include "base/containers/stack.h"
14*6777b538SAndroid Build Coastguard Worker #include "base/functional/callback_helpers.h"
15*6777b538SAndroid Build Coastguard Worker #include "base/synchronization/lock.h"
16*6777b538SAndroid Build Coastguard Worker #include "base/task/post_job.h"
17*6777b538SAndroid Build Coastguard Worker #include "base/task/thread_pool.h"
18*6777b538SAndroid Build Coastguard Worker #include "base/test/bind.h"
19*6777b538SAndroid Build Coastguard Worker #include "base/test/task_environment.h"
20*6777b538SAndroid Build Coastguard Worker #include "testing/gtest/include/gtest/gtest.h"
21*6777b538SAndroid Build Coastguard Worker #include "testing/perf/perf_result_reporter.h"
22*6777b538SAndroid Build Coastguard Worker
23*6777b538SAndroid Build Coastguard Worker namespace base {
24*6777b538SAndroid Build Coastguard Worker
25*6777b538SAndroid Build Coastguard Worker namespace {
26*6777b538SAndroid Build Coastguard Worker
27*6777b538SAndroid Build Coastguard Worker // The perftest implements the following assignment strategy:
28*6777b538SAndroid Build Coastguard Worker // - Naive: See RunJobWithNaiveAssignment().
29*6777b538SAndroid Build Coastguard Worker // - Dynamic: See RunJobWithDynamicAssignment().
30*6777b538SAndroid Build Coastguard Worker // - Loop around: See RunJobWithLoopAround().
31*6777b538SAndroid Build Coastguard Worker // The following test setups exists for different strategies, although
32*6777b538SAndroid Build Coastguard Worker // not every combination is performed:
33*6777b538SAndroid Build Coastguard Worker // - No-op: Work items are no-op tasks.
34*6777b538SAndroid Build Coastguard Worker // - No-op + disrupted: 10 disruptive tasks are posted every 1ms.
35*6777b538SAndroid Build Coastguard Worker // - Busy wait: Work items are busy wait for 5us.
36*6777b538SAndroid Build Coastguard Worker // - Busy wait + disrupted
37*6777b538SAndroid Build Coastguard Worker
38*6777b538SAndroid Build Coastguard Worker constexpr char kMetricPrefixJob[] = "Job.";
39*6777b538SAndroid Build Coastguard Worker constexpr char kMetricWorkThroughput[] = "work_throughput";
40*6777b538SAndroid Build Coastguard Worker constexpr char kStoryNoOpNaive[] = "noop_naive";
41*6777b538SAndroid Build Coastguard Worker constexpr char kStoryBusyWaitNaive[] = "busy_wait_naive";
42*6777b538SAndroid Build Coastguard Worker constexpr char kStoryNoOpAtomic[] = "noop_atomic";
43*6777b538SAndroid Build Coastguard Worker constexpr char kStoryNoOpAtomicDisrupted[] = "noop_atomic_disrupted";
44*6777b538SAndroid Build Coastguard Worker constexpr char kStoryBusyWaitAtomic[] = "busy_wait_atomic";
45*6777b538SAndroid Build Coastguard Worker constexpr char kStoryBusyWaitAtomicDisrupted[] = "busy_wait_atomic_disrupted";
46*6777b538SAndroid Build Coastguard Worker constexpr char kStoryNoOpDynamic[] = "noop_dynamic";
47*6777b538SAndroid Build Coastguard Worker constexpr char kStoryNoOpDynamicDisrupted[] = "noop_dynamic_disrupted";
48*6777b538SAndroid Build Coastguard Worker constexpr char kStoryBusyWaitDynamic[] = "busy_wait_dynamic";
49*6777b538SAndroid Build Coastguard Worker constexpr char kStoryBusyWaitDynamicDisrupted[] = "busy_wait_dynamic_disrupted";
50*6777b538SAndroid Build Coastguard Worker constexpr char kStoryNoOpLoopAround[] = "noop_loop_around";
51*6777b538SAndroid Build Coastguard Worker constexpr char kStoryNoOpLoopAroundDisrupted[] = "noop_loop_around_disrupted";
52*6777b538SAndroid Build Coastguard Worker constexpr char kStoryBusyWaitLoopAround[] = "busy_wait_loop_around";
53*6777b538SAndroid Build Coastguard Worker constexpr char kStoryBusyWaitLoopAroundDisrupted[] =
54*6777b538SAndroid Build Coastguard Worker "busy_wait_loop_around_disrupted";
55*6777b538SAndroid Build Coastguard Worker
SetUpReporter(const std::string & story_name)56*6777b538SAndroid Build Coastguard Worker perf_test::PerfResultReporter SetUpReporter(const std::string& story_name) {
57*6777b538SAndroid Build Coastguard Worker perf_test::PerfResultReporter reporter(kMetricPrefixJob, story_name);
58*6777b538SAndroid Build Coastguard Worker reporter.RegisterImportantMetric(kMetricWorkThroughput, "tasks/ms");
59*6777b538SAndroid Build Coastguard Worker return reporter;
60*6777b538SAndroid Build Coastguard Worker }
61*6777b538SAndroid Build Coastguard Worker
62*6777b538SAndroid Build Coastguard Worker // A thread-safe data structure that generates heuristic starting points in a
63*6777b538SAndroid Build Coastguard Worker // range to process items in parallel.
64*6777b538SAndroid Build Coastguard Worker // Note: we could expose this atomic-binary-search-index-generator in
65*6777b538SAndroid Build Coastguard Worker // //base/util if it's useful for real-world use cases.
66*6777b538SAndroid Build Coastguard Worker class IndexGenerator {
67*6777b538SAndroid Build Coastguard Worker public:
IndexGenerator(size_t size)68*6777b538SAndroid Build Coastguard Worker explicit IndexGenerator(size_t size) : size_(size) {
69*6777b538SAndroid Build Coastguard Worker AutoLock auto_lock(lock_);
70*6777b538SAndroid Build Coastguard Worker pending_indices_.push(0);
71*6777b538SAndroid Build Coastguard Worker ranges_to_split_.push({0, size_});
72*6777b538SAndroid Build Coastguard Worker }
73*6777b538SAndroid Build Coastguard Worker
74*6777b538SAndroid Build Coastguard Worker IndexGenerator(const IndexGenerator&) = delete;
75*6777b538SAndroid Build Coastguard Worker IndexGenerator& operator=(const IndexGenerator&) = delete;
76*6777b538SAndroid Build Coastguard Worker
GetNext()77*6777b538SAndroid Build Coastguard Worker std::optional<size_t> GetNext() {
78*6777b538SAndroid Build Coastguard Worker AutoLock auto_lock(lock_);
79*6777b538SAndroid Build Coastguard Worker if (!pending_indices_.empty()) {
80*6777b538SAndroid Build Coastguard Worker // Return any pending index first.
81*6777b538SAndroid Build Coastguard Worker auto index = pending_indices_.top();
82*6777b538SAndroid Build Coastguard Worker pending_indices_.pop();
83*6777b538SAndroid Build Coastguard Worker return index;
84*6777b538SAndroid Build Coastguard Worker }
85*6777b538SAndroid Build Coastguard Worker if (ranges_to_split_.empty())
86*6777b538SAndroid Build Coastguard Worker return std::nullopt;
87*6777b538SAndroid Build Coastguard Worker
88*6777b538SAndroid Build Coastguard Worker // Split the oldest running range in 2 and return the middle index as
89*6777b538SAndroid Build Coastguard Worker // starting point.
90*6777b538SAndroid Build Coastguard Worker auto range = ranges_to_split_.front();
91*6777b538SAndroid Build Coastguard Worker ranges_to_split_.pop();
92*6777b538SAndroid Build Coastguard Worker size_t size = range.second - range.first;
93*6777b538SAndroid Build Coastguard Worker size_t mid = range.first + size / 2;
94*6777b538SAndroid Build Coastguard Worker // Both sides of the range are added to |ranges_to_split_| so they may be
95*6777b538SAndroid Build Coastguard Worker // further split if possible.
96*6777b538SAndroid Build Coastguard Worker if (mid - range.first > 1)
97*6777b538SAndroid Build Coastguard Worker ranges_to_split_.push({range.first, mid});
98*6777b538SAndroid Build Coastguard Worker if (range.second - mid > 1)
99*6777b538SAndroid Build Coastguard Worker ranges_to_split_.push({mid, range.second});
100*6777b538SAndroid Build Coastguard Worker return mid;
101*6777b538SAndroid Build Coastguard Worker }
102*6777b538SAndroid Build Coastguard Worker
GiveBack(size_t index)103*6777b538SAndroid Build Coastguard Worker void GiveBack(size_t index) {
104*6777b538SAndroid Build Coastguard Worker AutoLock auto_lock(lock_);
105*6777b538SAndroid Build Coastguard Worker // Add |index| to pending indices so GetNext() may return it before anything
106*6777b538SAndroid Build Coastguard Worker // else.
107*6777b538SAndroid Build Coastguard Worker pending_indices_.push(index);
108*6777b538SAndroid Build Coastguard Worker }
109*6777b538SAndroid Build Coastguard Worker
110*6777b538SAndroid Build Coastguard Worker private:
111*6777b538SAndroid Build Coastguard Worker base::Lock lock_;
112*6777b538SAndroid Build Coastguard Worker // Pending indices that are ready to be handed out, prioritized over
113*6777b538SAndroid Build Coastguard Worker // |pending_ranges_| when non-empty.
114*6777b538SAndroid Build Coastguard Worker base::stack<size_t> pending_indices_ GUARDED_BY(lock_);
115*6777b538SAndroid Build Coastguard Worker // Pending [start, end] (exclusive) ranges to split and hand out indices from.
116*6777b538SAndroid Build Coastguard Worker base::queue<std::pair<size_t, size_t>> ranges_to_split_ GUARDED_BY(lock_);
117*6777b538SAndroid Build Coastguard Worker const size_t size_;
118*6777b538SAndroid Build Coastguard Worker };
119*6777b538SAndroid Build Coastguard Worker
120*6777b538SAndroid Build Coastguard Worker struct WorkItem {
121*6777b538SAndroid Build Coastguard Worker std::atomic_bool acquire{false};
122*6777b538SAndroid Build Coastguard Worker
TryAcquirebase::__anon8b4b63c80111::WorkItem123*6777b538SAndroid Build Coastguard Worker bool TryAcquire() {
124*6777b538SAndroid Build Coastguard Worker // memory_order_relaxed is sufficient as the WorkItem's state itself hasn't
125*6777b538SAndroid Build Coastguard Worker // been modified since the beginning of its associated job. This is only
126*6777b538SAndroid Build Coastguard Worker // atomically acquiring the right to work on it.
127*6777b538SAndroid Build Coastguard Worker return acquire.exchange(true, std::memory_order_relaxed) == false;
128*6777b538SAndroid Build Coastguard Worker }
129*6777b538SAndroid Build Coastguard Worker };
130*6777b538SAndroid Build Coastguard Worker
131*6777b538SAndroid Build Coastguard Worker class WorkList {
132*6777b538SAndroid Build Coastguard Worker public:
WorkList(size_t num_work_items,RepeatingCallback<void (size_t)> process_item)133*6777b538SAndroid Build Coastguard Worker WorkList(size_t num_work_items, RepeatingCallback<void(size_t)> process_item)
134*6777b538SAndroid Build Coastguard Worker : num_incomplete_items_(num_work_items),
135*6777b538SAndroid Build Coastguard Worker items_(num_work_items),
136*6777b538SAndroid Build Coastguard Worker process_item_(std::move(process_item)) {}
137*6777b538SAndroid Build Coastguard Worker
138*6777b538SAndroid Build Coastguard Worker WorkList(const WorkList&) = delete;
139*6777b538SAndroid Build Coastguard Worker WorkList& operator=(const WorkList&) = delete;
140*6777b538SAndroid Build Coastguard Worker
141*6777b538SAndroid Build Coastguard Worker // Acquires work item at |index|. Returns true if successful, or false if the
142*6777b538SAndroid Build Coastguard Worker // item was already acquired.
TryAcquire(size_t index)143*6777b538SAndroid Build Coastguard Worker bool TryAcquire(size_t index) { return items_[index].TryAcquire(); }
144*6777b538SAndroid Build Coastguard Worker
145*6777b538SAndroid Build Coastguard Worker // Processes work item at |index|. Returns true if there are more work items
146*6777b538SAndroid Build Coastguard Worker // to process, or false if all items were processed.
ProcessWorkItem(size_t index)147*6777b538SAndroid Build Coastguard Worker bool ProcessWorkItem(size_t index) {
148*6777b538SAndroid Build Coastguard Worker process_item_.Run(index);
149*6777b538SAndroid Build Coastguard Worker return num_incomplete_items_.fetch_sub(1, std::memory_order_relaxed) > 1;
150*6777b538SAndroid Build Coastguard Worker }
151*6777b538SAndroid Build Coastguard Worker
NumIncompleteWorkItems(size_t) const152*6777b538SAndroid Build Coastguard Worker size_t NumIncompleteWorkItems(size_t /*worker_count*/) const {
153*6777b538SAndroid Build Coastguard Worker // memory_order_relaxed is sufficient since this is not synchronized with
154*6777b538SAndroid Build Coastguard Worker // other state.
155*6777b538SAndroid Build Coastguard Worker return num_incomplete_items_.load(std::memory_order_relaxed);
156*6777b538SAndroid Build Coastguard Worker }
157*6777b538SAndroid Build Coastguard Worker
NumWorkItems() const158*6777b538SAndroid Build Coastguard Worker size_t NumWorkItems() const { return items_.size(); }
159*6777b538SAndroid Build Coastguard Worker
160*6777b538SAndroid Build Coastguard Worker private:
161*6777b538SAndroid Build Coastguard Worker std::atomic_size_t num_incomplete_items_;
162*6777b538SAndroid Build Coastguard Worker std::vector<WorkItem> items_;
163*6777b538SAndroid Build Coastguard Worker RepeatingCallback<void(size_t)> process_item_;
164*6777b538SAndroid Build Coastguard Worker };
165*6777b538SAndroid Build Coastguard Worker
BusyWaitCallback(TimeDelta delta)166*6777b538SAndroid Build Coastguard Worker RepeatingCallback<void(size_t)> BusyWaitCallback(TimeDelta delta) {
167*6777b538SAndroid Build Coastguard Worker return base::BindRepeating(
168*6777b538SAndroid Build Coastguard Worker [](base::TimeDelta duration, size_t index) {
169*6777b538SAndroid Build Coastguard Worker const base::TimeTicks end_time = base::TimeTicks::Now() + duration;
170*6777b538SAndroid Build Coastguard Worker while (base::TimeTicks::Now() < end_time)
171*6777b538SAndroid Build Coastguard Worker ;
172*6777b538SAndroid Build Coastguard Worker },
173*6777b538SAndroid Build Coastguard Worker delta);
174*6777b538SAndroid Build Coastguard Worker }
175*6777b538SAndroid Build Coastguard Worker
176*6777b538SAndroid Build Coastguard Worker // Posts |task_count| no-op tasks every |delay|.
DisruptivePostTasks(size_t task_count,TimeDelta delay)177*6777b538SAndroid Build Coastguard Worker void DisruptivePostTasks(size_t task_count, TimeDelta delay) {
178*6777b538SAndroid Build Coastguard Worker for (size_t i = 0; i < task_count; ++i) {
179*6777b538SAndroid Build Coastguard Worker ThreadPool::PostTask(FROM_HERE, {TaskPriority::USER_BLOCKING}, DoNothing());
180*6777b538SAndroid Build Coastguard Worker }
181*6777b538SAndroid Build Coastguard Worker ThreadPool::PostDelayedTask(FROM_HERE, {TaskPriority::USER_BLOCKING},
182*6777b538SAndroid Build Coastguard Worker BindOnce(&DisruptivePostTasks, task_count, delay),
183*6777b538SAndroid Build Coastguard Worker delay);
184*6777b538SAndroid Build Coastguard Worker }
185*6777b538SAndroid Build Coastguard Worker
186*6777b538SAndroid Build Coastguard Worker class JobPerfTest : public testing::Test {
187*6777b538SAndroid Build Coastguard Worker public:
188*6777b538SAndroid Build Coastguard Worker JobPerfTest() = default;
189*6777b538SAndroid Build Coastguard Worker
190*6777b538SAndroid Build Coastguard Worker JobPerfTest(const JobPerfTest&) = delete;
191*6777b538SAndroid Build Coastguard Worker JobPerfTest& operator=(const JobPerfTest&) = delete;
192*6777b538SAndroid Build Coastguard Worker
193*6777b538SAndroid Build Coastguard Worker // Process |num_work_items| items with |process_item| in parallel. Work is
194*6777b538SAndroid Build Coastguard Worker // assigned by having each worker sequentially traversing all items and
195*6777b538SAndroid Build Coastguard Worker // acquiring unvisited ones.
RunJobWithNaiveAssignment(const std::string & story_name,size_t num_work_items,RepeatingCallback<void (size_t)> process_item)196*6777b538SAndroid Build Coastguard Worker void RunJobWithNaiveAssignment(const std::string& story_name,
197*6777b538SAndroid Build Coastguard Worker size_t num_work_items,
198*6777b538SAndroid Build Coastguard Worker RepeatingCallback<void(size_t)> process_item) {
199*6777b538SAndroid Build Coastguard Worker WorkList work_list(num_work_items, std::move(process_item));
200*6777b538SAndroid Build Coastguard Worker
201*6777b538SAndroid Build Coastguard Worker const TimeTicks job_run_start = TimeTicks::Now();
202*6777b538SAndroid Build Coastguard Worker
203*6777b538SAndroid Build Coastguard Worker WaitableEvent complete;
204*6777b538SAndroid Build Coastguard Worker auto handle = PostJob(
205*6777b538SAndroid Build Coastguard Worker FROM_HERE, {TaskPriority::USER_VISIBLE},
206*6777b538SAndroid Build Coastguard Worker BindRepeating(
207*6777b538SAndroid Build Coastguard Worker [](WorkList* work_list, WaitableEvent* complete,
208*6777b538SAndroid Build Coastguard Worker JobDelegate* delegate) {
209*6777b538SAndroid Build Coastguard Worker for (size_t i = 0; i < work_list->NumWorkItems() &&
210*6777b538SAndroid Build Coastguard Worker work_list->NumIncompleteWorkItems(0) != 0 &&
211*6777b538SAndroid Build Coastguard Worker !delegate->ShouldYield();
212*6777b538SAndroid Build Coastguard Worker ++i) {
213*6777b538SAndroid Build Coastguard Worker if (!work_list->TryAcquire(i))
214*6777b538SAndroid Build Coastguard Worker continue;
215*6777b538SAndroid Build Coastguard Worker if (!work_list->ProcessWorkItem(i)) {
216*6777b538SAndroid Build Coastguard Worker complete->Signal();
217*6777b538SAndroid Build Coastguard Worker return;
218*6777b538SAndroid Build Coastguard Worker }
219*6777b538SAndroid Build Coastguard Worker }
220*6777b538SAndroid Build Coastguard Worker },
221*6777b538SAndroid Build Coastguard Worker Unretained(&work_list), Unretained(&complete)),
222*6777b538SAndroid Build Coastguard Worker BindRepeating(&WorkList::NumIncompleteWorkItems,
223*6777b538SAndroid Build Coastguard Worker Unretained(&work_list)));
224*6777b538SAndroid Build Coastguard Worker
225*6777b538SAndroid Build Coastguard Worker complete.Wait();
226*6777b538SAndroid Build Coastguard Worker handle.Join();
227*6777b538SAndroid Build Coastguard Worker const TimeDelta job_duration = TimeTicks::Now() - job_run_start;
228*6777b538SAndroid Build Coastguard Worker EXPECT_EQ(0U, work_list.NumIncompleteWorkItems(0));
229*6777b538SAndroid Build Coastguard Worker
230*6777b538SAndroid Build Coastguard Worker auto reporter = SetUpReporter(story_name);
231*6777b538SAndroid Build Coastguard Worker reporter.AddResult(kMetricWorkThroughput,
232*6777b538SAndroid Build Coastguard Worker size_t(num_work_items / job_duration.InMilliseconds()));
233*6777b538SAndroid Build Coastguard Worker }
234*6777b538SAndroid Build Coastguard Worker
235*6777b538SAndroid Build Coastguard Worker // Process |num_work_items| items with |process_item| in parallel. Work is
236*6777b538SAndroid Build Coastguard Worker // assigned by having each worker sequentially traversing all items
237*6777b538SAndroid Build Coastguard Worker // synchronized with an atomic variable.
RunJobWithAtomicAssignment(const std::string & story_name,size_t num_work_items,RepeatingCallback<void (size_t)> process_item,bool disruptive_post_tasks=false)238*6777b538SAndroid Build Coastguard Worker void RunJobWithAtomicAssignment(const std::string& story_name,
239*6777b538SAndroid Build Coastguard Worker size_t num_work_items,
240*6777b538SAndroid Build Coastguard Worker RepeatingCallback<void(size_t)> process_item,
241*6777b538SAndroid Build Coastguard Worker bool disruptive_post_tasks = false) {
242*6777b538SAndroid Build Coastguard Worker WorkList work_list(num_work_items, std::move(process_item));
243*6777b538SAndroid Build Coastguard Worker std::atomic_size_t index{0};
244*6777b538SAndroid Build Coastguard Worker
245*6777b538SAndroid Build Coastguard Worker // Post extra tasks to disrupt Job execution and cause workers to yield.
246*6777b538SAndroid Build Coastguard Worker if (disruptive_post_tasks)
247*6777b538SAndroid Build Coastguard Worker DisruptivePostTasks(10, Milliseconds(1));
248*6777b538SAndroid Build Coastguard Worker
249*6777b538SAndroid Build Coastguard Worker const TimeTicks job_run_start = TimeTicks::Now();
250*6777b538SAndroid Build Coastguard Worker
251*6777b538SAndroid Build Coastguard Worker WaitableEvent complete;
252*6777b538SAndroid Build Coastguard Worker auto handle = PostJob(
253*6777b538SAndroid Build Coastguard Worker FROM_HERE, {TaskPriority::USER_VISIBLE},
254*6777b538SAndroid Build Coastguard Worker BindRepeating(
255*6777b538SAndroid Build Coastguard Worker [](WorkList* work_list, WaitableEvent* complete,
256*6777b538SAndroid Build Coastguard Worker std::atomic_size_t* index, JobDelegate* delegate) {
257*6777b538SAndroid Build Coastguard Worker while (!delegate->ShouldYield()) {
258*6777b538SAndroid Build Coastguard Worker const size_t i = index->fetch_add(1, std::memory_order_relaxed);
259*6777b538SAndroid Build Coastguard Worker if (i >= work_list->NumWorkItems() ||
260*6777b538SAndroid Build Coastguard Worker !work_list->ProcessWorkItem(i)) {
261*6777b538SAndroid Build Coastguard Worker complete->Signal();
262*6777b538SAndroid Build Coastguard Worker return;
263*6777b538SAndroid Build Coastguard Worker }
264*6777b538SAndroid Build Coastguard Worker }
265*6777b538SAndroid Build Coastguard Worker },
266*6777b538SAndroid Build Coastguard Worker Unretained(&work_list), Unretained(&complete), Unretained(&index)),
267*6777b538SAndroid Build Coastguard Worker BindRepeating(&WorkList::NumIncompleteWorkItems,
268*6777b538SAndroid Build Coastguard Worker Unretained(&work_list)));
269*6777b538SAndroid Build Coastguard Worker
270*6777b538SAndroid Build Coastguard Worker complete.Wait();
271*6777b538SAndroid Build Coastguard Worker handle.Join();
272*6777b538SAndroid Build Coastguard Worker const TimeDelta job_duration = TimeTicks::Now() - job_run_start;
273*6777b538SAndroid Build Coastguard Worker EXPECT_EQ(0U, work_list.NumIncompleteWorkItems(0));
274*6777b538SAndroid Build Coastguard Worker
275*6777b538SAndroid Build Coastguard Worker auto reporter = SetUpReporter(story_name);
276*6777b538SAndroid Build Coastguard Worker reporter.AddResult(kMetricWorkThroughput,
277*6777b538SAndroid Build Coastguard Worker size_t(num_work_items / job_duration.InMilliseconds()));
278*6777b538SAndroid Build Coastguard Worker }
279*6777b538SAndroid Build Coastguard Worker
280*6777b538SAndroid Build Coastguard Worker // Process |num_work_items| items with |process_item| in parallel. Work is
281*6777b538SAndroid Build Coastguard Worker // assigned dynamically having each new worker given a different point far
282*6777b538SAndroid Build Coastguard Worker // from other workers until all work is done. This is achieved by recursively
283*6777b538SAndroid Build Coastguard Worker // splitting each range that was previously given in half.
RunJobWithDynamicAssignment(const std::string & story_name,size_t num_work_items,RepeatingCallback<void (size_t)> process_item,bool disruptive_post_tasks=false)284*6777b538SAndroid Build Coastguard Worker void RunJobWithDynamicAssignment(const std::string& story_name,
285*6777b538SAndroid Build Coastguard Worker size_t num_work_items,
286*6777b538SAndroid Build Coastguard Worker RepeatingCallback<void(size_t)> process_item,
287*6777b538SAndroid Build Coastguard Worker bool disruptive_post_tasks = false) {
288*6777b538SAndroid Build Coastguard Worker WorkList work_list(num_work_items, std::move(process_item));
289*6777b538SAndroid Build Coastguard Worker IndexGenerator generator(num_work_items);
290*6777b538SAndroid Build Coastguard Worker
291*6777b538SAndroid Build Coastguard Worker // Post extra tasks to disrupt Job execution and cause workers to yield.
292*6777b538SAndroid Build Coastguard Worker if (disruptive_post_tasks)
293*6777b538SAndroid Build Coastguard Worker DisruptivePostTasks(10, Milliseconds(1));
294*6777b538SAndroid Build Coastguard Worker
295*6777b538SAndroid Build Coastguard Worker const TimeTicks job_run_start = TimeTicks::Now();
296*6777b538SAndroid Build Coastguard Worker
297*6777b538SAndroid Build Coastguard Worker WaitableEvent complete;
298*6777b538SAndroid Build Coastguard Worker auto handle = PostJob(
299*6777b538SAndroid Build Coastguard Worker FROM_HERE, {TaskPriority::USER_VISIBLE},
300*6777b538SAndroid Build Coastguard Worker BindRepeating(
301*6777b538SAndroid Build Coastguard Worker [](IndexGenerator* generator, WorkList* work_list,
302*6777b538SAndroid Build Coastguard Worker WaitableEvent* complete, JobDelegate* delegate) {
303*6777b538SAndroid Build Coastguard Worker while (work_list->NumIncompleteWorkItems(0) != 0 &&
304*6777b538SAndroid Build Coastguard Worker !delegate->ShouldYield()) {
305*6777b538SAndroid Build Coastguard Worker std::optional<size_t> index = generator->GetNext();
306*6777b538SAndroid Build Coastguard Worker if (!index)
307*6777b538SAndroid Build Coastguard Worker return;
308*6777b538SAndroid Build Coastguard Worker for (size_t i = *index; i < work_list->NumWorkItems(); ++i) {
309*6777b538SAndroid Build Coastguard Worker if (delegate->ShouldYield()) {
310*6777b538SAndroid Build Coastguard Worker generator->GiveBack(i);
311*6777b538SAndroid Build Coastguard Worker return;
312*6777b538SAndroid Build Coastguard Worker }
313*6777b538SAndroid Build Coastguard Worker if (!work_list->TryAcquire(i)) {
314*6777b538SAndroid Build Coastguard Worker // If this was touched already, get a new starting point.
315*6777b538SAndroid Build Coastguard Worker break;
316*6777b538SAndroid Build Coastguard Worker }
317*6777b538SAndroid Build Coastguard Worker if (!work_list->ProcessWorkItem(i)) {
318*6777b538SAndroid Build Coastguard Worker complete->Signal();
319*6777b538SAndroid Build Coastguard Worker return;
320*6777b538SAndroid Build Coastguard Worker }
321*6777b538SAndroid Build Coastguard Worker }
322*6777b538SAndroid Build Coastguard Worker }
323*6777b538SAndroid Build Coastguard Worker },
324*6777b538SAndroid Build Coastguard Worker Unretained(&generator), Unretained(&work_list),
325*6777b538SAndroid Build Coastguard Worker Unretained(&complete)),
326*6777b538SAndroid Build Coastguard Worker BindRepeating(&WorkList::NumIncompleteWorkItems,
327*6777b538SAndroid Build Coastguard Worker Unretained(&work_list)));
328*6777b538SAndroid Build Coastguard Worker
329*6777b538SAndroid Build Coastguard Worker complete.Wait();
330*6777b538SAndroid Build Coastguard Worker handle.Join();
331*6777b538SAndroid Build Coastguard Worker const TimeDelta job_duration = TimeTicks::Now() - job_run_start;
332*6777b538SAndroid Build Coastguard Worker EXPECT_EQ(0U, work_list.NumIncompleteWorkItems(0));
333*6777b538SAndroid Build Coastguard Worker
334*6777b538SAndroid Build Coastguard Worker auto reporter = SetUpReporter(story_name);
335*6777b538SAndroid Build Coastguard Worker reporter.AddResult(kMetricWorkThroughput,
336*6777b538SAndroid Build Coastguard Worker size_t(num_work_items / job_duration.InMilliseconds()));
337*6777b538SAndroid Build Coastguard Worker }
338*6777b538SAndroid Build Coastguard Worker
339*6777b538SAndroid Build Coastguard Worker // Process |num_work_items| items with |process_item| in parallel. Work is
340*6777b538SAndroid Build Coastguard Worker // assigned having each new worker given a different starting point far from
341*6777b538SAndroid Build Coastguard Worker // other workers and loop over all work items from there. This is achieved by
342*6777b538SAndroid Build Coastguard Worker // recursively splitting each range that was previously given in half.
RunJobWithLoopAround(const std::string & story_name,size_t num_work_items,RepeatingCallback<void (size_t)> process_item,bool disruptive_post_tasks=false)343*6777b538SAndroid Build Coastguard Worker void RunJobWithLoopAround(const std::string& story_name,
344*6777b538SAndroid Build Coastguard Worker size_t num_work_items,
345*6777b538SAndroid Build Coastguard Worker RepeatingCallback<void(size_t)> process_item,
346*6777b538SAndroid Build Coastguard Worker bool disruptive_post_tasks = false) {
347*6777b538SAndroid Build Coastguard Worker WorkList work_list(num_work_items, std::move(process_item));
348*6777b538SAndroid Build Coastguard Worker IndexGenerator generator(num_work_items);
349*6777b538SAndroid Build Coastguard Worker
350*6777b538SAndroid Build Coastguard Worker // Post extra tasks to disrupt Job execution and cause workers to yield.
351*6777b538SAndroid Build Coastguard Worker if (disruptive_post_tasks)
352*6777b538SAndroid Build Coastguard Worker DisruptivePostTasks(10, Milliseconds(1));
353*6777b538SAndroid Build Coastguard Worker
354*6777b538SAndroid Build Coastguard Worker const TimeTicks job_run_start = TimeTicks::Now();
355*6777b538SAndroid Build Coastguard Worker
356*6777b538SAndroid Build Coastguard Worker WaitableEvent complete;
357*6777b538SAndroid Build Coastguard Worker auto handle =
358*6777b538SAndroid Build Coastguard Worker PostJob(FROM_HERE, {TaskPriority::USER_VISIBLE},
359*6777b538SAndroid Build Coastguard Worker BindRepeating(
360*6777b538SAndroid Build Coastguard Worker [](IndexGenerator* generator, WorkList* work_list,
361*6777b538SAndroid Build Coastguard Worker WaitableEvent* complete, JobDelegate* delegate) {
362*6777b538SAndroid Build Coastguard Worker std::optional<size_t> index = generator->GetNext();
363*6777b538SAndroid Build Coastguard Worker if (!index)
364*6777b538SAndroid Build Coastguard Worker return;
365*6777b538SAndroid Build Coastguard Worker size_t i = *index;
366*6777b538SAndroid Build Coastguard Worker while (true) {
367*6777b538SAndroid Build Coastguard Worker if (delegate->ShouldYield()) {
368*6777b538SAndroid Build Coastguard Worker generator->GiveBack(i);
369*6777b538SAndroid Build Coastguard Worker return;
370*6777b538SAndroid Build Coastguard Worker }
371*6777b538SAndroid Build Coastguard Worker if (!work_list->TryAcquire(i)) {
372*6777b538SAndroid Build Coastguard Worker // If this was touched already, skip.
373*6777b538SAndroid Build Coastguard Worker continue;
374*6777b538SAndroid Build Coastguard Worker }
375*6777b538SAndroid Build Coastguard Worker if (!work_list->ProcessWorkItem(i)) {
376*6777b538SAndroid Build Coastguard Worker // This will cause the loop to exit if there's no work
377*6777b538SAndroid Build Coastguard Worker // left.
378*6777b538SAndroid Build Coastguard Worker complete->Signal();
379*6777b538SAndroid Build Coastguard Worker return;
380*6777b538SAndroid Build Coastguard Worker }
381*6777b538SAndroid Build Coastguard Worker ++i;
382*6777b538SAndroid Build Coastguard Worker if (i == work_list->NumWorkItems())
383*6777b538SAndroid Build Coastguard Worker i = 0;
384*6777b538SAndroid Build Coastguard Worker }
385*6777b538SAndroid Build Coastguard Worker },
386*6777b538SAndroid Build Coastguard Worker Unretained(&generator), Unretained(&work_list),
387*6777b538SAndroid Build Coastguard Worker Unretained(&complete)),
388*6777b538SAndroid Build Coastguard Worker BindRepeating(&WorkList::NumIncompleteWorkItems,
389*6777b538SAndroid Build Coastguard Worker Unretained(&work_list)));
390*6777b538SAndroid Build Coastguard Worker
391*6777b538SAndroid Build Coastguard Worker complete.Wait();
392*6777b538SAndroid Build Coastguard Worker handle.Join();
393*6777b538SAndroid Build Coastguard Worker const TimeDelta job_duration = TimeTicks::Now() - job_run_start;
394*6777b538SAndroid Build Coastguard Worker EXPECT_EQ(0U, work_list.NumIncompleteWorkItems(0));
395*6777b538SAndroid Build Coastguard Worker
396*6777b538SAndroid Build Coastguard Worker auto reporter = SetUpReporter(story_name);
397*6777b538SAndroid Build Coastguard Worker reporter.AddResult(kMetricWorkThroughput,
398*6777b538SAndroid Build Coastguard Worker size_t(num_work_items / job_duration.InMilliseconds()));
399*6777b538SAndroid Build Coastguard Worker }
400*6777b538SAndroid Build Coastguard Worker
401*6777b538SAndroid Build Coastguard Worker private:
402*6777b538SAndroid Build Coastguard Worker test::TaskEnvironment task_environment;
403*6777b538SAndroid Build Coastguard Worker };
404*6777b538SAndroid Build Coastguard Worker
405*6777b538SAndroid Build Coastguard Worker } // namespace
406*6777b538SAndroid Build Coastguard Worker
TEST_F(JobPerfTest,NoOpWorkNaiveAssignment)407*6777b538SAndroid Build Coastguard Worker TEST_F(JobPerfTest, NoOpWorkNaiveAssignment) {
408*6777b538SAndroid Build Coastguard Worker RunJobWithNaiveAssignment(kStoryNoOpNaive, 10000000, DoNothing());
409*6777b538SAndroid Build Coastguard Worker }
410*6777b538SAndroid Build Coastguard Worker
TEST_F(JobPerfTest,BusyWaitNaiveAssignment)411*6777b538SAndroid Build Coastguard Worker TEST_F(JobPerfTest, BusyWaitNaiveAssignment) {
412*6777b538SAndroid Build Coastguard Worker RepeatingCallback<void(size_t)> callback = BusyWaitCallback(Microseconds(5));
413*6777b538SAndroid Build Coastguard Worker RunJobWithNaiveAssignment(kStoryBusyWaitNaive, 500000, std::move(callback));
414*6777b538SAndroid Build Coastguard Worker }
415*6777b538SAndroid Build Coastguard Worker
TEST_F(JobPerfTest,NoOpWorkAtomicAssignment)416*6777b538SAndroid Build Coastguard Worker TEST_F(JobPerfTest, NoOpWorkAtomicAssignment) {
417*6777b538SAndroid Build Coastguard Worker RunJobWithAtomicAssignment(kStoryNoOpAtomic, 10000000, DoNothing());
418*6777b538SAndroid Build Coastguard Worker }
419*6777b538SAndroid Build Coastguard Worker
TEST_F(JobPerfTest,NoOpDisruptedWorkAtomicAssignment)420*6777b538SAndroid Build Coastguard Worker TEST_F(JobPerfTest, NoOpDisruptedWorkAtomicAssignment) {
421*6777b538SAndroid Build Coastguard Worker RunJobWithAtomicAssignment(kStoryNoOpAtomicDisrupted, 10000000, DoNothing(),
422*6777b538SAndroid Build Coastguard Worker true);
423*6777b538SAndroid Build Coastguard Worker }
424*6777b538SAndroid Build Coastguard Worker
TEST_F(JobPerfTest,BusyWaitAtomicAssignment)425*6777b538SAndroid Build Coastguard Worker TEST_F(JobPerfTest, BusyWaitAtomicAssignment) {
426*6777b538SAndroid Build Coastguard Worker RepeatingCallback<void(size_t)> callback = BusyWaitCallback(Microseconds(5));
427*6777b538SAndroid Build Coastguard Worker RunJobWithAtomicAssignment(kStoryBusyWaitAtomic, 500000, std::move(callback));
428*6777b538SAndroid Build Coastguard Worker }
429*6777b538SAndroid Build Coastguard Worker
TEST_F(JobPerfTest,BusyWaitDisruptedWorkAtomicAssignment)430*6777b538SAndroid Build Coastguard Worker TEST_F(JobPerfTest, BusyWaitDisruptedWorkAtomicAssignment) {
431*6777b538SAndroid Build Coastguard Worker RepeatingCallback<void(size_t)> callback = BusyWaitCallback(Microseconds(5));
432*6777b538SAndroid Build Coastguard Worker RunJobWithAtomicAssignment(kStoryBusyWaitAtomicDisrupted, 500000,
433*6777b538SAndroid Build Coastguard Worker std::move(callback), true);
434*6777b538SAndroid Build Coastguard Worker }
435*6777b538SAndroid Build Coastguard Worker
TEST_F(JobPerfTest,NoOpWorkDynamicAssignment)436*6777b538SAndroid Build Coastguard Worker TEST_F(JobPerfTest, NoOpWorkDynamicAssignment) {
437*6777b538SAndroid Build Coastguard Worker RunJobWithDynamicAssignment(kStoryNoOpDynamic, 10000000, DoNothing());
438*6777b538SAndroid Build Coastguard Worker }
439*6777b538SAndroid Build Coastguard Worker
TEST_F(JobPerfTest,NoOpDisruptedWorkDynamicAssignment)440*6777b538SAndroid Build Coastguard Worker TEST_F(JobPerfTest, NoOpDisruptedWorkDynamicAssignment) {
441*6777b538SAndroid Build Coastguard Worker RunJobWithDynamicAssignment(kStoryNoOpDynamicDisrupted, 10000000, DoNothing(),
442*6777b538SAndroid Build Coastguard Worker true);
443*6777b538SAndroid Build Coastguard Worker }
444*6777b538SAndroid Build Coastguard Worker
TEST_F(JobPerfTest,BusyWaitWorkDynamicAssignment)445*6777b538SAndroid Build Coastguard Worker TEST_F(JobPerfTest, BusyWaitWorkDynamicAssignment) {
446*6777b538SAndroid Build Coastguard Worker RepeatingCallback<void(size_t)> callback = BusyWaitCallback(Microseconds(5));
447*6777b538SAndroid Build Coastguard Worker RunJobWithDynamicAssignment(kStoryBusyWaitDynamic, 500000,
448*6777b538SAndroid Build Coastguard Worker std::move(callback));
449*6777b538SAndroid Build Coastguard Worker }
450*6777b538SAndroid Build Coastguard Worker
TEST_F(JobPerfTest,BusyWaitDisruptedWorkDynamicAssignment)451*6777b538SAndroid Build Coastguard Worker TEST_F(JobPerfTest, BusyWaitDisruptedWorkDynamicAssignment) {
452*6777b538SAndroid Build Coastguard Worker RepeatingCallback<void(size_t)> callback = BusyWaitCallback(Microseconds(5));
453*6777b538SAndroid Build Coastguard Worker RunJobWithDynamicAssignment(kStoryBusyWaitDynamicDisrupted, 500000,
454*6777b538SAndroid Build Coastguard Worker std::move(callback), true);
455*6777b538SAndroid Build Coastguard Worker }
456*6777b538SAndroid Build Coastguard Worker
TEST_F(JobPerfTest,NoOpWorkLoopAround)457*6777b538SAndroid Build Coastguard Worker TEST_F(JobPerfTest, NoOpWorkLoopAround) {
458*6777b538SAndroid Build Coastguard Worker RunJobWithLoopAround(kStoryNoOpLoopAround, 10000000, DoNothing());
459*6777b538SAndroid Build Coastguard Worker }
460*6777b538SAndroid Build Coastguard Worker
TEST_F(JobPerfTest,NoOpDisruptedWorkLoopAround)461*6777b538SAndroid Build Coastguard Worker TEST_F(JobPerfTest, NoOpDisruptedWorkLoopAround) {
462*6777b538SAndroid Build Coastguard Worker RunJobWithLoopAround(kStoryNoOpLoopAroundDisrupted, 10000000, DoNothing(),
463*6777b538SAndroid Build Coastguard Worker true);
464*6777b538SAndroid Build Coastguard Worker }
465*6777b538SAndroid Build Coastguard Worker
TEST_F(JobPerfTest,BusyWaitWorkLoopAround)466*6777b538SAndroid Build Coastguard Worker TEST_F(JobPerfTest, BusyWaitWorkLoopAround) {
467*6777b538SAndroid Build Coastguard Worker RepeatingCallback<void(size_t)> callback = BusyWaitCallback(Microseconds(5));
468*6777b538SAndroid Build Coastguard Worker RunJobWithLoopAround(kStoryBusyWaitLoopAround, 500000, std::move(callback));
469*6777b538SAndroid Build Coastguard Worker }
470*6777b538SAndroid Build Coastguard Worker
TEST_F(JobPerfTest,BusyWaitDisruptedWorkLoopAround)471*6777b538SAndroid Build Coastguard Worker TEST_F(JobPerfTest, BusyWaitDisruptedWorkLoopAround) {
472*6777b538SAndroid Build Coastguard Worker RepeatingCallback<void(size_t)> callback = BusyWaitCallback(Microseconds(5));
473*6777b538SAndroid Build Coastguard Worker RunJobWithLoopAround(kStoryBusyWaitLoopAroundDisrupted, 500000,
474*6777b538SAndroid Build Coastguard Worker std::move(callback), true);
475*6777b538SAndroid Build Coastguard Worker }
476*6777b538SAndroid Build Coastguard Worker
477*6777b538SAndroid Build Coastguard Worker } // namespace base
478