Files
snmalloc/src/test/perf/memcpy/memcpy.cc
Matthew Parkinson c445de8eb4 Alter FailFast behaviour of memcpy (#526)
This commit changes the codegen for error messages for failed memcpys.
This no longer generates a stack frame and correctly tail calls the
error messages generator.

It also turns the error messages on in Release builds.  This will lead
to better adoption experience.
2022-05-19 14:20:45 +01:00

178 lines
3.9 KiB
C++

#include "snmalloc/global/memcpy.h"
#include <test/measuretime.h>
#include <test/opt.h>
#include <vector>
using namespace snmalloc;
struct Shape
{
void* object;
void* dst;
};
size_t my_random()
{
return (size_t)rand();
}
std::vector<Shape> allocs;
void shape(size_t size)
{
for (size_t i = 0; i < 1000; i++)
{
auto rsize = size * 2;
auto offset = 0;
// Uncomment the next two lines to introduce some randomness to the start of
// the memcpys. constexpr size_t alignment = 16; offset = (my_random() %
// size / alignment) * alignment;
Shape s;
s.object = ThreadAlloc::get().alloc(rsize);
s.dst = static_cast<unsigned char*>(s.object) + offset;
// Bring into cache the destination of the copy.
memset(s.dst, 0xFF, size);
allocs.push_back(s);
}
}
void unshape()
{
for (auto& s : allocs)
{
ThreadAlloc::get().dealloc(s.object);
}
allocs.clear();
}
template<typename Memcpy>
void test_memcpy(size_t size, void* src, Memcpy mc)
{
for (auto& s : allocs)
{
auto* dst = static_cast<unsigned char*>(s.dst);
mc(dst, src, size);
}
}
template<typename Memcpy>
void test(
size_t size,
Memcpy mc,
std::vector<std::pair<size_t, std::chrono::nanoseconds>>& stats)
{
auto src = ThreadAlloc::get().alloc(size);
shape(size);
for (size_t i = 0; i < 10; i++)
{
MeasureTime m(true);
test_memcpy(size, src, mc);
auto time = m.get_time();
stats.push_back({size, time});
}
ThreadAlloc::get().dealloc(src);
unshape();
}
NOINLINE
void memcpy_checked(void* dst, const void* src, size_t size)
{
memcpy<true>(dst, src, size);
}
NOINLINE
void memcpy_unchecked(void* dst, const void* src, size_t size)
{
memcpy<false>(dst, src, size);
}
NOINLINE
void memcpy_platform_checked(void* dst, const void* src, size_t size)
{
if (SNMALLOC_UNLIKELY(!check_bounds(dst, size)))
{
report_fatal_bounds_error(dst, size, "");
return;
}
memcpy(dst, src, size);
}
int main(int argc, char** argv)
{
opt::Opt opt(argc, argv);
#ifndef SNMALLOC_PASS_THROUGH
bool full_test = opt.has("--full_test");
// size_t size = 0;
auto mc1 = [](void* dst, const void* src, size_t len) {
memcpy_platform_checked(dst, src, len);
};
auto mc2 = [](void* dst, const void* src, size_t len) {
memcpy_unchecked(dst, src, len);
};
auto mc3 = [](void* dst, const void* src, size_t len) {
memcpy(dst, src, len);
};
std::vector<size_t> sizes;
for (size_t size = 0; size < 64; size++)
{
sizes.push_back(size);
}
for (size_t size = 64; size < 256; size += 16)
{
sizes.push_back(size);
sizes.push_back(size + 5);
}
for (size_t size = 256; size < 1024; size += 64)
{
sizes.push_back(size);
sizes.push_back(size + 5);
}
for (size_t size = 1024; size < 8192; size += 256)
{
sizes.push_back(size);
sizes.push_back(size + 5);
}
for (size_t size = 8192; size < bits::one_at_bit(18); size <<= 1)
{
sizes.push_back(size);
sizes.push_back(size + 5);
}
std::vector<std::pair<size_t, std::chrono::nanoseconds>> stats_checked;
std::vector<std::pair<size_t, std::chrono::nanoseconds>> stats_unchecked;
std::vector<std::pair<size_t, std::chrono::nanoseconds>> stats_platform;
printf("size, checked, unchecked, platform\n");
size_t repeats = full_test ? 80 : 1;
for (auto repeat = repeats; 0 < repeat; repeat--)
{
for (auto copy_size : sizes)
{
test(copy_size, mc1, stats_checked);
test(copy_size, mc2, stats_unchecked);
test(copy_size, mc3, stats_platform);
}
for (size_t i = 0; i < stats_checked.size(); i++)
{
auto& s1 = stats_checked[i];
auto& s2 = stats_unchecked[i];
auto& s3 = stats_platform[i];
std::cout << s1.first << ", " << s1.second.count() << ", "
<< s2.second.count() << ", " << s3.second.count() << std::endl;
}
stats_checked.clear();
stats_unchecked.clear();
stats_platform.clear();
}
#else
snmalloc::UNUSED(opt);
#endif
return 0;
}