1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
|
//===-- Benchmark memory specific tools -----------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "LibcMemoryBenchmark.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/MathExtras.h"
#include <algorithm>
namespace llvm {
namespace libc_benchmarks {
// Returns a distribution that samples the buffer to satisfy the required
// alignment.
// When alignment is set, the distribution is scaled down by `Factor` and scaled
// up again by the same amount during sampling.
static std::uniform_int_distribution<uint32_t>
getOffsetDistribution(size_t BufferSize, size_t MaxSizeValue,
MaybeAlign AccessAlignment) {
if (AccessAlignment && *AccessAlignment > AlignedBuffer::Alignment)
report_fatal_error(
"AccessAlignment must be less or equal to AlignedBuffer::Alignment");
if (!AccessAlignment)
return std::uniform_int_distribution<uint32_t>(0, 0); // Always 0.
// If we test up to Size bytes, the returned offset must stay under
// BuffersSize - Size.
int64_t MaxOffset = BufferSize;
MaxOffset -= MaxSizeValue;
MaxOffset -= 1;
if (MaxOffset < 0)
report_fatal_error(
"BufferSize too small to exercise specified Size configuration");
MaxOffset /= AccessAlignment->value();
return std::uniform_int_distribution<uint32_t>(0, MaxOffset);
}
OffsetDistribution::OffsetDistribution(size_t BufferSize, size_t MaxSizeValue,
MaybeAlign AccessAlignment)
: Distribution(
getOffsetDistribution(BufferSize, MaxSizeValue, AccessAlignment)),
Factor(AccessAlignment.valueOrOne().value()) {}
// Precomputes offset where to insert mismatches between the two buffers.
MismatchOffsetDistribution::MismatchOffsetDistribution(size_t BufferSize,
size_t MaxSizeValue,
size_t MismatchAt)
: MismatchAt(MismatchAt) {
if (MismatchAt <= 1)
return;
for (size_t I = MaxSizeValue + 1; I < BufferSize; I += MaxSizeValue)
MismatchIndices.push_back(I);
if (MismatchIndices.empty())
report_fatal_error("Unable to generate mismatch");
MismatchIndexSelector =
std::uniform_int_distribution<size_t>(0, MismatchIndices.size() - 1);
}
static size_t getL1DataCacheSize() {
const std::vector<CacheInfo> &CacheInfos = HostState::get().Caches;
const auto IsL1DataCache = [](const CacheInfo &CI) {
return CI.Type == "Data" && CI.Level == 1;
};
const auto CacheIt = find_if(CacheInfos, IsL1DataCache);
if (CacheIt != CacheInfos.end())
return CacheIt->Size;
report_fatal_error("Unable to read L1 Cache Data Size");
}
static constexpr int64_t KiB = 1024;
static constexpr int64_t ParameterStorageBytes = 4 * KiB;
static constexpr int64_t L1LeftAsideBytes = 1 * KiB;
static size_t getAvailableBufferSize() {
return getL1DataCacheSize() - L1LeftAsideBytes - ParameterStorageBytes;
}
ParameterBatch::ParameterBatch(size_t BufferCount)
: BufferSize(getAvailableBufferSize() / BufferCount),
BatchSize(ParameterStorageBytes / sizeof(ParameterType)),
Parameters(BatchSize) {
if (BufferSize <= 0 || BatchSize < 100)
report_fatal_error("Not enough L1 cache");
const size_t ParameterBytes = Parameters.size() * sizeof(ParameterType);
const size_t BufferBytes = BufferSize * BufferCount;
if (ParameterBytes + BufferBytes + L1LeftAsideBytes > getL1DataCacheSize())
report_fatal_error(
"We're splitting a buffer of the size of the L1 cache between a data "
"buffer and a benchmark parameters buffer, so by construction the "
"total should not exceed the size of the L1 cache");
}
size_t ParameterBatch::getBatchBytes() const {
size_t BatchBytes = 0;
for (auto &P : Parameters)
BatchBytes += P.SizeBytes;
return BatchBytes;
}
void ParameterBatch::checkValid(const ParameterType &P) const {
if (P.OffsetBytes + P.SizeBytes >= BufferSize)
report_fatal_error(
llvm::Twine("Call would result in buffer overflow: Offset=")
.concat(llvm::Twine(P.OffsetBytes))
.concat(", Size=")
.concat(llvm::Twine(P.SizeBytes))
.concat(", BufferSize=")
.concat(llvm::Twine(BufferSize)));
}
CopySetup::CopySetup()
: ParameterBatch(2), SrcBuffer(ParameterBatch::BufferSize),
DstBuffer(ParameterBatch::BufferSize) {}
MoveSetup::MoveSetup()
: ParameterBatch(3), Buffer(ParameterBatch::BufferSize * 3) {}
ComparisonSetup::ComparisonSetup()
: ParameterBatch(2), LhsBuffer(ParameterBatch::BufferSize),
RhsBuffer(ParameterBatch::BufferSize) {
// The memcmp buffers always compare equal.
memset(LhsBuffer.begin(), 0xF, BufferSize);
memset(RhsBuffer.begin(), 0xF, BufferSize);
}
SetSetup::SetSetup()
: ParameterBatch(1), DstBuffer(ParameterBatch::BufferSize) {}
} // namespace libc_benchmarks
} // namespace llvm
|