aboutsummaryrefslogtreecommitdiff
path: root/libc/benchmarks/LibcMemoryBenchmark.cpp
blob: 3ced306584d15130ad17fa97de04412bed383109 (plain)
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