Adam Rice | 3807a76 | 2023-08-18 10:25:25 | [diff] [blame] | 1 | // Copyright 2023 The Chromium Authors |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #include "base/big_endian.h" |
| 6 | |
| 7 | #include <stdint.h> |
| 8 | |
| 9 | #include "base/check.h" |
| 10 | #include "base/containers/span.h" |
danakj | a1e7d0a1 | 2024-02-29 15:22:17 | [diff] [blame] | 11 | #include "base/numerics/byte_conversions.h" |
Egor Pasko | 1403ac9 | 2024-02-16 17:07:51 | [diff] [blame] | 12 | #include "testing/gtest/include/gtest/gtest.h" |
Adam Rice | 3807a76 | 2023-08-18 10:25:25 | [diff] [blame] | 13 | #include "third_party/google_benchmark/src/include/benchmark/benchmark.h" |
| 14 | |
| 15 | namespace base { |
| 16 | namespace { |
| 17 | |
| 18 | constexpr size_t kSize = 128 * 1024 * 1024; |
| 19 | int64_t aligned_bytes[kSize / sizeof(int64_t)]; |
| 20 | struct { |
| 21 | int64_t aligment; |
| 22 | char padding_to_cause_misalignment; |
| 23 | char bytes[kSize]; |
| 24 | } misaligned_bytes; |
| 25 | |
danakj | 6b407a96 | 2024-02-16 19:08:09 | [diff] [blame] | 26 | void DoNotOptimizeSpan(span<const uint8_t> range) { |
Adam Rice | 3807a76 | 2023-08-18 10:25:25 | [diff] [blame] | 27 | // ::benchmark::DoNotOptimize() generates quite large code, so instead of |
| 28 | // calling it for every byte in the range, calculate `sum` which depends on |
| 29 | // every byte in the range and then call DoNotOptimise() on that. |
| 30 | int sum = 0; |
| 31 | for (char c : range) { |
| 32 | sum += c; |
| 33 | } |
| 34 | ::benchmark::DoNotOptimize(sum); |
| 35 | } |
| 36 | |
| 37 | template <typename T> |
danakj | 6b407a96 | 2024-02-16 19:08:09 | [diff] [blame] | 38 | inline void WriteBigEndianCommon(::benchmark::State& state, |
| 39 | span<uint8_t, kSize> buffer) { |
| 40 | size_t offset = 0u; |
| 41 | auto value = T{0}; |
Adam Rice | 3807a76 | 2023-08-18 10:25:25 | [diff] [blame] | 42 | for (auto _ : state) { |
danakj | a1e7d0a1 | 2024-02-29 15:22:17 | [diff] [blame] | 43 | if constexpr (sizeof(T) == 1) { |
Peter Kasting | 448444f | 2024-12-13 01:25:36 | [diff] [blame] | 44 | buffer.subspan(offset).copy_prefix_from(U8ToBigEndian(value)); |
danakj | a1e7d0a1 | 2024-02-29 15:22:17 | [diff] [blame] | 45 | } else if constexpr (sizeof(T) == 2) { |
Peter Kasting | 448444f | 2024-12-13 01:25:36 | [diff] [blame] | 46 | buffer.subspan(offset).copy_prefix_from(U16ToBigEndian(value)); |
danakj | a1e7d0a1 | 2024-02-29 15:22:17 | [diff] [blame] | 47 | } else if constexpr (sizeof(T) == 4) { |
Peter Kasting | 448444f | 2024-12-13 01:25:36 | [diff] [blame] | 48 | buffer.subspan(offset).copy_prefix_from(U32ToBigEndian(value)); |
danakj | a1e7d0a1 | 2024-02-29 15:22:17 | [diff] [blame] | 49 | } else { |
| 50 | static_assert(sizeof(T) == 8); |
Peter Kasting | 448444f | 2024-12-13 01:25:36 | [diff] [blame] | 51 | buffer.subspan(offset).copy_prefix_from(U64ToBigEndian(value)); |
danakj | a1e7d0a1 | 2024-02-29 15:22:17 | [diff] [blame] | 52 | } |
Adam Rice | 3807a76 | 2023-08-18 10:25:25 | [diff] [blame] | 53 | offset += sizeof(T); |
danakj | 6b407a96 | 2024-02-16 19:08:09 | [diff] [blame] | 54 | static_assert(kSize % sizeof(T) == 0u); |
Adam Rice | 3807a76 | 2023-08-18 10:25:25 | [diff] [blame] | 55 | if (offset == kSize) { |
| 56 | offset = 0; |
| 57 | } |
| 58 | ++value; |
| 59 | } |
danakj | 6b407a96 | 2024-02-16 19:08:09 | [diff] [blame] | 60 | DoNotOptimizeSpan(buffer); |
Adam Rice | 3807a76 | 2023-08-18 10:25:25 | [diff] [blame] | 61 | } |
| 62 | |
| 63 | template <typename T> |
| 64 | void BM_WriteBigEndianAligned(::benchmark::State& state) { |
David Benjamin | fad2e08 | 2024-04-02 19:14:39 | [diff] [blame] | 65 | span<uint8_t, kSize> buffer = as_writable_byte_span(aligned_bytes); |
danakj | 6b407a96 | 2024-02-16 19:08:09 | [diff] [blame] | 66 | CHECK(reinterpret_cast<uintptr_t>(buffer.data()) % alignof(T) == 0u); |
| 67 | WriteBigEndianCommon<T>(state, buffer); |
Adam Rice | 3807a76 | 2023-08-18 10:25:25 | [diff] [blame] | 68 | } |
| 69 | |
| 70 | template <typename T> |
| 71 | void BM_WriteBigEndianMisaligned(::benchmark::State& state) { |
David Benjamin | fad2e08 | 2024-04-02 19:14:39 | [diff] [blame] | 72 | span<uint8_t, kSize> buffer = as_writable_byte_span(misaligned_bytes.bytes); |
danakj | 6b407a96 | 2024-02-16 19:08:09 | [diff] [blame] | 73 | CHECK(reinterpret_cast<uintptr_t>(buffer.data()) % alignof(T) != 0u); |
| 74 | WriteBigEndianCommon<T>(state, buffer); |
Adam Rice | 3807a76 | 2023-08-18 10:25:25 | [diff] [blame] | 75 | } |
| 76 | |
| 77 | template <typename T> |
| 78 | inline void ReadBigEndianCommon(::benchmark::State& state, |
danakj | a1e7d0a1 | 2024-02-29 15:22:17 | [diff] [blame] | 79 | span<const uint8_t, kSize> buffer) { |
Adam Rice | 3807a76 | 2023-08-18 10:25:25 | [diff] [blame] | 80 | size_t offset = 0; |
| 81 | for (auto _ : state) { |
| 82 | T value; |
danakj | a1e7d0a1 | 2024-02-29 15:22:17 | [diff] [blame] | 83 | if constexpr (sizeof(T) == 1) { |
David Benjamin | fad2e08 | 2024-04-02 19:14:39 | [diff] [blame] | 84 | value = U8FromBigEndian(buffer.subspan(offset).first<sizeof(T)>()); |
danakj | a1e7d0a1 | 2024-02-29 15:22:17 | [diff] [blame] | 85 | } else if constexpr (sizeof(T) == 2) { |
David Benjamin | fad2e08 | 2024-04-02 19:14:39 | [diff] [blame] | 86 | value = U16FromBigEndian(buffer.subspan(offset).first<sizeof(T)>()); |
danakj | a1e7d0a1 | 2024-02-29 15:22:17 | [diff] [blame] | 87 | } else if constexpr (sizeof(T) == 4) { |
David Benjamin | fad2e08 | 2024-04-02 19:14:39 | [diff] [blame] | 88 | value = U32FromBigEndian(buffer.subspan(offset).first<sizeof(T)>()); |
danakj | a1e7d0a1 | 2024-02-29 15:22:17 | [diff] [blame] | 89 | } else { |
| 90 | static_assert(sizeof(T) == 8); |
David Benjamin | fad2e08 | 2024-04-02 19:14:39 | [diff] [blame] | 91 | value = U64FromBigEndian(buffer.subspan(offset).first<sizeof(T)>()); |
danakj | a1e7d0a1 | 2024-02-29 15:22:17 | [diff] [blame] | 92 | } |
Adam Rice | 3807a76 | 2023-08-18 10:25:25 | [diff] [blame] | 93 | ::benchmark::DoNotOptimize(value); |
| 94 | offset += sizeof(T); |
| 95 | static_assert(kSize % sizeof(T) == 0); |
| 96 | if (offset == kSize) { |
| 97 | offset = 0; |
| 98 | } |
| 99 | } |
| 100 | } |
| 101 | |
| 102 | template <typename T> |
| 103 | void BM_ReadBigEndianAligned(::benchmark::State& state) { |
David Benjamin | fad2e08 | 2024-04-02 19:14:39 | [diff] [blame] | 104 | span<const uint8_t, kSize> buffer = as_byte_span(aligned_bytes); |
danakj | a1e7d0a1 | 2024-02-29 15:22:17 | [diff] [blame] | 105 | CHECK(reinterpret_cast<uintptr_t>(buffer.data()) % alignof(T) == 0); |
| 106 | ReadBigEndianCommon<T>(state, buffer); |
Adam Rice | 3807a76 | 2023-08-18 10:25:25 | [diff] [blame] | 107 | } |
| 108 | |
| 109 | template <typename T> |
| 110 | void BM_ReadBigEndianMisaligned(::benchmark::State& state) { |
David Benjamin | fad2e08 | 2024-04-02 19:14:39 | [diff] [blame] | 111 | span<const uint8_t, kSize> buffer = as_byte_span(misaligned_bytes.bytes); |
danakj | a1e7d0a1 | 2024-02-29 15:22:17 | [diff] [blame] | 112 | CHECK(reinterpret_cast<uintptr_t>(buffer.data()) % alignof(T) != 0); |
| 113 | ReadBigEndianCommon<T>(state, buffer); |
Adam Rice | 3807a76 | 2023-08-18 10:25:25 | [diff] [blame] | 114 | } |
| 115 | |
| 116 | #define BENCHMARK_FOR_INT_TYPES(function) \ |
| 117 | BENCHMARK(function<int16_t>)->MinWarmUpTime(1.0); \ |
| 118 | BENCHMARK(function<uint16_t>)->MinWarmUpTime(1.0); \ |
| 119 | BENCHMARK(function<int32_t>)->MinWarmUpTime(1.0); \ |
| 120 | BENCHMARK(function<uint32_t>)->MinWarmUpTime(1.0); \ |
| 121 | BENCHMARK(function<int64_t>)->MinWarmUpTime(1.0); \ |
Egor Pasko | 1403ac9 | 2024-02-16 17:07:51 | [diff] [blame] | 122 | BENCHMARK(function<uint64_t>)->MinWarmUpTime(1.0); |
Adam Rice | 3807a76 | 2023-08-18 10:25:25 | [diff] [blame] | 123 | |
Egor Pasko | 1403ac9 | 2024-02-16 17:07:51 | [diff] [blame] | 124 | // Register the benchmarks as a GTest test. This allows using legacy |
| 125 | // --gtest_filter and --gtest_list_tests. |
| 126 | // TODO(https://crbug.com/40251982): Clean this up after transitioning to |
| 127 | // --benchmark_filter and --benchmark_list_tests. |
| 128 | TEST(BigEndianPerfTest, All) { |
| 129 | BENCHMARK_FOR_INT_TYPES(BM_WriteBigEndianAligned); |
| 130 | BENCHMARK_FOR_INT_TYPES(BM_WriteBigEndianMisaligned); |
| 131 | BENCHMARK_FOR_INT_TYPES(BM_ReadBigEndianAligned); |
| 132 | BENCHMARK_FOR_INT_TYPES(BM_ReadBigEndianMisaligned); |
| 133 | } |
Adam Rice | 3807a76 | 2023-08-18 10:25:25 | [diff] [blame] | 134 | |
| 135 | #undef BENCHMARK_FOR_INT_TYPES |
| 136 | |
| 137 | } // namespace |
| 138 | } // namespace base |