mirror of
https://github.com/bkaradzic/bx.git
synced 2026-10-22 16:51:01 +00:00
Added SIMD versions of memCopy, memMove, memSet, and memCmp, used when SIMD is available. Fixed memCmpRef comparing bytes as signed. Updated Catch2 to 3.16.0. (#448)
This commit is contained in:
committed by
GitHub
parent
d86e4ea9d9
commit
f56519b35d
752
3rdparty/catch/catch_amalgamated.cpp
vendored
752
3rdparty/catch/catch_amalgamated.cpp
vendored
File diff suppressed because it is too large
Load Diff
979
3rdparty/catch/catch_amalgamated.hpp
vendored
979
3rdparty/catch/catch_amalgamated.hpp
vendored
File diff suppressed because it is too large
Load Diff
375
src/bx.cpp
375
src/bx.cpp
@@ -6,6 +6,7 @@
|
||||
#include <bx/debug.h>
|
||||
#include <bx/readerwriter.h>
|
||||
#include <bx/os.h>
|
||||
#include <bx/simd_t.h>
|
||||
|
||||
#if !BX_CRT_NONE
|
||||
# include <string.h> // memcpy, memmove, memset
|
||||
@@ -102,6 +103,107 @@ namespace bx
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
#if BX_SIMD_AVX
|
||||
typedef simd256_t SimdT;
|
||||
#else
|
||||
typedef simd128_t SimdT;
|
||||
#endif // BX_SIMD_AVX
|
||||
|
||||
constexpr size_t kSimdSize = sizeof(SimdT);
|
||||
|
||||
#if BX_COMPILER_MSVC
|
||||
typedef __unaligned uint32_t UnalignedU32;
|
||||
typedef __unaligned uint64_t UnalignedU64;
|
||||
#else
|
||||
typedef uint32_t __attribute__( (aligned(1), may_alias) ) UnalignedU32;
|
||||
typedef uint64_t __attribute__( (aligned(1), may_alias) ) UnalignedU64;
|
||||
#endif // BX_COMPILER_MSVC
|
||||
|
||||
BX_FORCE_INLINE void copySmall(uint8_t* _dst, const uint8_t* _src, size_t _numBytes)
|
||||
{
|
||||
if (8 <= _numBytes)
|
||||
{
|
||||
const uint64_t head = *(const UnalignedU64*)_src;
|
||||
const uint64_t tail = *(const UnalignedU64*)(_src + _numBytes - 8);
|
||||
*(UnalignedU64*)_dst = head;
|
||||
*(UnalignedU64*)(_dst + _numBytes - 8) = tail;
|
||||
}
|
||||
else if (4 <= _numBytes)
|
||||
{
|
||||
const uint32_t head = *(const UnalignedU32*)_src;
|
||||
const uint32_t tail = *(const UnalignedU32*)(_src + _numBytes - 4);
|
||||
*(UnalignedU32*)_dst = head;
|
||||
*(UnalignedU32*)(_dst + _numBytes - 4) = tail;
|
||||
}
|
||||
else if (0 < _numBytes)
|
||||
{
|
||||
const uint8_t first = _src[0];
|
||||
const uint8_t middle = _src[_numBytes/2];
|
||||
const uint8_t last = _src[_numBytes - 1];
|
||||
_dst[0] = first;
|
||||
_dst[_numBytes/2] = middle;
|
||||
_dst[_numBytes - 1] = last;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Ty>
|
||||
BX_FORCE_INLINE void copyTwo(uint8_t* _dst, const uint8_t* _src, size_t _numBytes)
|
||||
{
|
||||
const Ty head = simd_ldu<Ty>(_src);
|
||||
const Ty tail = simd_ldu<Ty>(_src + _numBytes - sizeof(Ty) );
|
||||
simd_stu(_dst, head);
|
||||
simd_stu(_dst + _numBytes - sizeof(Ty), tail);
|
||||
}
|
||||
|
||||
BX_FORCE_INLINE void copyForward(uint8_t* _dst, const uint8_t* _src, size_t _begin, size_t _end)
|
||||
{
|
||||
size_t ii = _begin;
|
||||
|
||||
for (; ii + 4*kSimdSize <= _end; ii += 4*kSimdSize)
|
||||
{
|
||||
const SimdT a0 = simd_ldu<SimdT>(_src + ii);
|
||||
const SimdT a1 = simd_ldu<SimdT>(_src + ii + kSimdSize);
|
||||
const SimdT a2 = simd_ldu<SimdT>(_src + ii + 2*kSimdSize);
|
||||
const SimdT a3 = simd_ldu<SimdT>(_src + ii + 3*kSimdSize);
|
||||
simd_st(_dst + ii, a0);
|
||||
simd_st(_dst + ii + kSimdSize, a1);
|
||||
simd_st(_dst + ii + 2*kSimdSize, a2);
|
||||
simd_st(_dst + ii + 3*kSimdSize, a3);
|
||||
}
|
||||
|
||||
for (; ii + kSimdSize <= _end; ii += kSimdSize)
|
||||
{
|
||||
const SimdT a0 = simd_ldu<SimdT>(_src + ii);
|
||||
simd_st(_dst + ii, a0);
|
||||
}
|
||||
}
|
||||
|
||||
BX_FORCE_INLINE int32_t cmpBytes(const uint8_t* _lhs, const uint8_t* _rhs, size_t _numBytes)
|
||||
{
|
||||
for (size_t ii = 0; ii < _numBytes; ++ii)
|
||||
{
|
||||
if (_lhs[ii] != _rhs[ii])
|
||||
{
|
||||
return int32_t(_lhs[ii]) - int32_t(_rhs[ii]);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<typename Ty>
|
||||
BX_FORCE_INLINE bool memIsEqual(const uint8_t* _lhs, const uint8_t* _rhs)
|
||||
{
|
||||
const Ty lhs = simd_ldu<Ty>(_lhs);
|
||||
const Ty rhs = simd_ldu<Ty>(_rhs);
|
||||
const Ty diff = simd_xor(lhs, rhs);
|
||||
return simd_test_zero(diff, diff);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void memCopyRef(void* _dst, const void* _src, size_t _numBytes)
|
||||
{
|
||||
uint8_t* dst = (uint8_t*)_dst;
|
||||
@@ -113,13 +215,47 @@ namespace bx
|
||||
}
|
||||
}
|
||||
|
||||
void memCopySimd(void* _dst, const void* _src, size_t _numBytes)
|
||||
{
|
||||
uint8_t* dst = (uint8_t*)_dst;
|
||||
const uint8_t* src = (const uint8_t*)_src;
|
||||
|
||||
if (16 > _numBytes)
|
||||
{
|
||||
copySmall(dst, src, _numBytes);
|
||||
return;
|
||||
}
|
||||
|
||||
if (32 >= _numBytes)
|
||||
{
|
||||
copyTwo<simd128_t>(dst, src, _numBytes);
|
||||
return;
|
||||
}
|
||||
|
||||
if (2*kSimdSize >= _numBytes)
|
||||
{
|
||||
copyTwo<SimdT>(dst, src, _numBytes);
|
||||
return;
|
||||
}
|
||||
|
||||
const SimdT head = simd_ldu<SimdT>(src);
|
||||
const SimdT tail = simd_ldu<SimdT>(src + _numBytes - kSimdSize);
|
||||
simd_stu(dst, head);
|
||||
|
||||
copyForward(dst, src, kSimdSize - (uintptr_t(dst) & (kSimdSize - 1) ), _numBytes);
|
||||
|
||||
simd_stu(dst + _numBytes - kSimdSize, tail);
|
||||
}
|
||||
|
||||
void memCopy(void* _dst, const void* _src, size_t _numBytes)
|
||||
{
|
||||
#if BX_CRT_NONE
|
||||
#if BX_SIMD_SUPPORTED
|
||||
memCopySimd(_dst, _src, _numBytes);
|
||||
#elif BX_CRT_NONE
|
||||
memCopyRef(_dst, _src, _numBytes);
|
||||
#else
|
||||
::memcpy(_dst, _src, _numBytes);
|
||||
#endif // BX_CRT_NONE
|
||||
#endif // BX_*
|
||||
}
|
||||
|
||||
void memCopy(
|
||||
@@ -171,13 +307,79 @@ namespace bx
|
||||
}
|
||||
}
|
||||
|
||||
void memMoveSimd(void* _dst, const void* _src, size_t _numBytes)
|
||||
{
|
||||
uint8_t* dst = (uint8_t*)_dst;
|
||||
const uint8_t* src = (const uint8_t*)_src;
|
||||
|
||||
if (0 == _numBytes
|
||||
|| dst == src)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (16 > _numBytes)
|
||||
{
|
||||
copySmall(dst, src, _numBytes);
|
||||
return;
|
||||
}
|
||||
|
||||
if (32 >= _numBytes)
|
||||
{
|
||||
copyTwo<simd128_t>(dst, src, _numBytes);
|
||||
return;
|
||||
}
|
||||
|
||||
if (2*kSimdSize >= _numBytes)
|
||||
{
|
||||
copyTwo<SimdT>(dst, src, _numBytes);
|
||||
return;
|
||||
}
|
||||
|
||||
const SimdT head = simd_ldu<SimdT>(src);
|
||||
const SimdT tail = simd_ldu<SimdT>(src + _numBytes - kSimdSize);
|
||||
|
||||
if (dst < src
|
||||
|| dst >= src + _numBytes)
|
||||
{
|
||||
copyForward(dst, src, kSimdSize - (uintptr_t(dst) & (kSimdSize - 1) ), _numBytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t ii = _numBytes - (uintptr_t(dst + _numBytes) & (kSimdSize - 1) );
|
||||
|
||||
for (; ii >= 4*kSimdSize; ii -= 4*kSimdSize)
|
||||
{
|
||||
const SimdT a0 = simd_ldu<SimdT>(src + ii - kSimdSize);
|
||||
const SimdT a1 = simd_ldu<SimdT>(src + ii - 2*kSimdSize);
|
||||
const SimdT a2 = simd_ldu<SimdT>(src + ii - 3*kSimdSize);
|
||||
const SimdT a3 = simd_ldu<SimdT>(src + ii - 4*kSimdSize);
|
||||
simd_st(dst + ii - kSimdSize, a0);
|
||||
simd_st(dst + ii - 2*kSimdSize, a1);
|
||||
simd_st(dst + ii - 3*kSimdSize, a2);
|
||||
simd_st(dst + ii - 4*kSimdSize, a3);
|
||||
}
|
||||
|
||||
for (; ii >= kSimdSize; ii -= kSimdSize)
|
||||
{
|
||||
const SimdT a0 = simd_ldu<SimdT>(src + ii - kSimdSize);
|
||||
simd_st(dst + ii - kSimdSize, a0);
|
||||
}
|
||||
}
|
||||
|
||||
simd_stu(dst, head);
|
||||
simd_stu(dst + _numBytes - kSimdSize, tail);
|
||||
}
|
||||
|
||||
void memMove(void* _dst, const void* _src, size_t _numBytes)
|
||||
{
|
||||
#if BX_CRT_NONE
|
||||
#if BX_SIMD_SUPPORTED
|
||||
memMoveSimd(_dst, _src, _numBytes);
|
||||
#elif BX_CRT_NONE
|
||||
memMoveRef(_dst, _src, _numBytes);
|
||||
#else
|
||||
::memmove(_dst, _src, _numBytes);
|
||||
#endif // BX_CRT_NONE
|
||||
#endif // BX_*
|
||||
}
|
||||
|
||||
void memMove(
|
||||
@@ -215,13 +417,79 @@ namespace bx
|
||||
}
|
||||
}
|
||||
|
||||
void memSetSimd(void* _dst, uint8_t _ch, size_t _numBytes)
|
||||
{
|
||||
uint8_t* dst = (uint8_t*)_dst;
|
||||
|
||||
if (16 > _numBytes)
|
||||
{
|
||||
const uint64_t value = uint64_t(_ch) * UINT64_C(0x0101010101010101);
|
||||
|
||||
if (8 <= _numBytes)
|
||||
{
|
||||
*(UnalignedU64*)dst = value;
|
||||
*(UnalignedU64*)(dst + _numBytes - 8) = value;
|
||||
}
|
||||
else if (4 <= _numBytes)
|
||||
{
|
||||
*(UnalignedU32*)dst = uint32_t(value);
|
||||
*(UnalignedU32*)(dst + _numBytes - 4) = uint32_t(value);
|
||||
}
|
||||
else if (0 < _numBytes)
|
||||
{
|
||||
dst[0] = _ch;
|
||||
dst[_numBytes/2] = _ch;
|
||||
dst[_numBytes - 1] = _ch;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const uint32_t pattern = uint32_t(_ch) * 0x01010101u;
|
||||
|
||||
if (32 >= _numBytes)
|
||||
{
|
||||
const simd128_t value = simd_splat<simd128_t>(pattern);
|
||||
simd_stu(dst, value);
|
||||
simd_stu(dst + _numBytes - 16, value);
|
||||
return;
|
||||
}
|
||||
|
||||
const SimdT value = simd_splat<SimdT>(pattern);
|
||||
|
||||
simd_stu(dst, value);
|
||||
simd_stu(dst + _numBytes - kSimdSize, value);
|
||||
|
||||
if (2*kSimdSize >= _numBytes)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
size_t ii = kSimdSize - (uintptr_t(dst) & (kSimdSize - 1) );
|
||||
|
||||
for (; ii + 4*kSimdSize <= _numBytes; ii += 4*kSimdSize)
|
||||
{
|
||||
simd_st(dst + ii, value);
|
||||
simd_st(dst + ii + kSimdSize, value);
|
||||
simd_st(dst + ii + 2*kSimdSize, value);
|
||||
simd_st(dst + ii + 3*kSimdSize, value);
|
||||
}
|
||||
|
||||
for (; ii + kSimdSize <= _numBytes; ii += kSimdSize)
|
||||
{
|
||||
simd_st(dst + ii, value);
|
||||
}
|
||||
}
|
||||
|
||||
void memSet(void* _dst, uint8_t _ch, size_t _numBytes)
|
||||
{
|
||||
#if BX_CRT_NONE
|
||||
#if BX_SIMD_SUPPORTED
|
||||
memSetSimd(_dst, _ch, _numBytes);
|
||||
#elif BX_CRT_NONE
|
||||
memSetRef(_dst, _ch, _numBytes);
|
||||
#else
|
||||
::memset(_dst, _ch, _numBytes);
|
||||
#endif // BX_CRT_NONE
|
||||
#endif // BX_*
|
||||
}
|
||||
|
||||
void memSet(void* _dst, uint32_t _dstStride, uint8_t _ch, uint32_t _stride, uint32_t _num)
|
||||
@@ -242,8 +510,8 @@ namespace bx
|
||||
|
||||
int32_t memCmpRef(const void* _lhs, const void* _rhs, size_t _numBytes)
|
||||
{
|
||||
const char* lhs = (const char*)_lhs;
|
||||
const char* rhs = (const char*)_rhs;
|
||||
const uint8_t* lhs = (const uint8_t*)_lhs;
|
||||
const uint8_t* rhs = (const uint8_t*)_rhs;
|
||||
for (
|
||||
; 0 < _numBytes && *lhs == *rhs
|
||||
; ++lhs, ++rhs, --_numBytes
|
||||
@@ -251,16 +519,101 @@ namespace bx
|
||||
{
|
||||
}
|
||||
|
||||
return 0 == _numBytes ? 0 : *lhs - *rhs;
|
||||
return 0 == _numBytes ? 0 : int32_t(*lhs) - int32_t(*rhs);
|
||||
}
|
||||
|
||||
int32_t memCmpSimd(const void* _lhs, const void* _rhs, size_t _numBytes)
|
||||
{
|
||||
const uint8_t* lhs = (const uint8_t*)_lhs;
|
||||
const uint8_t* rhs = (const uint8_t*)_rhs;
|
||||
|
||||
if (16 > _numBytes)
|
||||
{
|
||||
if (8 <= _numBytes)
|
||||
{
|
||||
if (*(const UnalignedU64*)lhs == *(const UnalignedU64*)rhs
|
||||
&& *(const UnalignedU64*)(lhs + _numBytes - 8) == *(const UnalignedU64*)(rhs + _numBytes - 8) )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else if (4 <= _numBytes)
|
||||
{
|
||||
if (*(const UnalignedU32*)lhs == *(const UnalignedU32*)rhs
|
||||
&& *(const UnalignedU32*)(lhs + _numBytes - 4) == *(const UnalignedU32*)(rhs + _numBytes - 4) )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return cmpBytes(lhs, rhs, _numBytes);
|
||||
}
|
||||
|
||||
if (kSimdSize > _numBytes)
|
||||
{
|
||||
if (!memIsEqual<simd128_t>(lhs, rhs) )
|
||||
{
|
||||
return cmpBytes(lhs, rhs, 16);
|
||||
}
|
||||
|
||||
const size_t last = _numBytes - 16;
|
||||
|
||||
return memIsEqual<simd128_t>(lhs + last, rhs + last)
|
||||
? 0
|
||||
: cmpBytes(lhs + last, rhs + last, 16)
|
||||
;
|
||||
}
|
||||
|
||||
size_t ii = 0;
|
||||
|
||||
for (; ii + 2*kSimdSize <= _numBytes; ii += 2*kSimdSize)
|
||||
{
|
||||
const SimdT lhs0 = simd_ldu<SimdT>(lhs + ii);
|
||||
const SimdT rhs0 = simd_ldu<SimdT>(rhs + ii);
|
||||
const SimdT lhs1 = simd_ldu<SimdT>(lhs + ii + kSimdSize);
|
||||
const SimdT rhs1 = simd_ldu<SimdT>(rhs + ii + kSimdSize);
|
||||
const SimdT diff0 = simd_xor(lhs0, rhs0);
|
||||
const SimdT diff1 = simd_xor(lhs1, rhs1);
|
||||
const SimdT diff = simd_or(diff0, diff1);
|
||||
|
||||
if (!simd_test_zero(diff, diff) )
|
||||
{
|
||||
return cmpBytes(lhs + ii, rhs + ii, 2*kSimdSize);
|
||||
}
|
||||
}
|
||||
|
||||
if (ii + kSimdSize <= _numBytes)
|
||||
{
|
||||
if (!memIsEqual<SimdT>(lhs + ii, rhs + ii) )
|
||||
{
|
||||
return cmpBytes(lhs + ii, rhs + ii, kSimdSize);
|
||||
}
|
||||
|
||||
ii += kSimdSize;
|
||||
}
|
||||
|
||||
if (ii < _numBytes)
|
||||
{
|
||||
const size_t last = _numBytes - kSimdSize;
|
||||
|
||||
if (!memIsEqual<SimdT>(lhs + last, rhs + last) )
|
||||
{
|
||||
return cmpBytes(lhs + last, rhs + last, kSimdSize);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t memCmp(const void* _lhs, const void* _rhs, size_t _numBytes)
|
||||
{
|
||||
#if BX_CRT_NONE
|
||||
#if BX_SIMD_SUPPORTED
|
||||
return memCmpSimd(_lhs, _rhs, _numBytes);
|
||||
#elif BX_CRT_NONE
|
||||
return memCmpRef(_lhs, _rhs, _numBytes);
|
||||
#else
|
||||
return ::memcmp(_lhs, _rhs, _numBytes);
|
||||
#endif // BX_CRT_NONE
|
||||
#endif // BX_*
|
||||
}
|
||||
|
||||
///
|
||||
|
||||
@@ -5,6 +5,24 @@
|
||||
|
||||
#include "test.h"
|
||||
|
||||
#include <bx/allocator.h>
|
||||
#include <bx/rng.h>
|
||||
#include <bx/string.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
namespace bx
|
||||
{
|
||||
void memCopyRef(void* _dst, const void* _src, size_t _numBytes);
|
||||
void memCopySimd(void* _dst, const void* _src, size_t _numBytes);
|
||||
void memMoveRef(void* _dst, const void* _src, size_t _numBytes);
|
||||
void memMoveSimd(void* _dst, const void* _src, size_t _numBytes);
|
||||
void memSetRef(void* _dst, uint8_t _ch, size_t _numBytes);
|
||||
void memSetSimd(void* _dst, uint8_t _ch, size_t _numBytes);
|
||||
int32_t memCmpRef(const void* _lhs, const void* _rhs, size_t _numBytes);
|
||||
int32_t memCmpSimd(const void* _lhs, const void* _rhs, size_t _numBytes);
|
||||
}
|
||||
|
||||
TEST_CASE("memSet", "")
|
||||
{
|
||||
char temp[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
|
||||
@@ -56,3 +74,272 @@ TEST_CASE("scatter/gather", "")
|
||||
REQUIRE(0 == bx::memCmp(tmp1, str, 5) );
|
||||
|
||||
}
|
||||
|
||||
TEST_CASE("memCmpRef", "")
|
||||
{
|
||||
const uint8_t lhs[] = { 1, 2, 0x80 };
|
||||
const uint8_t rhs[] = { 1, 2, 0x7f };
|
||||
|
||||
REQUIRE(0 < bx::memCmpRef(lhs, rhs, 3) );
|
||||
REQUIRE(0 > bx::memCmpRef(rhs, lhs, 3) );
|
||||
REQUIRE(0 == bx::memCmpRef(lhs, rhs, 2) );
|
||||
REQUIRE(0 < ::memcmp(lhs, rhs, 3) );
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr uint32_t kMaxSize = 300;
|
||||
constexpr uint32_t kGuard = 32;
|
||||
constexpr uint32_t kMaxShift = 32;
|
||||
constexpr uint32_t kBuffer = kGuard + kMaxShift + kMaxSize + kGuard;
|
||||
constexpr uint8_t kGuardVal = 0xcd;
|
||||
|
||||
void fillRandom(uint8_t* _data, uint32_t _num, bx::RngMwc& _rng)
|
||||
{
|
||||
for (uint32_t ii = 0; ii < _num; ++ii)
|
||||
{
|
||||
_data[ii] = uint8_t(_rng.gen() );
|
||||
}
|
||||
}
|
||||
|
||||
int32_t sign(int32_t _value)
|
||||
{
|
||||
return (0 < _value) - (0 > _value);
|
||||
}
|
||||
|
||||
uint32_t mismatch(const uint8_t* _lhs, const uint8_t* _rhs, uint32_t _size)
|
||||
{
|
||||
return sign(bx::memCmpSimd(_lhs, _rhs, _size) ) != sign(::memcmp(_lhs, _rhs, _size) );
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("memCopySimd", "")
|
||||
{
|
||||
bx::RngMwc rng;
|
||||
|
||||
uint8_t src[kBuffer];
|
||||
uint8_t dst[kBuffer];
|
||||
fillRandom(src, kBuffer, rng);
|
||||
|
||||
for (uint32_t size = 0; size <= kMaxSize; ++size)
|
||||
{
|
||||
for (uint32_t srcOffset = 0; srcOffset < kMaxShift; ++srcOffset)
|
||||
{
|
||||
for (uint32_t dstOffset = 0; dstOffset < kMaxShift; ++dstOffset)
|
||||
{
|
||||
::memset(dst, kGuardVal, kBuffer);
|
||||
|
||||
const uint8_t* from = src + kGuard + srcOffset;
|
||||
uint8_t* to = dst + kGuard + dstOffset;
|
||||
bx::memCopySimd(to, from, size);
|
||||
|
||||
REQUIRE(0 == ::memcmp(to, from, size) );
|
||||
|
||||
bool guardsIntact = true;
|
||||
for (uint32_t ii = 0; ii < kBuffer; ++ii)
|
||||
{
|
||||
const bool inside = dst + ii >= to && dst + ii < to + size;
|
||||
guardsIntact &= inside || kGuardVal == dst[ii];
|
||||
}
|
||||
|
||||
REQUIRE(guardsIntact);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("memMoveSimd", "")
|
||||
{
|
||||
bx::RngMwc rng;
|
||||
|
||||
uint8_t initial[kBuffer];
|
||||
uint8_t actual[kBuffer];
|
||||
uint8_t expected[kBuffer];
|
||||
fillRandom(initial, kBuffer, rng);
|
||||
|
||||
const uint32_t kMaxDistance = 40;
|
||||
|
||||
for (uint32_t size = 0; size <= kMaxSize; size += 1 + size/32)
|
||||
{
|
||||
uint32_t numMismatch = 0;
|
||||
|
||||
for (uint32_t srcPos = kGuard; srcPos <= kGuard + kMaxDistance; ++srcPos)
|
||||
{
|
||||
for (uint32_t dstPos = kGuard; dstPos <= kGuard + kMaxDistance; ++dstPos)
|
||||
{
|
||||
if (srcPos + size + kGuard > kBuffer
|
||||
|| dstPos + size + kGuard > kBuffer)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
::memcpy(actual, initial, kBuffer);
|
||||
::memcpy(expected, initial, kBuffer);
|
||||
|
||||
bx::memMoveSimd(actual + dstPos, actual + srcPos, size);
|
||||
::memmove(expected + dstPos, expected + srcPos, size);
|
||||
|
||||
numMismatch += 0 != ::memcmp(actual, expected, kBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
CAPTURE(size);
|
||||
REQUIRE(0 == numMismatch);
|
||||
}
|
||||
|
||||
for (uint32_t size = 0; size <= 100; ++size)
|
||||
{
|
||||
::memcpy(actual, initial, kBuffer);
|
||||
::memcpy(expected, initial, kBuffer);
|
||||
|
||||
bx::memMoveSimd(actual + kBuffer - kGuard - size, actual + kGuard, size);
|
||||
::memmove(expected + kBuffer - kGuard - size, expected + kGuard, size);
|
||||
REQUIRE(0 == ::memcmp(actual, expected, kBuffer) );
|
||||
|
||||
bx::memMoveSimd(actual + kGuard, actual + kBuffer - kGuard - size, size);
|
||||
::memmove(expected + kGuard, expected + kBuffer - kGuard - size, size);
|
||||
REQUIRE(0 == ::memcmp(actual, expected, kBuffer) );
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("memSetSimd", "")
|
||||
{
|
||||
const uint8_t values[] = { 0x00, 0x01, 0x7f, 0x80, 0xa5, 0xff };
|
||||
|
||||
uint8_t dst[kBuffer];
|
||||
|
||||
for (uint8_t value : values)
|
||||
{
|
||||
for (uint32_t size = 0; size <= kMaxSize; ++size)
|
||||
{
|
||||
for (uint32_t offset = 0; offset < kMaxShift; ++offset)
|
||||
{
|
||||
::memset(dst, kGuardVal, kBuffer);
|
||||
|
||||
uint8_t* to = dst + kGuard + offset;
|
||||
bx::memSetSimd(to, value, size);
|
||||
|
||||
bool filled = true;
|
||||
for (uint32_t ii = 0; ii < kBuffer; ++ii)
|
||||
{
|
||||
const bool inside = dst + ii >= to && dst + ii < to + size;
|
||||
filled &= (inside ? value : kGuardVal) == dst[ii];
|
||||
}
|
||||
|
||||
REQUIRE(filled);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("memCmpSimd", "")
|
||||
{
|
||||
bx::RngMwc rng;
|
||||
|
||||
uint8_t lhs[kBuffer];
|
||||
uint8_t rhs[kBuffer];
|
||||
fillRandom(lhs, kBuffer, rng);
|
||||
|
||||
const uint32_t offsets[] = { 0, 1, 7, 15, 31 };
|
||||
|
||||
for (uint32_t size = 0; size <= kMaxSize; ++size)
|
||||
{
|
||||
uint32_t numMismatch = 0;
|
||||
|
||||
for (uint32_t lhsOffset : offsets)
|
||||
{
|
||||
for (uint32_t rhsOffset : offsets)
|
||||
{
|
||||
uint8_t* left = lhs + kGuard + lhsOffset;
|
||||
uint8_t* right = rhs + kGuard + rhsOffset;
|
||||
|
||||
fillRandom(rhs, kBuffer, rng);
|
||||
::memcpy(right, left, size);
|
||||
|
||||
numMismatch += 0 != bx::memCmpSimd(left, right, size);
|
||||
|
||||
for (uint32_t pos = 0; pos < size; ++pos)
|
||||
{
|
||||
const uint8_t saved = right[pos];
|
||||
|
||||
right[pos] = uint8_t(left[pos] ^ 0x80);
|
||||
numMismatch += mismatch(left, right, size);
|
||||
numMismatch += mismatch(right, left, size);
|
||||
|
||||
right[pos] = uint8_t(left[pos] + 1);
|
||||
numMismatch += mismatch(left, right, size);
|
||||
|
||||
if (pos + 1 < size)
|
||||
{
|
||||
right[size - 1] = uint8_t(left[size - 1] - 1);
|
||||
numMismatch += mismatch(left, right, size);
|
||||
right[size - 1] = left[size - 1];
|
||||
}
|
||||
|
||||
right[pos] = saved;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CAPTURE(size);
|
||||
REQUIRE(0 == numMismatch);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("memSimd benchmark", "[.][!benchmark]")
|
||||
{
|
||||
const uint32_t sizes[] = { 7, 16, 64, 256, 4<<10, 64<<10, 1<<20 };
|
||||
const uint32_t kMax = 1<<20;
|
||||
|
||||
static uint8_t s_src[kMax + 4096];
|
||||
static uint8_t s_dst[kMax + 8192];
|
||||
uint8_t* src = (uint8_t*)bx::alignPtr(s_src, 0, 4096) + 1;
|
||||
uint8_t* dst = (uint8_t*)bx::alignPtr(s_dst, 0, 4096) + 2048 + 3;
|
||||
|
||||
::memset(src, 0x5a, kMax);
|
||||
::memset(dst, 0x5a, kMax);
|
||||
|
||||
char name[64];
|
||||
|
||||
for (uint32_t size : sizes)
|
||||
{
|
||||
const uint32_t moveSize = size - 5;
|
||||
|
||||
bx::snprintf(name, sizeof(name), "::memcpy %u", size);
|
||||
BENCHMARK(name) { ::memcpy(dst, src, size); return dst[0]; };
|
||||
bx::snprintf(name, sizeof(name), "bx::memCopyRef %u", size);
|
||||
BENCHMARK(name) { bx::memCopyRef(dst, src, size); return dst[0]; };
|
||||
bx::snprintf(name, sizeof(name), "bx::memCopySimd %u", size);
|
||||
BENCHMARK(name) { bx::memCopySimd(dst, src, size); return dst[0]; };
|
||||
bx::snprintf(name, sizeof(name), "bx::memCopy %u", size);
|
||||
BENCHMARK(name) { bx::memCopy(dst, src, size); return dst[0]; };
|
||||
|
||||
bx::snprintf(name, sizeof(name), "::memmove %u", size);
|
||||
BENCHMARK(name) { ::memmove(dst, dst + 5, moveSize); return dst[0]; };
|
||||
bx::snprintf(name, sizeof(name), "bx::memMoveRef %u", size);
|
||||
BENCHMARK(name) { bx::memMoveRef(dst, dst + 5, moveSize); return dst[0]; };
|
||||
bx::snprintf(name, sizeof(name), "bx::memMoveSimd %u", size);
|
||||
BENCHMARK(name) { bx::memMoveSimd(dst, dst + 5, moveSize); return dst[0]; };
|
||||
bx::snprintf(name, sizeof(name), "bx::memMove %u", size);
|
||||
BENCHMARK(name) { bx::memMove(dst, dst + 5, moveSize); return dst[0]; };
|
||||
|
||||
bx::snprintf(name, sizeof(name), "::memset %u", size);
|
||||
BENCHMARK(name) { ::memset(dst, 0x5a, size); return dst[0]; };
|
||||
bx::snprintf(name, sizeof(name), "bx::memSetRef %u", size);
|
||||
BENCHMARK(name) { bx::memSetRef(dst, 0x5a, size); return dst[0]; };
|
||||
bx::snprintf(name, sizeof(name), "bx::memSetSimd %u", size);
|
||||
BENCHMARK(name) { bx::memSetSimd(dst, 0x5a, size); return dst[0]; };
|
||||
bx::snprintf(name, sizeof(name), "bx::memSet %u", size);
|
||||
BENCHMARK(name) { bx::memSet(dst, 0x5a, size); return dst[0]; };
|
||||
|
||||
bx::snprintf(name, sizeof(name), "::memcmp %u", size);
|
||||
BENCHMARK(name) { return ::memcmp(dst, src, size); };
|
||||
bx::snprintf(name, sizeof(name), "bx::memCmpRef %u", size);
|
||||
BENCHMARK(name) { return bx::memCmpRef(dst, src, size); };
|
||||
bx::snprintf(name, sizeof(name), "bx::memCmpSimd %u", size);
|
||||
BENCHMARK(name) { return bx::memCmpSimd(dst, src, size); };
|
||||
bx::snprintf(name, sizeof(name), "bx::memCmp %u", size);
|
||||
BENCHMARK(name) { return bx::memCmp(dst, src, size); };
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user