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