• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #include <benchmark/benchmark.h>
17 
18 #include <map>
19 #include <unordered_map>
20 #include <unordered_set>
21 #include <vector>
22 
23 #include "utils.h"
24 
25 namespace android {
26 namespace os {
27 namespace statsd {
28 
29 namespace {
30 
31 const int kMaxAtomId = 100000;
32 const int kMaxErrorCode = 20;
33 
generateIdsAndErrorsVectors(const std::vector<int> & idsCounts,const std::vector<int> & errorsCounts)34 std::vector<std::pair<std::vector<int>, std::vector<int>>> generateIdsAndErrorsVectors(
35         const std::vector<int>& idsCounts, const std::vector<int>& errorsCounts) {
36     std::vector<std::pair<std::vector<int>, std::vector<int>>> result;
37     for (const int idCount : idsCounts) {
38         for (const int errorCount : errorsCounts) {
39             auto ids = generateRandomIds(idCount, kMaxAtomId);
40             auto errors = generateRandomIds(errorCount, kMaxErrorCode);
41             result.push_back(std::make_pair(ids, errors));
42         }
43     }
44     return result;
45 }
46 
47 const std::vector<std::pair<std::vector<int>, std::vector<int>>> kRandomIdsAndErrorsPairs =
48         generateIdsAndErrorsVectors({1, 5, 10, 50}, {1, 2, 5, 10});
49 
50 struct TestVector {
51     std::vector<int> errors;
52     std::vector<int> tags;
53 };
54 
generateTestVector(int count,const std::vector<std::pair<std::vector<int>,std::vector<int>>> & idsAndErrorsPairs)55 std::vector<TestVector> generateTestVector(
56         int count,
57         const std::vector<std::pair<std::vector<int>, std::vector<int>>>& idsAndErrorsPairs) {
58     std::srand(std::time(nullptr));
59 
60     std::vector<TestVector> result;
61 
62     for (const auto& idsAndErrors : idsAndErrorsPairs) {
63         TestVector testVector;
64 
65         testVector.errors.reserve(count);
66         testVector.tags.reserve(count);
67 
68         for (int i = 0; i < count; i++) {
69             const int randomAtomIdFromReferenceList =
70                     idsAndErrors.first[std::rand() % idsAndErrors.first.size()];
71             const int randomErrorFromReferenceList =
72                     idsAndErrors.second[std::rand() % idsAndErrors.second.size()];
73 
74             testVector.errors.push_back(randomErrorFromReferenceList);
75             testVector.tags.push_back(randomAtomIdFromReferenceList);
76         }
77         result.push_back(testVector);
78     }
79 
80     return result;
81 }
82 
83 constexpr int kTestVectorSize = 4096;
84 constexpr int kMaxAtomTagsCount = 100;
85 
86 const std::vector<TestVector> kTestVectors =
87         generateTestVector(kTestVectorSize, kRandomIdsAndErrorsPairs);
88 
89 struct LossInfoVector {
90     // using vectors is more memory efficient
91     // using vectors fits well with the dump API implementation - no need to transform data
92     // before writing into AStatsEvent since it is aligned with repeated int32 fields
93     std::vector<int> errors;
94     std::vector<int> tags;
95     std::vector<int> counts;
96 
noteLossInfoandroid::os::statsd::__anona7c452720111::LossInfoVector97     bool noteLossInfo(int error, int tag) {
98         // linear search is Ok here since we do not expect to see many tags, usually 1-5 per uid
99         // exception is system server where we see 10s atoms
100         size_t locatedTagIndex = 0;
101         for (locatedTagIndex = 0; locatedTagIndex < errors.size(); ++locatedTagIndex) {
102             // is there already logged an atom with tag == atomId
103             if (errors[locatedTagIndex] == error && tags[locatedTagIndex] == tag) {
104                 ++counts[locatedTagIndex];
105                 return true;
106             }
107         }
108 
109         // if pair [error, atomId] is not found and guardrail is not reached yet store loss
110         // counter
111         if (locatedTagIndex == errors.size() && tags.size() < kMaxAtomTagsCount) {
112             errors.push_back(error);
113             tags.push_back(tag);
114             counts.push_back(1);
115         } else {
116             return false;
117         }
118         return true;
119     }
120 };
121 
122 using LossInfoKey = std::pair<int, int>;  // [error, tag]
123 
124 template <typename T>
125 struct LossInfoMap {
126     // using maps is more CPU efficient however will require some postprocessing before
127     // writing into the socket
128     T countsPerErrorTag;
129 
noteLossInfoandroid::os::statsd::__anona7c452720111::LossInfoMap130     bool noteLossInfo(int error, int tag) {
131         LossInfoKey key = std::make_pair(error, tag);
132         auto counterIt = countsPerErrorTag.find(key);
133 
134         if (counterIt != countsPerErrorTag.end()) {
135             ++counterIt->second;
136         } else if (countsPerErrorTag.size() < kMaxAtomTagsCount) {
137             countsPerErrorTag[key] = 1;
138         } else {
139             return false;
140         }
141 
142         return true;
143     }
144 };
145 
146 }  // namespace
147 
148 struct hash_pair final {
149     template <class TFirst, class TSecond>
operator ()android::os::statsd::hash_pair150     size_t operator()(const std::pair<TFirst, TSecond>& p) const noexcept {
151         uintmax_t hash = std::hash<TFirst>{}(p.first);
152         hash <<= sizeof(uintmax_t) * 4;
153         hash ^= std::hash<TSecond>{}(p.second);
154         return std::hash<uintmax_t>{}(hash);
155     }
156 };
157 
BM_LossInfoCollectionAndDumpViaVector(benchmark::State & state)158 static void BM_LossInfoCollectionAndDumpViaVector(benchmark::State& state) {
159     const TestVector& testVector = kTestVectors[state.range(0)];
160     LossInfoVector lossInfo;
161 
162     while (state.KeepRunning()) {
163         int res = 0;
164         for (int i = 0; i < kTestVectorSize; i++) {
165             res += lossInfo.noteLossInfo(testVector.errors[i], testVector.tags[i]);
166         }
167         benchmark::DoNotOptimize(res);
168     }
169 }
170 BENCHMARK(BM_LossInfoCollectionAndDumpViaVector)
171         ->Args({0})
172         ->Args({1})
173         ->Args({2})
174         ->Args({3})
175         ->Args({4})
176         ->Args({5})
177         ->Args({6})
178         ->Args({7})
179         ->Args({8})
180         ->Args({9})
181         ->Args({10})
182         ->Args({11})
183         ->Args({12})
184         ->Args({13})
185         ->Args({14})
186         ->Args({15});
187 
BM_LossInfoCollectionAndDumpViaUnorderedMap(benchmark::State & state)188 static void BM_LossInfoCollectionAndDumpViaUnorderedMap(benchmark::State& state) {
189     const TestVector& testVector = kTestVectors[state.range(0)];
190     LossInfoMap<std::unordered_map<LossInfoKey, int, hash_pair>> lossInfo;
191 
192     while (state.KeepRunning()) {
193         int res = 0;
194         for (int i = 0; i < kTestVectorSize; i++) {
195             res += lossInfo.noteLossInfo(testVector.errors[i], testVector.tags[i]);
196         }
197         benchmark::DoNotOptimize(res);
198     }
199 }
200 BENCHMARK(BM_LossInfoCollectionAndDumpViaUnorderedMap)
201         ->Args({0})
202         ->Args({1})
203         ->Args({2})
204         ->Args({3})
205         ->Args({4})
206         ->Args({5})
207         ->Args({6})
208         ->Args({7})
209         ->Args({8})
210         ->Args({9})
211         ->Args({10})
212         ->Args({11})
213         ->Args({12})
214         ->Args({13})
215         ->Args({14})
216         ->Args({15});
217 
218 }  //  namespace statsd
219 }  //  namespace os
220 }  //  namespace android
221