• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //===--  Automemcpy Json Results Analyzer Test ----------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "automemcpy/ResultAnalyzer.h"
10 #include "gmock/gmock.h"
11 #include "gtest/gtest.h"
12 
13 using testing::DoubleNear;
14 using testing::ElementsAre;
15 using testing::Pair;
16 using testing::SizeIs;
17 
18 namespace llvm {
19 namespace automemcpy {
20 namespace {
21 
TEST(AutomemcpyJsonResultsAnalyzer,getThroughputsOneSample)22 TEST(AutomemcpyJsonResultsAnalyzer, getThroughputsOneSample) {
23   static constexpr FunctionId Foo1 = {"memcpy1", FunctionType::MEMCPY};
24   static constexpr DistributionId DistA = {{"A"}};
25   static constexpr SampleId Id = {Foo1, DistA};
26   static constexpr Sample kSamples[] = {
27       Sample{Id, SampleType::ITERATION, 4},
28       Sample{Id, SampleType::AGGREGATE, -1}, // Aggegates gets discarded
29   };
30 
31   const std::vector<FunctionData> Data = getThroughputs(kSamples);
32   EXPECT_THAT(Data, SizeIs(1));
33   EXPECT_THAT(Data[0].Id, Foo1);
34   EXPECT_THAT(Data[0].PerDistributionData, SizeIs(1));
35   // A single value is provided.
36   const auto &DistributionData = Data[0].PerDistributionData.lookup(DistA.Name);
37   EXPECT_THAT(DistributionData.BytesPerSecondMedian, 4);
38   EXPECT_THAT(DistributionData.BytesPerSecondMean, 4);
39   EXPECT_THAT(DistributionData.BytesPerSecondVariance, 0);
40 }
41 
TEST(AutomemcpyJsonResultsAnalyzer,getThroughputsManySamplesSameBucket)42 TEST(AutomemcpyJsonResultsAnalyzer, getThroughputsManySamplesSameBucket) {
43   static constexpr FunctionId Foo1 = {"memcpy1", FunctionType::MEMCPY};
44   static constexpr DistributionId DistA = {{"A"}};
45   static constexpr SampleId Id = {Foo1, DistA};
46   static constexpr Sample kSamples[] = {Sample{Id, SampleType::ITERATION, 4},
47                                         Sample{Id, SampleType::ITERATION, 5},
48                                         Sample{Id, SampleType::ITERATION, 5}};
49 
50   const std::vector<FunctionData> Data = getThroughputs(kSamples);
51   EXPECT_THAT(Data, SizeIs(1));
52   EXPECT_THAT(Data[0].Id, Foo1);
53   EXPECT_THAT(Data[0].PerDistributionData, SizeIs(1));
54   // When multiple values are provided we pick the median one (here median of 4,
55   // 5, 5).
56   const auto &DistributionData = Data[0].PerDistributionData.lookup(DistA.Name);
57   EXPECT_THAT(DistributionData.BytesPerSecondMedian, 5);
58   EXPECT_THAT(DistributionData.BytesPerSecondMean, DoubleNear(4.6, 0.1));
59   EXPECT_THAT(DistributionData.BytesPerSecondVariance, DoubleNear(0.33, 0.01));
60 }
61 
TEST(AutomemcpyJsonResultsAnalyzer,getThroughputsServeralFunctionAndDist)62 TEST(AutomemcpyJsonResultsAnalyzer, getThroughputsServeralFunctionAndDist) {
63   static constexpr FunctionId Foo1 = {"memcpy1", FunctionType::MEMCPY};
64   static constexpr DistributionId DistA = {{"A"}};
65   static constexpr FunctionId Foo2 = {"memcpy2", FunctionType::MEMCPY};
66   static constexpr DistributionId DistB = {{"B"}};
67   static constexpr Sample kSamples[] = {
68       Sample{{Foo1, DistA}, SampleType::ITERATION, 1},
69       Sample{{Foo1, DistB}, SampleType::ITERATION, 2},
70       Sample{{Foo2, DistA}, SampleType::ITERATION, 3},
71       Sample{{Foo2, DistB}, SampleType::ITERATION, 4}};
72   // Data is aggregated per function.
73   const std::vector<FunctionData> Data = getThroughputs(kSamples);
74   EXPECT_THAT(Data, SizeIs(2)); // 2 functions Foo1 and Foo2.
75   // Each function has data for both distributions DistA and DistB.
76   EXPECT_THAT(Data[0].PerDistributionData, SizeIs(2));
77   EXPECT_THAT(Data[1].PerDistributionData, SizeIs(2));
78 }
79 
TEST(AutomemcpyJsonResultsAnalyzer,getScore)80 TEST(AutomemcpyJsonResultsAnalyzer, getScore) {
81   static constexpr FunctionId Foo1 = {"memcpy1", FunctionType::MEMCPY};
82   static constexpr FunctionId Foo2 = {"memcpy2", FunctionType::MEMCPY};
83   static constexpr FunctionId Foo3 = {"memcpy3", FunctionType::MEMCPY};
84   static constexpr DistributionId Dist = {{"A"}};
85   static constexpr Sample kSamples[] = {
86       Sample{{Foo1, Dist}, SampleType::ITERATION, 1},
87       Sample{{Foo2, Dist}, SampleType::ITERATION, 2},
88       Sample{{Foo3, Dist}, SampleType::ITERATION, 3}};
89 
90   // Data is aggregated per function.
91   std::vector<FunctionData> Data = getThroughputs(kSamples);
92 
93   // Sort Data by function name so we can test them.
94   std::sort(
95       Data.begin(), Data.end(),
96       [](const FunctionData &A, const FunctionData &B) { return A.Id < B.Id; });
97 
98   EXPECT_THAT(Data[0].Id, Foo1);
99   EXPECT_THAT(Data[0].PerDistributionData.lookup("A").BytesPerSecondMedian, 1);
100   EXPECT_THAT(Data[1].Id, Foo2);
101   EXPECT_THAT(Data[1].PerDistributionData.lookup("A").BytesPerSecondMedian, 2);
102   EXPECT_THAT(Data[2].Id, Foo3);
103   EXPECT_THAT(Data[2].PerDistributionData.lookup("A").BytesPerSecondMedian, 3);
104 
105   // Normalizes throughput per distribution.
106   fillScores(Data);
107   EXPECT_THAT(Data[0].PerDistributionData.lookup("A").Score, 0);
108   EXPECT_THAT(Data[1].PerDistributionData.lookup("A").Score, 0.5);
109   EXPECT_THAT(Data[2].PerDistributionData.lookup("A").Score, 1);
110 }
111 
TEST(AutomemcpyJsonResultsAnalyzer,castVotes)112 TEST(AutomemcpyJsonResultsAnalyzer, castVotes) {
113   static constexpr double kAbsErr = 0.01;
114 
115   static constexpr FunctionId Foo1 = {"memcpy1", FunctionType::MEMCPY};
116   static constexpr FunctionId Foo2 = {"memcpy2", FunctionType::MEMCPY};
117   static constexpr FunctionId Foo3 = {"memcpy3", FunctionType::MEMCPY};
118   static constexpr DistributionId DistA = {{"A"}};
119   static constexpr DistributionId DistB = {{"B"}};
120   static constexpr Sample kSamples[] = {
121       Sample{{Foo1, DistA}, SampleType::ITERATION, 0},
122       Sample{{Foo1, DistB}, SampleType::ITERATION, 30},
123       Sample{{Foo2, DistA}, SampleType::ITERATION, 1},
124       Sample{{Foo2, DistB}, SampleType::ITERATION, 100},
125       Sample{{Foo3, DistA}, SampleType::ITERATION, 7},
126       Sample{{Foo3, DistB}, SampleType::ITERATION, 100},
127   };
128 
129   // DistA Thoughput ranges from 0 to 7.
130   // DistB Thoughput ranges from 30 to 100.
131 
132   // Data is aggregated per function.
133   std::vector<FunctionData> Data = getThroughputs(kSamples);
134 
135   // Sort Data by function name so we can test them.
136   std::sort(
137       Data.begin(), Data.end(),
138       [](const FunctionData &A, const FunctionData &B) { return A.Id < B.Id; });
139 
140   // Normalizes throughput per distribution.
141   fillScores(Data);
142 
143   // Cast votes
144   castVotes(Data);
145 
146   EXPECT_THAT(Data[0].Id, Foo1);
147   EXPECT_THAT(Data[1].Id, Foo2);
148   EXPECT_THAT(Data[2].Id, Foo3);
149 
150   const auto GetDistData = [&Data](size_t Index, StringRef Name) {
151     return Data[Index].PerDistributionData.lookup(Name);
152   };
153 
154   // Distribution A
155   // Throughput is 0, 1 and 7, so normalized scores are 0, 1/7 and 1.
156   EXPECT_THAT(GetDistData(0, "A").Score, DoubleNear(0, kAbsErr));
157   EXPECT_THAT(GetDistData(1, "A").Score, DoubleNear(1. / 7, kAbsErr));
158   EXPECT_THAT(GetDistData(2, "A").Score, DoubleNear(1, kAbsErr));
159   // which are turned into grades BAD,  MEDIOCRE and EXCELLENT.
160   EXPECT_THAT(GetDistData(0, "A").Grade, Grade::BAD);
161   EXPECT_THAT(GetDistData(1, "A").Grade, Grade::MEDIOCRE);
162   EXPECT_THAT(GetDistData(2, "A").Grade, Grade::EXCELLENT);
163 
164   // Distribution B
165   // Throughput is 30, 100 and 100, so normalized scores are 0, 1 and 1.
166   EXPECT_THAT(GetDistData(0, "B").Score, DoubleNear(0, kAbsErr));
167   EXPECT_THAT(GetDistData(1, "B").Score, DoubleNear(1, kAbsErr));
168   EXPECT_THAT(GetDistData(2, "B").Score, DoubleNear(1, kAbsErr));
169   // which are turned into grades BAD, EXCELLENT and EXCELLENT.
170   EXPECT_THAT(GetDistData(0, "B").Grade, Grade::BAD);
171   EXPECT_THAT(GetDistData(1, "B").Grade, Grade::EXCELLENT);
172   EXPECT_THAT(GetDistData(2, "B").Grade, Grade::EXCELLENT);
173 
174   // Now looking from the functions point of view.
175   EXPECT_THAT(Data[0].ScoresGeoMean, DoubleNear(0, kAbsErr));
176   EXPECT_THAT(Data[1].ScoresGeoMean, DoubleNear(1. * (1. / 7), kAbsErr));
177   EXPECT_THAT(Data[2].ScoresGeoMean, DoubleNear(1, kAbsErr));
178 
179   // Note the array is indexed by GradeEnum values (EXCELLENT=0 / BAD = 6)
180   EXPECT_THAT(Data[0].GradeHisto, ElementsAre(0, 0, 0, 0, 0, 0, 2));
181   EXPECT_THAT(Data[1].GradeHisto, ElementsAre(1, 0, 0, 0, 0, 1, 0));
182   EXPECT_THAT(Data[2].GradeHisto, ElementsAre(2, 0, 0, 0, 0, 0, 0));
183 
184   EXPECT_THAT(Data[0].FinalGrade, Grade::BAD);
185   EXPECT_THAT(Data[1].FinalGrade, Grade::MEDIOCRE);
186   EXPECT_THAT(Data[2].FinalGrade, Grade::EXCELLENT);
187 }
188 
189 } // namespace
190 } // namespace automemcpy
191 } // namespace llvm
192