mirror of
https://chromium.googlesource.com/libyuv/libyuv
synced 2026-10-29 02:17:13 +08:00
- Add HammingDistance_AVX512BW, SumSquareError_AVX512BW and
HashDjb2_AVX512BW for 32 and 64 bit, as inline assembly in
compare_gcc.cc and intrinsics in compare_win.cc.
- AVX512BW processes 64 bytes per loop with full vectors, and the
remainder of 1 to 63 bytes with one masked load, so compare.cc passes
any count and does not use C for the remainder. HashDjb2 loads the
remainder into the high bytes, ending at src + count, so the same
33 ^ (63 - i) multipliers apply, and multiplies the hash by 33 ^ n.
- Add SumSquareError_AVX2 and HashDjb2_AVX2 to compare_gcc.cc and
HammingDistance_AVX2 to compare_win.cc, so both files have the same
functions. Enable AVX2 in compare_gcc.cc for gcc as well as clang.
- HashDjb2 AVX2 and AVX512BW split the 32 bit multipliers 33 ^ i into
16 bit halves and use vpmaddwd, instead of vpmulld which is 2 uops
with 10 cycle latency. Bytes are zero extended with vpmovzxbw from
memory. The hash = hash * 33 ^ 64 + sum dependency uses scalar imul
instead of vpmulld. AVX2 does 64 bytes per horizontal add.
- Port compare_win.cc from 32 bit Visual C inline assembly to
intrinsics and enable it for Visual C 32/64 bit and
LIBYUV_ENABLE_ROWWIN, which MemorySanitizer also enables. Visual C 64
bit and MemorySanitizer previously used C.
- Fix HammingDistance_SSSE3 and HammingDistance_AVX2 to allow unaligned
pointers. They used movdqa and crashed on unaligned src_a.
- Add TestCompareAnyCount to test every count from 0 to 321, aligned
and unaligned, ending at the end of a page.
Functions available:
Function compare_gcc compare_win
32/64 bit 32/64 bit
HammingDistance_SSE42 asm intrinsics
HammingDistance_SSSE3 asm -
HammingDistance_AVX2 asm intrinsics
HammingDistance_AVX512BW asm intrinsics
SumSquareError_SSE2 asm intrinsics
SumSquareError_AVX2 asm intrinsics
SumSquareError_AVX512BW asm intrinsics
HashDjb2_SSE41 asm intrinsics
HashDjb2_AVX2 asm intrinsics
HashDjb2_AVX512BW asm intrinsics
Performance, 1280x720, LIBYUV_REPEAT=1000, best of 3, ms.
Before is main (SSE, and AVX2 Hamming only, in compare_gcc).
BenchmarkDjb2_Opt main gcc gcc win win
AVX2 AVX512 AVX2 AVX512
Intel Haswell 179 77 - 75 -
AMD Milan (Zen 3) 101 35 - 35 -
Intel Skylake-X 214 63 55 67 59
Intel Ice Lake 191 51 47 52 49
Intel Sapphire Rapids 175 39 39 38 39
Intel Granite Rapids 170 40 38 40 38
AMD Genoa (Zen 4) 91 34 37 34 38
AMD Turin (Zen 5) 80 29 22 28 23
C 502 to 669
BenchmarkSumSquareError main gcc gcc win win
_Opt AVX2 AVX512 AVX2 AVX512
Intel Haswell 79 37 - 38 -
AMD Milan (Zen 3) 47 24 - 25 -
Intel Skylake-X 78 32 26 34 24
Intel Ice Lake 86 43 23 43 22
Intel Sapphire Rapids 48 24 21 24 22
Intel Granite Rapids 47 23 21 23 21
AMD Genoa (Zen 4) 50 25 21 25 21
AMD Turin (Zen 5) 57 28 13 28 14
C 154 to 425
Testing:
LIBYUV_WIDTH=1279 LIBYUV_HEIGHT=719 out/Release/libyuv_unittest \
--gtest_filter='*Compare*:*Djb2*:*Hamming*:*SumSquare*:*Psnr*:*Ssim*'
With out/Release, out/Release_x86, out/rowwin, out/rowwin_x86 and
out/msan, with LIBYUV_DISABLE_AVX512BW and LIBYUV_DISABLE_AVX2, and
LIBYUV_WIDTH of 1, 31 to 33, 63 to 65, 95, 127, 129 and 200. Under
sde64 -skx for AVX512BW with out/Release and out/rowwin.
Built out/android_x86.
Bug: None
Change-Id: Ief197d546daca3db02ee1503d4649f4c755eb189
Reviewed-on: https://chromium-review.googlesource.com/c/libyuv/libyuv/+/8504717
Reviewed-by: richard winterton <rrwinterton@gmail.com>
Commit-Queue: Frank Barchard <fbarchard@google.com>
769 lines
24 KiB
C++
769 lines
24 KiB
C++
/*
|
|
* Copyright 2011 The LibYuv Project Authors. All rights reserved.
|
|
*
|
|
* Use of this source code is governed by a BSD-style license
|
|
* that can be found in the LICENSE file in the root of the source
|
|
* tree. An additional intellectual property rights grant can be found
|
|
* in the file PATENTS. All contributing project authors may
|
|
* be found in the AUTHORS file in the root of the source tree.
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
|
|
#include "../unit_test/unit_test.h"
|
|
#include "libyuv/compare.h"
|
|
#include "libyuv/cpu_id.h"
|
|
#include "libyuv/video_common.h"
|
|
|
|
#ifdef ENABLE_ROW_TESTS
|
|
#include "libyuv/compare_row.h" /* For HammingDistance_C */
|
|
#endif
|
|
|
|
namespace libyuv {
|
|
|
|
// hash seed of 5381 recommended.
|
|
static uint32_t ReferenceHashDjb2(const uint8_t* src,
|
|
uint64_t count,
|
|
uint32_t seed) {
|
|
uint32_t hash = seed;
|
|
if (count > 0) {
|
|
do {
|
|
hash = hash * 33 + *src++;
|
|
} while (--count);
|
|
}
|
|
return hash;
|
|
}
|
|
|
|
TEST_F(LibYUVCompareTest, Djb2_Test) {
|
|
const int kMaxTest = benchmark_width_ * benchmark_height_;
|
|
align_buffer_page_end(src_a, kMaxTest);
|
|
align_buffer_page_end(src_b, kMaxTest);
|
|
|
|
const char* fox =
|
|
"The quick brown fox jumps over the lazy dog"
|
|
" and feels as if he were in the seventh heaven of typography"
|
|
" together with Hermann Zapf";
|
|
uint32_t foxhash = HashDjb2(reinterpret_cast<const uint8_t*>(fox), 131, 5381);
|
|
const uint32_t kExpectedFoxHash = 2611006483u;
|
|
ASSERT_EQ(kExpectedFoxHash, foxhash);
|
|
|
|
for (int i = 0; i < kMaxTest; ++i) {
|
|
src_a[i] = (fastrand() & 0xff);
|
|
src_b[i] = (fastrand() & 0xff);
|
|
}
|
|
// Compare different buffers. Expect hash is different.
|
|
uint32_t h1 = HashDjb2(src_a, kMaxTest, 5381);
|
|
uint32_t h2 = HashDjb2(src_b, kMaxTest, 5381);
|
|
ASSERT_NE(h1, h2);
|
|
|
|
// Make last half same. Expect hash is different.
|
|
memcpy(src_a + kMaxTest / 2, src_b + kMaxTest / 2, kMaxTest / 2);
|
|
h1 = HashDjb2(src_a, kMaxTest, 5381);
|
|
h2 = HashDjb2(src_b, kMaxTest, 5381);
|
|
ASSERT_NE(h1, h2);
|
|
|
|
// Make first half same. Expect hash is different.
|
|
memcpy(src_a + kMaxTest / 2, src_a, kMaxTest / 2);
|
|
memcpy(src_b + kMaxTest / 2, src_b, kMaxTest / 2);
|
|
memcpy(src_a, src_b, kMaxTest / 2);
|
|
h1 = HashDjb2(src_a, kMaxTest, 5381);
|
|
h2 = HashDjb2(src_b, kMaxTest, 5381);
|
|
ASSERT_NE(h1, h2);
|
|
|
|
// Make same. Expect hash is same.
|
|
memcpy(src_a, src_b, kMaxTest);
|
|
h1 = HashDjb2(src_a, kMaxTest, 5381);
|
|
h2 = HashDjb2(src_b, kMaxTest, 5381);
|
|
ASSERT_EQ(h1, h2);
|
|
|
|
// Mask seed different. Expect hash is different.
|
|
memcpy(src_a, src_b, kMaxTest);
|
|
h1 = HashDjb2(src_a, kMaxTest, 5381);
|
|
h2 = HashDjb2(src_b, kMaxTest, 1234);
|
|
ASSERT_NE(h1, h2);
|
|
|
|
// Make one byte different in middle. Expect hash is different.
|
|
memcpy(src_a, src_b, kMaxTest);
|
|
++src_b[kMaxTest / 2];
|
|
h1 = HashDjb2(src_a, kMaxTest, 5381);
|
|
h2 = HashDjb2(src_b, kMaxTest, 5381);
|
|
ASSERT_NE(h1, h2);
|
|
|
|
// Make first byte different. Expect hash is different.
|
|
memcpy(src_a, src_b, kMaxTest);
|
|
++src_b[0];
|
|
h1 = HashDjb2(src_a, kMaxTest, 5381);
|
|
h2 = HashDjb2(src_b, kMaxTest, 5381);
|
|
ASSERT_NE(h1, h2);
|
|
|
|
// Make last byte different. Expect hash is different.
|
|
memcpy(src_a, src_b, kMaxTest);
|
|
++src_b[kMaxTest - 1];
|
|
h1 = HashDjb2(src_a, kMaxTest, 5381);
|
|
h2 = HashDjb2(src_b, kMaxTest, 5381);
|
|
ASSERT_NE(h1, h2);
|
|
|
|
// Make a zeros. Test different lengths. Expect hash is different.
|
|
memset(src_a, 0, kMaxTest);
|
|
h1 = HashDjb2(src_a, kMaxTest, 5381);
|
|
h2 = HashDjb2(src_a, kMaxTest / 2, 5381);
|
|
ASSERT_NE(h1, h2);
|
|
|
|
// Make a zeros and seed of zero. Test different lengths. Expect hash is same.
|
|
memset(src_a, 0, kMaxTest);
|
|
h1 = HashDjb2(src_a, kMaxTest, 0);
|
|
h2 = HashDjb2(src_a, kMaxTest / 2, 0);
|
|
ASSERT_EQ(h1, h2);
|
|
|
|
free_aligned_buffer_page_end(src_a);
|
|
free_aligned_buffer_page_end(src_b);
|
|
}
|
|
|
|
TEST_F(LibYUVCompareTest, BenchmarkDjb2_Opt) {
|
|
const int kMaxTest = benchmark_width_ * benchmark_height_;
|
|
align_buffer_page_end(src_a, kMaxTest);
|
|
|
|
for (int i = 0; i < kMaxTest; ++i) {
|
|
src_a[i] = i;
|
|
}
|
|
uint32_t h2 = ReferenceHashDjb2(src_a, kMaxTest, 5381);
|
|
uint32_t h1;
|
|
for (int i = 0; i < benchmark_iterations_; ++i) {
|
|
h1 = HashDjb2(src_a, kMaxTest, 5381);
|
|
}
|
|
ASSERT_EQ(h1, h2);
|
|
free_aligned_buffer_page_end(src_a);
|
|
}
|
|
|
|
TEST_F(LibYUVCompareTest, BenchmarkDjb2_Unaligned) {
|
|
const int kMaxTest = benchmark_width_ * benchmark_height_;
|
|
align_buffer_page_end(src_a, kMaxTest + 1);
|
|
for (int i = 0; i < kMaxTest; ++i) {
|
|
src_a[i + 1] = i;
|
|
}
|
|
uint32_t h2 = ReferenceHashDjb2(src_a + 1, kMaxTest, 5381);
|
|
uint32_t h1;
|
|
for (int i = 0; i < benchmark_iterations_; ++i) {
|
|
h1 = HashDjb2(src_a + 1, kMaxTest, 5381);
|
|
}
|
|
ASSERT_EQ(h1, h2);
|
|
free_aligned_buffer_page_end(src_a);
|
|
}
|
|
|
|
TEST_F(LibYUVCompareTest, BenchmarkARGBDetect_Opt) {
|
|
uint32_t fourcc;
|
|
const int kMaxTest = benchmark_width_ * benchmark_height_ * 4;
|
|
align_buffer_page_end(src_a, kMaxTest);
|
|
for (int i = 0; i < kMaxTest; ++i) {
|
|
src_a[i] = 255;
|
|
}
|
|
|
|
src_a[0] = 0;
|
|
fourcc = ARGBDetect(src_a, benchmark_width_ * 4, benchmark_width_,
|
|
benchmark_height_);
|
|
ASSERT_EQ(static_cast<uint32_t>(libyuv::FOURCC_BGRA), fourcc);
|
|
src_a[0] = 255;
|
|
src_a[3] = 0;
|
|
fourcc = ARGBDetect(src_a, benchmark_width_ * 4, benchmark_width_,
|
|
benchmark_height_);
|
|
ASSERT_EQ(static_cast<uint32_t>(libyuv::FOURCC_ARGB), fourcc);
|
|
src_a[3] = 255;
|
|
|
|
for (int i = 0; i < benchmark_iterations_; ++i) {
|
|
fourcc = ARGBDetect(src_a, benchmark_width_ * 4, benchmark_width_,
|
|
benchmark_height_);
|
|
}
|
|
ASSERT_EQ(0u, fourcc);
|
|
|
|
free_aligned_buffer_page_end(src_a);
|
|
}
|
|
|
|
TEST_F(LibYUVCompareTest, BenchmarkARGBDetect_Unaligned) {
|
|
uint32_t fourcc;
|
|
const int kMaxTest = benchmark_width_ * benchmark_height_ * 4 + 1;
|
|
align_buffer_page_end(src_a, kMaxTest);
|
|
for (int i = 1; i < kMaxTest; ++i) {
|
|
src_a[i] = 255;
|
|
}
|
|
|
|
src_a[0 + 1] = 0;
|
|
fourcc = ARGBDetect(src_a + 1, benchmark_width_ * 4, benchmark_width_,
|
|
benchmark_height_);
|
|
ASSERT_EQ(static_cast<uint32_t>(libyuv::FOURCC_BGRA), fourcc);
|
|
src_a[0 + 1] = 255;
|
|
src_a[3 + 1] = 0;
|
|
fourcc = ARGBDetect(src_a + 1, benchmark_width_ * 4, benchmark_width_,
|
|
benchmark_height_);
|
|
ASSERT_EQ(static_cast<uint32_t>(libyuv::FOURCC_ARGB), fourcc);
|
|
src_a[3 + 1] = 255;
|
|
|
|
for (int i = 0; i < benchmark_iterations_; ++i) {
|
|
fourcc = ARGBDetect(src_a + 1, benchmark_width_ * 4, benchmark_width_,
|
|
benchmark_height_);
|
|
}
|
|
ASSERT_EQ(0u, fourcc);
|
|
|
|
free_aligned_buffer_page_end(src_a);
|
|
}
|
|
|
|
#ifdef ENABLE_ROW_TESTS
|
|
TEST_F(LibYUVCompareTest, BenchmarkHammingDistance_Opt) {
|
|
const int kMaxWidth = 4096 * 3;
|
|
align_buffer_page_end(src_a, kMaxWidth);
|
|
align_buffer_page_end(src_b, kMaxWidth);
|
|
memset(src_a, 0, kMaxWidth);
|
|
memset(src_b, 0, kMaxWidth);
|
|
|
|
// Test known value
|
|
memcpy(src_a, "test0123test4567", 16);
|
|
memcpy(src_b, "tick0123tock4567", 16);
|
|
uint32_t h1 = HammingDistance_C(src_a, src_b, 16);
|
|
ASSERT_EQ(16u, h1);
|
|
|
|
// Test C vs OPT on random buffer
|
|
MemRandomize(src_a, kMaxWidth);
|
|
MemRandomize(src_b, kMaxWidth);
|
|
|
|
uint32_t h0 = HammingDistance_C(src_a, src_b, kMaxWidth);
|
|
|
|
int count =
|
|
benchmark_iterations_ *
|
|
((benchmark_width_ * benchmark_height_ + kMaxWidth - 1) / kMaxWidth);
|
|
for (int i = 0; i < count; ++i) {
|
|
#if defined(HAS_HAMMINGDISTANCE_NEON)
|
|
h1 = HammingDistance_NEON(src_a, src_b, kMaxWidth);
|
|
#elif defined(HAS_HAMMINGDISTANCE_AVX2)
|
|
int has_avx2 = TestCpuFlag(kCpuHasAVX2);
|
|
if (has_avx2) {
|
|
h1 = HammingDistance_AVX2(src_a, src_b, kMaxWidth);
|
|
} else {
|
|
int has_sse42 = TestCpuFlag(kCpuHasSSE42);
|
|
if (has_sse42) {
|
|
h1 = HammingDistance_SSE42(src_a, src_b, kMaxWidth);
|
|
} else {
|
|
int has_ssse3 = TestCpuFlag(kCpuHasSSSE3);
|
|
if (has_ssse3) {
|
|
h1 = HammingDistance_SSSE3(src_a, src_b, kMaxWidth);
|
|
} else {
|
|
h1 = HammingDistance_C(src_a, src_b, kMaxWidth);
|
|
}
|
|
}
|
|
}
|
|
#elif defined(HAS_HAMMINGDISTANCE_SSE42)
|
|
int has_sse42 = TestCpuFlag(kCpuHasSSE42);
|
|
if (has_sse42) {
|
|
h1 = HammingDistance_SSE42(src_a, src_b, kMaxWidth);
|
|
} else {
|
|
h1 = HammingDistance_C(src_a, src_b, kMaxWidth);
|
|
}
|
|
#else
|
|
h1 = HammingDistance_C(src_a, src_b, kMaxWidth);
|
|
#endif
|
|
}
|
|
ASSERT_EQ(h0, h1);
|
|
|
|
free_aligned_buffer_page_end(src_a);
|
|
free_aligned_buffer_page_end(src_b);
|
|
}
|
|
|
|
TEST_F(LibYUVCompareTest, BenchmarkHammingDistance_C) {
|
|
const int kMaxWidth = 4096 * 3;
|
|
align_buffer_page_end(src_a, kMaxWidth);
|
|
align_buffer_page_end(src_b, kMaxWidth);
|
|
memset(src_a, 0, kMaxWidth);
|
|
memset(src_b, 0, kMaxWidth);
|
|
|
|
// Test known value
|
|
memcpy(src_a, "test0123test4567", 16);
|
|
memcpy(src_b, "tick0123tock4567", 16);
|
|
uint32_t h1 = HammingDistance_C(src_a, src_b, 16);
|
|
ASSERT_EQ(16u, h1);
|
|
|
|
// Test C vs OPT on random buffer
|
|
MemRandomize(src_a, kMaxWidth);
|
|
MemRandomize(src_b, kMaxWidth);
|
|
|
|
uint32_t h0 = HammingDistance_C(src_a, src_b, kMaxWidth);
|
|
|
|
int count =
|
|
benchmark_iterations_ *
|
|
((benchmark_width_ * benchmark_height_ + kMaxWidth - 1) / kMaxWidth);
|
|
for (int i = 0; i < count; ++i) {
|
|
h1 = HammingDistance_C(src_a, src_b, kMaxWidth);
|
|
}
|
|
|
|
ASSERT_EQ(h0, h1);
|
|
|
|
free_aligned_buffer_page_end(src_a);
|
|
free_aligned_buffer_page_end(src_b);
|
|
}
|
|
|
|
TEST_F(LibYUVCompareTest, BenchmarkHammingDistance) {
|
|
const int kMaxWidth = 4096 * 3;
|
|
align_buffer_page_end(src_a, kMaxWidth);
|
|
align_buffer_page_end(src_b, kMaxWidth);
|
|
memset(src_a, 0, kMaxWidth);
|
|
memset(src_b, 0, kMaxWidth);
|
|
|
|
memcpy(src_a, "test0123test4567", 16);
|
|
memcpy(src_b, "tick0123tock4567", 16);
|
|
uint64_t h1 = ComputeHammingDistance(src_a, src_b, 16);
|
|
ASSERT_EQ(16u, h1);
|
|
|
|
// Test C vs OPT on random buffer
|
|
MemRandomize(src_a, kMaxWidth);
|
|
MemRandomize(src_b, kMaxWidth);
|
|
|
|
uint32_t h0 = HammingDistance_C(src_a, src_b, kMaxWidth);
|
|
|
|
int count =
|
|
benchmark_iterations_ *
|
|
((benchmark_width_ * benchmark_height_ + kMaxWidth - 1) / kMaxWidth);
|
|
for (int i = 0; i < count; ++i) {
|
|
h1 = ComputeHammingDistance(src_a, src_b, kMaxWidth);
|
|
}
|
|
|
|
ASSERT_EQ(h0, h1);
|
|
|
|
free_aligned_buffer_page_end(src_a);
|
|
free_aligned_buffer_page_end(src_b);
|
|
}
|
|
|
|
// Tests low levels match reference C for specified size.
|
|
// The opt implementations have size limitations
|
|
// For NEON the counters are 16 bit so the shorts overflow after 65536 bytes.
|
|
// So doing one less iteration of the loop is the maximum.
|
|
#if defined(HAS_HAMMINGDISTANCE_NEON)
|
|
static const int kMaxOptCount = 65536 - 32; // 65504
|
|
#else
|
|
static const int kMaxOptCount = (1 << (32 - 3)) - 64; // 536870848
|
|
#endif
|
|
|
|
TEST_F(LibYUVCompareTest, TestHammingDistance_Opt) {
|
|
uint32_t h1 = 0;
|
|
const int kMaxWidth = (benchmark_width_ * benchmark_height_ + 63) & ~63;
|
|
align_buffer_page_end(src_a, kMaxWidth);
|
|
align_buffer_page_end(src_b, kMaxWidth);
|
|
memset(src_a, 255u, kMaxWidth);
|
|
memset(src_b, 0u, kMaxWidth);
|
|
|
|
uint64_t h0 = ComputeHammingDistance(src_a, src_b, kMaxWidth);
|
|
ASSERT_EQ(kMaxWidth * 8ULL, h0);
|
|
|
|
for (int i = 0; i < benchmark_iterations_; ++i) {
|
|
#if defined(HAS_HAMMINGDISTANCE_NEON)
|
|
h1 = HammingDistance_NEON(src_a, src_b, kMaxWidth);
|
|
#elif defined(HAS_HAMMINGDISTANCE_AVX2)
|
|
int has_avx2 = TestCpuFlag(kCpuHasAVX2);
|
|
if (has_avx2) {
|
|
h1 = HammingDistance_AVX2(src_a, src_b, kMaxWidth);
|
|
} else {
|
|
int has_sse42 = TestCpuFlag(kCpuHasSSE42);
|
|
if (has_sse42) {
|
|
h1 = HammingDistance_SSE42(src_a, src_b, kMaxWidth);
|
|
} else {
|
|
int has_ssse3 = TestCpuFlag(kCpuHasSSSE3);
|
|
if (has_ssse3) {
|
|
h1 = HammingDistance_SSSE3(src_a, src_b, kMaxWidth);
|
|
} else {
|
|
h1 = HammingDistance_C(src_a, src_b, kMaxWidth);
|
|
}
|
|
}
|
|
}
|
|
#elif defined(HAS_HAMMINGDISTANCE_SSE42)
|
|
int has_sse42 = TestCpuFlag(kCpuHasSSE42);
|
|
if (has_sse42) {
|
|
h1 = HammingDistance_SSE42(src_a, src_b, kMaxWidth);
|
|
} else {
|
|
h1 = HammingDistance_C(src_a, src_b, kMaxWidth);
|
|
}
|
|
#else
|
|
h1 = HammingDistance_C(src_a, src_b, kMaxWidth);
|
|
#endif
|
|
}
|
|
|
|
// A large count will cause the low level to potentially overflow so the
|
|
// result can not be expected to be correct.
|
|
// TODO(fbarchard): Consider expecting the low 16 bits to match.
|
|
if (kMaxWidth <= kMaxOptCount) {
|
|
ASSERT_EQ(kMaxWidth * 8U, h1);
|
|
} else {
|
|
if (kMaxWidth * 8ULL != static_cast<uint64_t>(h1)) {
|
|
printf(
|
|
"warning - HammingDistance_Opt %u does not match %llu "
|
|
"but length of %u is longer than guaranteed.\n",
|
|
h1, kMaxWidth * 8ULL, kMaxWidth);
|
|
} else {
|
|
printf(
|
|
"warning - HammingDistance_Opt %u matches but length of %u "
|
|
"is longer than guaranteed.\n",
|
|
h1, kMaxWidth);
|
|
}
|
|
}
|
|
|
|
free_aligned_buffer_page_end(src_a);
|
|
free_aligned_buffer_page_end(src_b);
|
|
}
|
|
#endif // ENABLE_ROW_TESTS
|
|
|
|
TEST_F(LibYUVCompareTest, TestHammingDistance) {
|
|
align_buffer_page_end(src_a, benchmark_width_ * benchmark_height_);
|
|
align_buffer_page_end(src_b, benchmark_width_ * benchmark_height_);
|
|
memset(src_a, 255u, benchmark_width_ * benchmark_height_);
|
|
memset(src_b, 0, benchmark_width_ * benchmark_height_);
|
|
|
|
uint64_t h1 = 0;
|
|
for (int i = 0; i < benchmark_iterations_; ++i) {
|
|
h1 = ComputeHammingDistance(src_a, src_b,
|
|
benchmark_width_ * benchmark_height_);
|
|
}
|
|
ASSERT_EQ(benchmark_width_ * benchmark_height_ * 8ULL, h1);
|
|
|
|
free_aligned_buffer_page_end(src_a);
|
|
free_aligned_buffer_page_end(src_b);
|
|
}
|
|
|
|
TEST_F(LibYUVCompareTest, BenchmarkSumSquareError_Opt) {
|
|
const int kMaxWidth = 4096 * 3;
|
|
align_buffer_page_end(src_a, kMaxWidth);
|
|
align_buffer_page_end(src_b, kMaxWidth);
|
|
memset(src_a, 0, kMaxWidth);
|
|
memset(src_b, 0, kMaxWidth);
|
|
|
|
memcpy(src_a, "test0123test4567", 16);
|
|
memcpy(src_b, "tick0123tock4567", 16);
|
|
uint64_t h1 = ComputeSumSquareError(src_a, src_b, 16);
|
|
ASSERT_EQ(790u, h1);
|
|
|
|
for (int i = 0; i < kMaxWidth; ++i) {
|
|
src_a[i] = i;
|
|
src_b[i] = i;
|
|
}
|
|
memset(src_a, 0, kMaxWidth);
|
|
memset(src_b, 0, kMaxWidth);
|
|
|
|
int count =
|
|
benchmark_iterations_ *
|
|
((benchmark_width_ * benchmark_height_ + kMaxWidth - 1) / kMaxWidth);
|
|
for (int i = 0; i < count; ++i) {
|
|
h1 = ComputeSumSquareError(src_a, src_b, kMaxWidth);
|
|
}
|
|
|
|
ASSERT_EQ(0u, h1);
|
|
|
|
free_aligned_buffer_page_end(src_a);
|
|
free_aligned_buffer_page_end(src_b);
|
|
}
|
|
|
|
TEST_F(LibYUVCompareTest, SumSquareError) {
|
|
const int kMaxWidth = 4096 * 3;
|
|
align_buffer_page_end(src_a, kMaxWidth);
|
|
align_buffer_page_end(src_b, kMaxWidth);
|
|
memset(src_a, 0, kMaxWidth);
|
|
memset(src_b, 0, kMaxWidth);
|
|
|
|
uint64_t err;
|
|
err = ComputeSumSquareError(src_a, src_b, kMaxWidth);
|
|
|
|
ASSERT_EQ(0u, err);
|
|
|
|
memset(src_a, 1, kMaxWidth);
|
|
err = ComputeSumSquareError(src_a, src_b, kMaxWidth);
|
|
|
|
ASSERT_EQ(static_cast<int>(err), kMaxWidth);
|
|
|
|
memset(src_a, 190, kMaxWidth);
|
|
memset(src_b, 193, kMaxWidth);
|
|
err = ComputeSumSquareError(src_a, src_b, kMaxWidth);
|
|
|
|
ASSERT_EQ(static_cast<int>(err), kMaxWidth * 3 * 3);
|
|
|
|
for (int i = 0; i < kMaxWidth; ++i) {
|
|
src_a[i] = (fastrand() & 0xff);
|
|
src_b[i] = (fastrand() & 0xff);
|
|
}
|
|
|
|
MaskCpuFlags(disable_cpu_flags_);
|
|
uint64_t c_err = ComputeSumSquareError(src_a, src_b, kMaxWidth);
|
|
|
|
MaskCpuFlags(benchmark_cpu_info_);
|
|
uint64_t opt_err = ComputeSumSquareError(src_a, src_b, kMaxWidth);
|
|
|
|
ASSERT_EQ(c_err, opt_err);
|
|
|
|
free_aligned_buffer_page_end(src_a);
|
|
free_aligned_buffer_page_end(src_b);
|
|
}
|
|
|
|
// Test every count, aligned and unaligned, ending at the end of a page.
|
|
TEST_F(LibYUVCompareTest, TestCompareAnyCount) {
|
|
const int kMaxCount = 64 * 5 + 1;
|
|
align_buffer_page_end(src_a, kMaxCount + 1);
|
|
align_buffer_page_end(src_b, kMaxCount + 1);
|
|
MemRandomize(src_a, kMaxCount + 1);
|
|
MemRandomize(src_b, kMaxCount + 1);
|
|
|
|
for (int count = 0; count <= kMaxCount; ++count) {
|
|
for (int offset = 0; offset < 2; ++offset) {
|
|
const uint8_t* a = src_a + kMaxCount + 1 - count - offset;
|
|
const uint8_t* b = src_b + kMaxCount + 1 - count - offset;
|
|
MaskCpuFlags(disable_cpu_flags_);
|
|
uint32_t c_hash = HashDjb2(a, count, 5381);
|
|
uint64_t c_diff = ComputeHammingDistance(a, b, count);
|
|
uint64_t c_err = ComputeSumSquareError(a, b, count);
|
|
MaskCpuFlags(benchmark_cpu_info_);
|
|
uint32_t opt_hash = HashDjb2(a, count, 5381);
|
|
uint64_t opt_diff = ComputeHammingDistance(a, b, count);
|
|
uint64_t opt_err = ComputeSumSquareError(a, b, count);
|
|
EXPECT_EQ(c_hash, opt_hash) << "count " << count << " offset " << offset;
|
|
EXPECT_EQ(c_diff, opt_diff) << "count " << count << " offset " << offset;
|
|
EXPECT_EQ(c_err, opt_err) << "count " << count << " offset " << offset;
|
|
}
|
|
}
|
|
|
|
free_aligned_buffer_page_end(src_a);
|
|
free_aligned_buffer_page_end(src_b);
|
|
}
|
|
|
|
TEST_F(LibYUVCompareTest, BenchmarkPsnr_Opt) {
|
|
align_buffer_page_end(src_a, benchmark_width_ * benchmark_height_);
|
|
align_buffer_page_end(src_b, benchmark_width_ * benchmark_height_);
|
|
for (int i = 0; i < benchmark_width_ * benchmark_height_; ++i) {
|
|
src_a[i] = i;
|
|
src_b[i] = i;
|
|
}
|
|
|
|
MaskCpuFlags(benchmark_cpu_info_);
|
|
|
|
double opt_time = get_time();
|
|
for (int i = 0; i < benchmark_iterations_; ++i) {
|
|
CalcFramePsnr(src_a, benchmark_width_, src_b, benchmark_width_,
|
|
benchmark_width_, benchmark_height_);
|
|
}
|
|
|
|
opt_time = (get_time() - opt_time) / benchmark_iterations_;
|
|
printf("BenchmarkPsnr_Opt - %8.2f us opt\n", opt_time * 1e6);
|
|
|
|
ASSERT_EQ(0, 0);
|
|
|
|
free_aligned_buffer_page_end(src_a);
|
|
free_aligned_buffer_page_end(src_b);
|
|
}
|
|
|
|
TEST_F(LibYUVCompareTest, BenchmarkPsnr_Unaligned) {
|
|
align_buffer_page_end(src_a, benchmark_width_ * benchmark_height_ + 1);
|
|
align_buffer_page_end(src_b, benchmark_width_ * benchmark_height_);
|
|
for (int i = 0; i < benchmark_width_ * benchmark_height_; ++i) {
|
|
src_a[i + 1] = i;
|
|
src_b[i] = i;
|
|
}
|
|
|
|
MaskCpuFlags(benchmark_cpu_info_);
|
|
|
|
double opt_time = get_time();
|
|
for (int i = 0; i < benchmark_iterations_; ++i) {
|
|
CalcFramePsnr(src_a + 1, benchmark_width_, src_b, benchmark_width_,
|
|
benchmark_width_, benchmark_height_);
|
|
}
|
|
|
|
opt_time = (get_time() - opt_time) / benchmark_iterations_;
|
|
printf("BenchmarkPsnr_Opt - %8.2f us opt\n", opt_time * 1e6);
|
|
|
|
ASSERT_EQ(0, 0);
|
|
|
|
free_aligned_buffer_page_end(src_a);
|
|
free_aligned_buffer_page_end(src_b);
|
|
}
|
|
|
|
TEST_F(LibYUVCompareTest, Psnr) {
|
|
const int kSrcWidth = benchmark_width_;
|
|
const int kSrcHeight = benchmark_height_;
|
|
const int b = 128;
|
|
const int kSrcPlaneSize = (kSrcWidth + b * 2) * (kSrcHeight + b * 2);
|
|
const int kSrcStride = 2 * b + kSrcWidth;
|
|
align_buffer_page_end(src_a, kSrcPlaneSize);
|
|
align_buffer_page_end(src_b, kSrcPlaneSize);
|
|
memset(src_a, 0, kSrcPlaneSize);
|
|
memset(src_b, 0, kSrcPlaneSize);
|
|
|
|
double err;
|
|
err = CalcFramePsnr(src_a + kSrcStride * b + b, kSrcStride,
|
|
src_b + kSrcStride * b + b, kSrcStride, kSrcWidth,
|
|
kSrcHeight);
|
|
|
|
ASSERT_EQ(err, kMaxPsnr);
|
|
|
|
memset(src_a, 255, kSrcPlaneSize);
|
|
|
|
err = CalcFramePsnr(src_a + kSrcStride * b + b, kSrcStride,
|
|
src_b + kSrcStride * b + b, kSrcStride, kSrcWidth,
|
|
kSrcHeight);
|
|
|
|
ASSERT_EQ(err, 0.0);
|
|
|
|
memset(src_a, 1, kSrcPlaneSize);
|
|
|
|
err = CalcFramePsnr(src_a + kSrcStride * b + b, kSrcStride,
|
|
src_b + kSrcStride * b + b, kSrcStride, kSrcWidth,
|
|
kSrcHeight);
|
|
|
|
ASSERT_GT(err, 48.0);
|
|
ASSERT_LT(err, 49.0);
|
|
|
|
for (int i = 0; i < kSrcPlaneSize; ++i) {
|
|
src_a[i] = i;
|
|
}
|
|
|
|
err = CalcFramePsnr(src_a + kSrcStride * b + b, kSrcStride,
|
|
src_b + kSrcStride * b + b, kSrcStride, kSrcWidth,
|
|
kSrcHeight);
|
|
|
|
ASSERT_GT(err, 2.0);
|
|
if (kSrcWidth * kSrcHeight >= 256) {
|
|
ASSERT_LT(err, 6.0);
|
|
}
|
|
|
|
memset(src_a, 0, kSrcPlaneSize);
|
|
memset(src_b, 0, kSrcPlaneSize);
|
|
|
|
for (int i = b; i < (kSrcHeight + b); ++i) {
|
|
for (int j = b; j < (kSrcWidth + b); ++j) {
|
|
src_a[(i * kSrcStride) + j] = (fastrand() & 0xff);
|
|
src_b[(i * kSrcStride) + j] = (fastrand() & 0xff);
|
|
}
|
|
}
|
|
|
|
MaskCpuFlags(disable_cpu_flags_);
|
|
double c_err, opt_err;
|
|
|
|
c_err = CalcFramePsnr(src_a + kSrcStride * b + b, kSrcStride,
|
|
src_b + kSrcStride * b + b, kSrcStride, kSrcWidth,
|
|
kSrcHeight);
|
|
|
|
MaskCpuFlags(benchmark_cpu_info_);
|
|
|
|
opt_err = CalcFramePsnr(src_a + kSrcStride * b + b, kSrcStride,
|
|
src_b + kSrcStride * b + b, kSrcStride, kSrcWidth,
|
|
kSrcHeight);
|
|
|
|
ASSERT_EQ(opt_err, c_err);
|
|
|
|
free_aligned_buffer_page_end(src_a);
|
|
free_aligned_buffer_page_end(src_b);
|
|
}
|
|
|
|
TEST_F(LibYUVCompareTest, DISABLED_BenchmarkSsim_Opt) {
|
|
align_buffer_page_end(src_a, benchmark_width_ * benchmark_height_);
|
|
align_buffer_page_end(src_b, benchmark_width_ * benchmark_height_);
|
|
for (int i = 0; i < benchmark_width_ * benchmark_height_; ++i) {
|
|
src_a[i] = i;
|
|
src_b[i] = i;
|
|
}
|
|
|
|
MaskCpuFlags(benchmark_cpu_info_);
|
|
|
|
double opt_time = get_time();
|
|
for (int i = 0; i < benchmark_iterations_; ++i) {
|
|
CalcFrameSsim(src_a, benchmark_width_, src_b, benchmark_width_,
|
|
benchmark_width_, benchmark_height_);
|
|
}
|
|
|
|
opt_time = (get_time() - opt_time) / benchmark_iterations_;
|
|
printf("BenchmarkSsim_Opt - %8.2f us opt\n", opt_time * 1e6);
|
|
|
|
ASSERT_EQ(0, 0); // Pass if we get this far.
|
|
|
|
free_aligned_buffer_page_end(src_a);
|
|
free_aligned_buffer_page_end(src_b);
|
|
}
|
|
|
|
TEST_F(LibYUVCompareTest, Ssim) {
|
|
const int kSrcWidth = benchmark_width_;
|
|
const int kSrcHeight = benchmark_height_;
|
|
const int b = 128;
|
|
const int kSrcPlaneSize = (kSrcWidth + b * 2) * (kSrcHeight + b * 2);
|
|
const int kSrcStride = 2 * b + kSrcWidth;
|
|
align_buffer_page_end(src_a, kSrcPlaneSize);
|
|
align_buffer_page_end(src_b, kSrcPlaneSize);
|
|
memset(src_a, 0, kSrcPlaneSize);
|
|
memset(src_b, 0, kSrcPlaneSize);
|
|
|
|
if (kSrcWidth <= 8 || kSrcHeight <= 8) {
|
|
printf("warning - Ssim size too small. Testing function executes.\n");
|
|
}
|
|
|
|
double err;
|
|
err = CalcFrameSsim(src_a + kSrcStride * b + b, kSrcStride,
|
|
src_b + kSrcStride * b + b, kSrcStride, kSrcWidth,
|
|
kSrcHeight);
|
|
|
|
if (kSrcWidth > 8 && kSrcHeight > 8) {
|
|
ASSERT_EQ(err, 1.0);
|
|
}
|
|
|
|
memset(src_a, 255, kSrcPlaneSize);
|
|
|
|
err = CalcFrameSsim(src_a + kSrcStride * b + b, kSrcStride,
|
|
src_b + kSrcStride * b + b, kSrcStride, kSrcWidth,
|
|
kSrcHeight);
|
|
|
|
if (kSrcWidth > 8 && kSrcHeight > 8) {
|
|
ASSERT_LT(err, 0.0001);
|
|
}
|
|
|
|
memset(src_a, 1, kSrcPlaneSize);
|
|
|
|
err = CalcFrameSsim(src_a + kSrcStride * b + b, kSrcStride,
|
|
src_b + kSrcStride * b + b, kSrcStride, kSrcWidth,
|
|
kSrcHeight);
|
|
|
|
if (kSrcWidth > 8 && kSrcHeight > 8) {
|
|
ASSERT_GT(err, 0.0001);
|
|
ASSERT_LT(err, 0.9);
|
|
}
|
|
|
|
for (int i = 0; i < kSrcPlaneSize; ++i) {
|
|
src_a[i] = i;
|
|
}
|
|
|
|
err = CalcFrameSsim(src_a + kSrcStride * b + b, kSrcStride,
|
|
src_b + kSrcStride * b + b, kSrcStride, kSrcWidth,
|
|
kSrcHeight);
|
|
|
|
if (kSrcWidth > 8 && kSrcHeight > 8) {
|
|
ASSERT_GT(err, 0.0);
|
|
ASSERT_LT(err, 0.01);
|
|
}
|
|
|
|
for (int i = b; i < (kSrcHeight + b); ++i) {
|
|
for (int j = b; j < (kSrcWidth + b); ++j) {
|
|
src_a[(i * kSrcStride) + j] = (fastrand() & 0xff);
|
|
src_b[(i * kSrcStride) + j] = (fastrand() & 0xff);
|
|
}
|
|
}
|
|
|
|
MaskCpuFlags(disable_cpu_flags_);
|
|
double c_err, opt_err;
|
|
|
|
c_err = CalcFrameSsim(src_a + kSrcStride * b + b, kSrcStride,
|
|
src_b + kSrcStride * b + b, kSrcStride, kSrcWidth,
|
|
kSrcHeight);
|
|
|
|
MaskCpuFlags(benchmark_cpu_info_);
|
|
|
|
opt_err = CalcFrameSsim(src_a + kSrcStride * b + b, kSrcStride,
|
|
src_b + kSrcStride * b + b, kSrcStride, kSrcWidth,
|
|
kSrcHeight);
|
|
|
|
if (kSrcWidth > 8 && kSrcHeight > 8) {
|
|
ASSERT_EQ(opt_err, c_err);
|
|
}
|
|
|
|
free_aligned_buffer_page_end(src_a);
|
|
free_aligned_buffer_page_end(src_b);
|
|
}
|
|
|
|
} // namespace libyuv
|