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::ElementsAre;
14 using testing::Pair;
15 using testing::SizeIs;
16 
17 namespace llvm {
18 namespace automemcpy {
19 namespace {
20 
21 TEST(AutomemcpyJsonResultsAnalyzer, getThroughputsOneSample) {
22   static constexpr FunctionId Foo1 = {"memcpy1", FunctionType::MEMCPY};
23   static constexpr DistributionId DistA = {{"A"}};
24   static constexpr SampleId Id = {Foo1, DistA};
25   static constexpr Sample kSamples[] = {
26       Sample{Id, 4},
27   };
28 
29   const std::vector<FunctionData> Data = getThroughputs(kSamples);
30   EXPECT_THAT(Data, SizeIs(1));
31   EXPECT_THAT(Data[0].Id, Foo1);
32   EXPECT_THAT(Data[0].PerDistributionData, SizeIs(1));
33   // A single value is provided.
34   EXPECT_THAT(
35       Data[0].PerDistributionData.lookup(DistA.Name).MedianBytesPerSecond, 4);
36 }
37 
38 TEST(AutomemcpyJsonResultsAnalyzer, getThroughputsManySamplesSameBucket) {
39   static constexpr FunctionId Foo1 = {"memcpy1", FunctionType::MEMCPY};
40   static constexpr DistributionId DistA = {{"A"}};
41   static constexpr SampleId Id = {Foo1, DistA};
42   static constexpr Sample kSamples[] = {Sample{Id, 4}, Sample{Id, 5},
43                                         Sample{Id, 5}};
44 
45   const std::vector<FunctionData> Data = getThroughputs(kSamples);
46   EXPECT_THAT(Data, SizeIs(1));
47   EXPECT_THAT(Data[0].Id, Foo1);
48   EXPECT_THAT(Data[0].PerDistributionData, SizeIs(1));
49   // When multiple values are provided we pick the median one (here median of 4,
50   // 5, 5).
51   EXPECT_THAT(
52       Data[0].PerDistributionData.lookup(DistA.Name).MedianBytesPerSecond, 5);
53 }
54 
55 TEST(AutomemcpyJsonResultsAnalyzer, getThroughputsServeralFunctionAndDist) {
56   static constexpr FunctionId Foo1 = {"memcpy1", FunctionType::MEMCPY};
57   static constexpr DistributionId DistA = {{"A"}};
58   static constexpr FunctionId Foo2 = {"memcpy2", FunctionType::MEMCPY};
59   static constexpr DistributionId DistB = {{"B"}};
60   static constexpr Sample kSamples[] = {
61       Sample{{Foo1, DistA}, 1}, Sample{{Foo1, DistB}, 2},
62       Sample{{Foo2, DistA}, 3}, Sample{{Foo2, DistB}, 4}};
63   // Data is aggregated per function.
64   const std::vector<FunctionData> Data = getThroughputs(kSamples);
65   EXPECT_THAT(Data, SizeIs(2)); // 2 functions Foo1 and Foo2.
66   // Each function has data for both distributions DistA and DistB.
67   EXPECT_THAT(Data[0].PerDistributionData, SizeIs(2));
68   EXPECT_THAT(Data[1].PerDistributionData, SizeIs(2));
69 }
70 
71 TEST(AutomemcpyJsonResultsAnalyzer, getScore) {
72   static constexpr FunctionId Foo1 = {"memcpy1", FunctionType::MEMCPY};
73   static constexpr FunctionId Foo2 = {"memcpy2", FunctionType::MEMCPY};
74   static constexpr FunctionId Foo3 = {"memcpy3", FunctionType::MEMCPY};
75   static constexpr DistributionId Dist = {{"A"}};
76   static constexpr Sample kSamples[] = {Sample{{Foo1, Dist}, 1},
77                                         Sample{{Foo2, Dist}, 2},
78                                         Sample{{Foo3, Dist}, 3}};
79 
80   // Data is aggregated per function.
81   std::vector<FunctionData> Data = getThroughputs(kSamples);
82 
83   // Sort Data by function name so we can test them.
84   std::sort(
85       Data.begin(), Data.end(),
86       [](const FunctionData &A, const FunctionData &B) { return A.Id < B.Id; });
87 
88   EXPECT_THAT(Data[0].Id, Foo1);
89   EXPECT_THAT(Data[0].PerDistributionData.lookup("A").MedianBytesPerSecond, 1);
90   EXPECT_THAT(Data[1].Id, Foo2);
91   EXPECT_THAT(Data[1].PerDistributionData.lookup("A").MedianBytesPerSecond, 2);
92   EXPECT_THAT(Data[2].Id, Foo3);
93   EXPECT_THAT(Data[2].PerDistributionData.lookup("A").MedianBytesPerSecond, 3);
94 
95   // Normalizes throughput per distribution.
96   fillScores(Data);
97   EXPECT_THAT(Data[0].PerDistributionData.lookup("A").Score, 0);
98   EXPECT_THAT(Data[1].PerDistributionData.lookup("A").Score, 0.5);
99   EXPECT_THAT(Data[2].PerDistributionData.lookup("A").Score, 1);
100 }
101 
102 TEST(AutomemcpyJsonResultsAnalyzer, castVotes) {
103   static constexpr double kAbsErr = 0.01;
104 
105   static constexpr FunctionId Foo1 = {"memcpy1", FunctionType::MEMCPY};
106   static constexpr FunctionId Foo2 = {"memcpy2", FunctionType::MEMCPY};
107   static constexpr FunctionId Foo3 = {"memcpy3", FunctionType::MEMCPY};
108   static constexpr DistributionId DistA = {{"A"}};
109   static constexpr DistributionId DistB = {{"B"}};
110   static constexpr Sample kSamples[] = {
111       Sample{{Foo1, DistA}, 0}, Sample{{Foo1, DistB}, 30},
112       Sample{{Foo2, DistA}, 1}, Sample{{Foo2, DistB}, 100},
113       Sample{{Foo3, DistA}, 7}, Sample{{Foo3, DistB}, 100},
114   };
115 
116   // DistA Thoughput ranges from 0 to 7.
117   // DistB Thoughput ranges from 30 to 100.
118 
119   // Data is aggregated per function.
120   std::vector<FunctionData> Data = getThroughputs(kSamples);
121 
122   // Sort Data by function name so we can test them.
123   std::sort(
124       Data.begin(), Data.end(),
125       [](const FunctionData &A, const FunctionData &B) { return A.Id < B.Id; });
126 
127   // Normalizes throughput per distribution.
128   fillScores(Data);
129 
130   // Cast votes
131   castVotes(Data);
132 
133   EXPECT_THAT(Data[0].Id, Foo1);
134   EXPECT_THAT(Data[1].Id, Foo2);
135   EXPECT_THAT(Data[2].Id, Foo3);
136 
137   // Distribution A
138   // Throughput is 0, 1 and 7, so normalized scores are 0, 1/7 and 1.
139   EXPECT_NEAR(Data[0].PerDistributionData.lookup("A").Score, 0, kAbsErr);
140   EXPECT_NEAR(Data[1].PerDistributionData.lookup("A").Score, 1. / 7, kAbsErr);
141   EXPECT_NEAR(Data[2].PerDistributionData.lookup("A").Score, 1, kAbsErr);
142   // which are turned into grades BAD,  MEDIOCRE and EXCELLENT.
143   EXPECT_THAT(Data[0].PerDistributionData.lookup("A").Grade, Grade::BAD);
144   EXPECT_THAT(Data[1].PerDistributionData.lookup("A").Grade, Grade::MEDIOCRE);
145   EXPECT_THAT(Data[2].PerDistributionData.lookup("A").Grade, Grade::EXCELLENT);
146 
147   // Distribution B
148   // Throughput is 30, 100 and 100, so normalized scores are 0, 1 and 1.
149   EXPECT_NEAR(Data[0].PerDistributionData.lookup("B").Score, 0, kAbsErr);
150   EXPECT_NEAR(Data[1].PerDistributionData.lookup("B").Score, 1, kAbsErr);
151   EXPECT_NEAR(Data[2].PerDistributionData.lookup("B").Score, 1, kAbsErr);
152   // which are turned into grades BAD, EXCELLENT and EXCELLENT.
153   EXPECT_THAT(Data[0].PerDistributionData.lookup("B").Grade, Grade::BAD);
154   EXPECT_THAT(Data[1].PerDistributionData.lookup("B").Grade, Grade::EXCELLENT);
155   EXPECT_THAT(Data[2].PerDistributionData.lookup("B").Grade, Grade::EXCELLENT);
156 
157   // Now looking from the functions point of view.
158   // Note the array is indexed by GradeEnum values (EXCELLENT=0 / BAD = 6)
159   EXPECT_THAT(Data[0].GradeHisto, ElementsAre(0, 0, 0, 0, 0, 0, 2));
160   EXPECT_THAT(Data[1].GradeHisto, ElementsAre(1, 0, 0, 0, 0, 1, 0));
161   EXPECT_THAT(Data[2].GradeHisto, ElementsAre(2, 0, 0, 0, 0, 0, 0));
162 
163   EXPECT_THAT(Data[0].FinalGrade, Grade::BAD);
164   EXPECT_THAT(Data[1].FinalGrade, Grade::MEDIOCRE);
165   EXPECT_THAT(Data[2].FinalGrade, Grade::EXCELLENT);
166 }
167 
168 } // namespace
169 } // namespace automemcpy
170 } // namespace llvm
171