Welcome to mirror list, hosted at ThFree Co, Russian Federation.

test.cc « profiler - github.com/google/ruy.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 6a8fbda07a6484330ae7d6c506344a738c702024 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
/* Copyright 2020 Google LLC. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

#include <chrono>
#include <random>
#include <thread>

#include "testing/base/public/gunit.h"
#include "profiler/profiler.h"
#include "profiler/test_instrumented_library.h"
#include "profiler/treeview.h"

namespace ruy {
namespace profiler {
namespace {

void DoSomeMergeSort(int size) {
  std::vector<int> data(size);

  std::default_random_engine engine;
  for (auto& val : data) {
    val = engine();
  }

  MergeSort(size, data.data());
}

// The purpose of this basic test is to cover the basic path that will be taken
// by a majority of users, not inspecting treeviews but just implicitly printing
// them on stdout, and to have this test enabled even when RUY_PROFILER is not
// defined, so that we have coverage for the non-RUY_PROFILER case.
TEST(ProfilerTest, MergeSortSingleThreadBasicTestEvenWithoutProfiler) {
  {
    ScopeProfile profile;
    DoSomeMergeSort(1 << 20);
  }
}

#ifdef RUY_PROFILER

TEST(ProfilerTest, MergeSortSingleThread) {
  TreeView treeview;
  {
    ScopeProfile profile;
    profile.SetUserTreeView(&treeview);
    DoSomeMergeSort(1 << 20);
  }
  Print(treeview);
  EXPECT_EQ(treeview.thread_roots().size(), 1);
  const auto& thread_root = *treeview.thread_roots().begin()->second;
  EXPECT_EQ(DepthOfTreeBelow(thread_root), 22);
  EXPECT_GE(
      WeightBelowNodeMatchingUnformatted(thread_root, "Merging sorted halves"),
      0.1 * thread_root.weight);
  EXPECT_GE(WeightBelowNodeMatchingFormatted(
                thread_root, "MergeSortRecurse (level=20, size=1)"),
            0.01 * thread_root.weight);

  TreeView treeview_collapsed;
  CollapseNodesMatchingUnformatted(treeview, 5, "MergeSort (size=%d)",
                                   &treeview_collapsed);
  Print(treeview_collapsed);
  const auto& collapsed_thread_root =
      *treeview_collapsed.thread_roots().begin()->second;
  EXPECT_EQ(DepthOfTreeBelow(collapsed_thread_root), 6);
  EXPECT_EQ(
      WeightBelowNodeMatchingUnformatted(thread_root, "MergeSort (size=%d)"),
      WeightBelowNodeMatchingUnformatted(collapsed_thread_root,
                                         "MergeSort (size=%d)"));
}

TEST(ProfilerTest, MemcpyFourThreads) {
  TreeView treeview;
  {
    ScopeProfile profile;
    profile.SetUserTreeView(&treeview);
    std::vector<std::unique_ptr<std::thread>> threads;
    for (int i = 0; i < 4; i++) {
      threads.emplace_back(new std::thread([i]() {
        ScopeLabel thread_label("worker thread #%d", i);
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
        ScopeLabel some_more_work_label("some more work");
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
      }));
    }
    for (int i = 0; i < 4; i++) {
      threads[i]->join();
    }
  }
  Print(treeview);
  // Since we cleared GlobalAllThreadStacks and the current thread hasn't
  // created any ScopeLabel, only the 4 worker threads should be recorded.
  EXPECT_EQ(treeview.thread_roots().size(), 4);
  for (const auto& thread_root : treeview.thread_roots()) {
    const TreeView::Node& root_node = *thread_root.second;
    // The root node may have 1 or 2 children depending on whether there is
    // an "[other]" child.
    EXPECT_GE(root_node.children.size(), 1);
    EXPECT_LE(root_node.children.size(), 2);
    const TreeView::Node& child_node = *root_node.children[0];
    EXPECT_EQ(child_node.label.format(), "worker thread #%d");
    // There must be 2 children, since roughly half the time will be in
    // "some more work" leaving the other half in "[other]".
    EXPECT_EQ(child_node.children.size(), 2);
    const TreeView::Node& child_child_node = *child_node.children[0];
    // Since we sample every millisecond and the threads run for >= 2000
    // milliseconds, the "thread func" label should get roughly 2000 samples.
    // Not very rigorous, as we're depending on the profiler thread getting
    // scheduled, so to avoid this test being flaky, we use a much more
    // conservative value of 500, one quarter of that normal value 2000.
    EXPECT_GE(child_node.weight, 500);
    // Likewise, allow up to four times more than the normal value 2000.
    EXPECT_LE(child_node.weight, 8000);
    // Roughly half of time should be spent under the "some more work" label.
    float some_more_work_percentage =
        100.f * child_child_node.weight / child_node.weight;
    EXPECT_GE(some_more_work_percentage, 40.0f);
    EXPECT_LE(some_more_work_percentage, 60.0f);
  }
}

TEST(ProfilerTest, OneThreadAfterAnother) {
  TreeView treeview;
  {
    ScopeProfile profile;
    profile.SetUserTreeView(&treeview);
    {
      std::thread thread([]() {
        ScopeLabel thread_label("thread 0");
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
      });
      thread.join();
    }
    {
      std::thread thread([]() {
        ScopeLabel thread_label("thread 1");
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
      });
      thread.join();
    }
  }
  Print(treeview);
  EXPECT_EQ(treeview.thread_roots().size(), 2);
}

#endif  // RUY_PROFILER

}  // namespace
}  // namespace profiler
}  // namespace ruy

int main(int argc, char** argv) {
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}