diff options
Diffstat (limited to 'chromium/media/base/vector_math_unittest.cc')
-rw-r--r-- | chromium/media/base/vector_math_unittest.cc | 390 |
1 files changed, 242 insertions, 148 deletions
diff --git a/chromium/media/base/vector_math_unittest.cc b/chromium/media/base/vector_math_unittest.cc index 2c7740142cb..f8278ce1b5d 100644 --- a/chromium/media/base/vector_math_unittest.cc +++ b/chromium/media/base/vector_math_unittest.cc @@ -6,68 +6,50 @@ #define _USE_MATH_DEFINES #include <cmath> -#include "base/command_line.h" #include "base/cpu.h" #include "base/memory/aligned_memory.h" #include "base/memory/scoped_ptr.h" #include "base/strings/string_number_conversions.h" #include "base/strings/stringize_macros.h" -#include "base/time/time.h" #include "media/base/vector_math.h" #include "media/base/vector_math_testing.h" #include "testing/gtest/include/gtest/gtest.h" -using base::TimeTicks; using std::fill; -// Command line switch for runtime adjustment of benchmark iterations. -static const char kBenchmarkIterations[] = "vector-math-iterations"; -static const int kDefaultIterations = 10; +namespace media { // Default test values. static const float kScale = 0.5; static const float kInputFillValue = 1.0; static const float kOutputFillValue = 3.0; - -namespace media { +static const int kVectorSize = 8192; class VectorMathTest : public testing::Test { public: - static const int kVectorSize = 8192; VectorMathTest() { // Initialize input and output vectors. - input_vector.reset(static_cast<float*>(base::AlignedAlloc( + input_vector_.reset(static_cast<float*>(base::AlignedAlloc( sizeof(float) * kVectorSize, vector_math::kRequiredAlignment))); - output_vector.reset(static_cast<float*>(base::AlignedAlloc( + output_vector_.reset(static_cast<float*>(base::AlignedAlloc( sizeof(float) * kVectorSize, vector_math::kRequiredAlignment))); } void FillTestVectors(float input, float output) { // Setup input and output vectors. - fill(input_vector.get(), input_vector.get() + kVectorSize, input); - fill(output_vector.get(), output_vector.get() + kVectorSize, output); + fill(input_vector_.get(), input_vector_.get() + kVectorSize, input); + fill(output_vector_.get(), output_vector_.get() + kVectorSize, output); } void VerifyOutput(float value) { for (int i = 0; i < kVectorSize; ++i) - ASSERT_FLOAT_EQ(output_vector.get()[i], value); - } - - int BenchmarkIterations() { - int vector_math_iterations = kDefaultIterations; - std::string iterations( - CommandLine::ForCurrentProcess()->GetSwitchValueASCII( - kBenchmarkIterations)); - if (!iterations.empty()) - base::StringToInt(iterations, &vector_math_iterations); - return vector_math_iterations; + ASSERT_FLOAT_EQ(output_vector_.get()[i], value); } protected: - int benchmark_iterations; - scoped_ptr_malloc<float, base::ScopedPtrAlignedFree> input_vector; - scoped_ptr_malloc<float, base::ScopedPtrAlignedFree> output_vector; + scoped_ptr_malloc<float, base::ScopedPtrAlignedFree> input_vector_; + scoped_ptr_malloc<float, base::ScopedPtrAlignedFree> output_vector_; DISALLOW_COPY_AND_ASSIGN(VectorMathTest); }; @@ -80,7 +62,7 @@ TEST_F(VectorMathTest, FMAC) { SCOPED_TRACE("FMAC"); FillTestVectors(kInputFillValue, kOutputFillValue); vector_math::FMAC( - input_vector.get(), kScale, kVectorSize, output_vector.get()); + input_vector_.get(), kScale, kVectorSize, output_vector_.get()); VerifyOutput(kResult); } @@ -88,7 +70,7 @@ TEST_F(VectorMathTest, FMAC) { SCOPED_TRACE("FMAC_C"); FillTestVectors(kInputFillValue, kOutputFillValue); vector_math::FMAC_C( - input_vector.get(), kScale, kVectorSize, output_vector.get()); + input_vector_.get(), kScale, kVectorSize, output_vector_.get()); VerifyOutput(kResult); } @@ -98,7 +80,7 @@ TEST_F(VectorMathTest, FMAC) { SCOPED_TRACE("FMAC_SSE"); FillTestVectors(kInputFillValue, kOutputFillValue); vector_math::FMAC_SSE( - input_vector.get(), kScale, kVectorSize, output_vector.get()); + input_vector_.get(), kScale, kVectorSize, output_vector_.get()); VerifyOutput(kResult); } #endif @@ -108,7 +90,7 @@ TEST_F(VectorMathTest, FMAC) { SCOPED_TRACE("FMAC_NEON"); FillTestVectors(kInputFillValue, kOutputFillValue); vector_math::FMAC_NEON( - input_vector.get(), kScale, kVectorSize, output_vector.get()); + input_vector_.get(), kScale, kVectorSize, output_vector_.get()); VerifyOutput(kResult); } #endif @@ -122,7 +104,7 @@ TEST_F(VectorMathTest, FMUL) { SCOPED_TRACE("FMUL"); FillTestVectors(kInputFillValue, kOutputFillValue); vector_math::FMUL( - input_vector.get(), kScale, kVectorSize, output_vector.get()); + input_vector_.get(), kScale, kVectorSize, output_vector_.get()); VerifyOutput(kResult); } @@ -130,7 +112,7 @@ TEST_F(VectorMathTest, FMUL) { SCOPED_TRACE("FMUL_C"); FillTestVectors(kInputFillValue, kOutputFillValue); vector_math::FMUL_C( - input_vector.get(), kScale, kVectorSize, output_vector.get()); + input_vector_.get(), kScale, kVectorSize, output_vector_.get()); VerifyOutput(kResult); } @@ -140,7 +122,7 @@ TEST_F(VectorMathTest, FMUL) { SCOPED_TRACE("FMUL_SSE"); FillTestVectors(kInputFillValue, kOutputFillValue); vector_math::FMUL_SSE( - input_vector.get(), kScale, kVectorSize, output_vector.get()); + input_vector_.get(), kScale, kVectorSize, output_vector_.get()); VerifyOutput(kResult); } #endif @@ -150,142 +132,254 @@ TEST_F(VectorMathTest, FMUL) { SCOPED_TRACE("FMUL_NEON"); FillTestVectors(kInputFillValue, kOutputFillValue); vector_math::FMUL_NEON( - input_vector.get(), kScale, kVectorSize, output_vector.get()); + input_vector_.get(), kScale, kVectorSize, output_vector_.get()); VerifyOutput(kResult); } #endif } -// Define platform independent function name for FMACBenchmark* tests. -#if defined(ARCH_CPU_X86_FAMILY) -#define FMAC_FUNC FMAC_SSE -#elif defined(ARCH_CPU_ARM_FAMILY) && defined(USE_NEON) -#define FMAC_FUNC FMAC_NEON -#endif +namespace { -// Benchmark for each optimized vector_math::FMAC() method. Original benchmarks -// were run with --vector-fmac-iterations=200000. -TEST_F(VectorMathTest, FMACBenchmark) { - static const int kBenchmarkIterations = BenchmarkIterations(); - - printf("Benchmarking %d iterations:\n", kBenchmarkIterations); +class EWMATestScenario { + public: + EWMATestScenario(float initial_value, const float src[], int len, + float smoothing_factor) + : initial_value_(initial_value), + data_(static_cast<float*>( + len == 0 ? NULL : + base::AlignedAlloc(len * sizeof(float), + vector_math::kRequiredAlignment))), + data_len_(len), + smoothing_factor_(smoothing_factor), + expected_final_avg_(initial_value), + expected_max_(0.0f) { + if (data_len_ > 0) + memcpy(data_.get(), src, len * sizeof(float)); + } - // Benchmark FMAC_C(). - FillTestVectors(kInputFillValue, kOutputFillValue); - TimeTicks start = TimeTicks::HighResNow(); - for (int i = 0; i < kBenchmarkIterations; ++i) { - vector_math::FMAC_C( - input_vector.get(), kScale, kVectorSize, output_vector.get()); + // Copy constructor and assignment operator for ::testing::Values(...). + EWMATestScenario(const EWMATestScenario& other) { *this = other; } + EWMATestScenario& operator=(const EWMATestScenario& other) { + this->initial_value_ = other.initial_value_; + this->smoothing_factor_ = other.smoothing_factor_; + if (other.data_len_ == 0) { + this->data_.reset(); + } else { + this->data_.reset(static_cast<float*>( + base::AlignedAlloc(other.data_len_ * sizeof(float), + vector_math::kRequiredAlignment))); + memcpy(this->data_.get(), other.data_.get(), + other.data_len_ * sizeof(float)); + } + this->data_len_ = other.data_len_; + this->expected_final_avg_ = other.expected_final_avg_; + this->expected_max_ = other.expected_max_; + return *this; } - double total_time_c_ms = (TimeTicks::HighResNow() - start).InMillisecondsF(); - printf("FMAC_C took %.2fms.\n", total_time_c_ms); -#if defined(FMAC_FUNC) -#if defined(ARCH_CPU_X86_FAMILY) - ASSERT_TRUE(base::CPU().has_sse()); -#endif + EWMATestScenario ScaledBy(float scale) const { + EWMATestScenario result(*this); + float* p = result.data_.get(); + float* const p_end = p + result.data_len_; + for (; p < p_end; ++p) + *p *= scale; + return result; + } - // Benchmark FMAC_FUNC() with unaligned size. - ASSERT_NE((kVectorSize - 1) % (vector_math::kRequiredAlignment / - sizeof(float)), 0U); - FillTestVectors(kInputFillValue, kOutputFillValue); - start = TimeTicks::HighResNow(); - for (int j = 0; j < kBenchmarkIterations; ++j) { - vector_math::FMAC_FUNC( - input_vector.get(), kScale, kVectorSize - 1, output_vector.get()); + EWMATestScenario WithImpulse(float value, int offset) const { + EWMATestScenario result(*this); + result.data_.get()[offset] = value; + return result; } - double total_time_optimized_unaligned_ms = - (TimeTicks::HighResNow() - start).InMillisecondsF(); - printf(STRINGIZE(FMAC_FUNC) " (unaligned size) took %.2fms; which is %.2fx " - "faster than FMAC_C.\n", total_time_optimized_unaligned_ms, - total_time_c_ms / total_time_optimized_unaligned_ms); - - // Benchmark FMAC_FUNC() with aligned size. - ASSERT_EQ(kVectorSize % (vector_math::kRequiredAlignment / sizeof(float)), - 0U); - FillTestVectors(kInputFillValue, kOutputFillValue); - start = TimeTicks::HighResNow(); - for (int j = 0; j < kBenchmarkIterations; ++j) { - vector_math::FMAC_FUNC( - input_vector.get(), kScale, kVectorSize, output_vector.get()); + + EWMATestScenario HasExpectedResult(float final_avg_value, + float max_value) const { + EWMATestScenario result(*this); + result.expected_final_avg_ = final_avg_value; + result.expected_max_ = max_value; + return result; } - double total_time_optimized_aligned_ms = - (TimeTicks::HighResNow() - start).InMillisecondsF(); - printf(STRINGIZE(FMAC_FUNC) " (aligned) took %.2fms; which is %.2fx " - "faster than FMAC_C and %.2fx faster than " - STRINGIZE(FMAC_FUNC) " (unaligned).\n", - total_time_optimized_aligned_ms, - total_time_c_ms / total_time_optimized_aligned_ms, - total_time_optimized_unaligned_ms / total_time_optimized_aligned_ms); -#endif -} -#undef FMAC_FUNC + void RunTest() const { + { + SCOPED_TRACE("EWMAAndMaxPower"); + const std::pair<float, float>& result = vector_math::EWMAAndMaxPower( + initial_value_, data_.get(), data_len_, smoothing_factor_); + EXPECT_NEAR(expected_final_avg_, result.first, 0.0000001f); + EXPECT_NEAR(expected_max_, result.second, 0.0000001f); + } + + { + SCOPED_TRACE("EWMAAndMaxPower_C"); + const std::pair<float, float>& result = vector_math::EWMAAndMaxPower_C( + initial_value_, data_.get(), data_len_, smoothing_factor_); + EXPECT_NEAR(expected_final_avg_, result.first, 0.0000001f); + EXPECT_NEAR(expected_max_, result.second, 0.0000001f); + } -// Define platform independent function name for FMULBenchmark* tests. #if defined(ARCH_CPU_X86_FAMILY) -#define FMUL_FUNC FMUL_SSE -#elif defined(ARCH_CPU_ARM_FAMILY) && defined(USE_NEON) -#define FMUL_FUNC FMUL_NEON + { + ASSERT_TRUE(base::CPU().has_sse()); + SCOPED_TRACE("EWMAAndMaxPower_SSE"); + const std::pair<float, float>& result = vector_math::EWMAAndMaxPower_SSE( + initial_value_, data_.get(), data_len_, smoothing_factor_); + EXPECT_NEAR(expected_final_avg_, result.first, 0.0000001f); + EXPECT_NEAR(expected_max_, result.second, 0.0000001f); + } #endif -// Benchmark for each optimized vector_math::FMUL() method. Original benchmarks -// were run with --vector-math-iterations=200000. -TEST_F(VectorMathTest, FMULBenchmark) { - static const int kBenchmarkIterations = BenchmarkIterations(); +#if defined(ARCH_CPU_ARM_FAMILY) && defined(USE_NEON) + { + SCOPED_TRACE("EWMAAndMaxPower_NEON"); + const std::pair<float, float>& result = vector_math::EWMAAndMaxPower_NEON( + initial_value_, data_.get(), data_len_, smoothing_factor_); + EXPECT_NEAR(expected_final_avg_, result.first, 0.0000001f); + EXPECT_NEAR(expected_max_, result.second, 0.0000001f); + } +#endif + } - printf("Benchmarking %d iterations:\n", kBenchmarkIterations); + private: + float initial_value_; + scoped_ptr_malloc<float, base::ScopedPtrAlignedFree> data_; + int data_len_; + float smoothing_factor_; + float expected_final_avg_; + float expected_max_; +}; - // Benchmark FMUL_C(). - FillTestVectors(kInputFillValue, kOutputFillValue); - TimeTicks start = TimeTicks::HighResNow(); - for (int i = 0; i < kBenchmarkIterations; ++i) { - vector_math::FMUL_C( - input_vector.get(), kScale, kVectorSize, output_vector.get()); - } - double total_time_c_ms = (TimeTicks::HighResNow() - start).InMillisecondsF(); - printf("FMUL_C took %.2fms.\n", total_time_c_ms); +} // namespace -#if defined(FMUL_FUNC) -#if defined(ARCH_CPU_X86_FAMILY) - ASSERT_TRUE(base::CPU().has_sse()); -#endif +typedef testing::TestWithParam<EWMATestScenario> VectorMathEWMAAndMaxPowerTest; - // Benchmark FMUL_SSE() with unaligned size. - ASSERT_NE((kVectorSize - 1) % (vector_math::kRequiredAlignment / - sizeof(float)), 0U); - FillTestVectors(kInputFillValue, kOutputFillValue); - start = TimeTicks::HighResNow(); - for (int j = 0; j < kBenchmarkIterations; ++j) { - vector_math::FMUL_FUNC( - input_vector.get(), kScale, kVectorSize - 1, output_vector.get()); - } - double total_time_optimized_unaligned_ms = - (TimeTicks::HighResNow() - start).InMillisecondsF(); - printf(STRINGIZE(FMUL_FUNC) " (unaligned size) took %.2fms; which is %.2fx " - "faster than FMUL_C.\n", total_time_optimized_unaligned_ms, - total_time_c_ms / total_time_optimized_unaligned_ms); - - // Benchmark FMUL_SSE() with aligned size. - ASSERT_EQ(kVectorSize % (vector_math::kRequiredAlignment / sizeof(float)), - 0U); - FillTestVectors(kInputFillValue, kOutputFillValue); - start = TimeTicks::HighResNow(); - for (int j = 0; j < kBenchmarkIterations; ++j) { - vector_math::FMUL_FUNC( - input_vector.get(), kScale, kVectorSize, output_vector.get()); - } - double total_time_optimized_aligned_ms = - (TimeTicks::HighResNow() - start).InMillisecondsF(); - printf(STRINGIZE(FMUL_FUNC) " (aligned) took %.2fms; which is %.2fx " - "faster than FMUL_C and %.2fx faster than " - STRINGIZE(FMUL_FUNC) " (unaligned).\n", - total_time_optimized_aligned_ms, - total_time_c_ms / total_time_optimized_aligned_ms, - total_time_optimized_unaligned_ms / total_time_optimized_aligned_ms); -#endif +TEST_P(VectorMathEWMAAndMaxPowerTest, Correctness) { + GetParam().RunTest(); } -#undef FMUL_FUNC +static const float kZeros[] = { // 32 zeros + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +static const float kOnes[] = { // 32 ones + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 +}; + +static const float kCheckerboard[] = { // 32 alternating 0, 1 + 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, + 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1 +}; + +static const float kInverseCheckerboard[] = { // 32 alternating 1, 0 + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0 +}; + +INSTANTIATE_TEST_CASE_P( + Scenarios, VectorMathEWMAAndMaxPowerTest, + ::testing::Values( + // Zero-length input: Result should equal initial value. + EWMATestScenario(0.0f, NULL, 0, 0.0f).HasExpectedResult(0.0f, 0.0f), + EWMATestScenario(1.0f, NULL, 0, 0.0f).HasExpectedResult(1.0f, 0.0f), + + // Smoothing factor of zero: Samples have no effect on result. + EWMATestScenario(0.0f, kOnes, 32, 0.0f).HasExpectedResult(0.0f, 1.0f), + EWMATestScenario(1.0f, kZeros, 32, 0.0f).HasExpectedResult(1.0f, 0.0f), + + // Smothing factor of one: Result = last sample squared. + EWMATestScenario(0.0f, kCheckerboard, 32, 1.0f) + .ScaledBy(2.0f) + .HasExpectedResult(4.0f, 4.0f), + EWMATestScenario(1.0f, kInverseCheckerboard, 32, 1.0f) + .ScaledBy(2.0f) + .HasExpectedResult(0.0f, 4.0f), + + // Smoothing factor of 1/4, muted signal. + EWMATestScenario(1.0f, kZeros, 1, 0.25f) + .HasExpectedResult(powf(0.75, 1.0f), 0.0f), + EWMATestScenario(1.0f, kZeros, 2, 0.25f) + .HasExpectedResult(powf(0.75, 2.0f), 0.0f), + EWMATestScenario(1.0f, kZeros, 3, 0.25f) + .HasExpectedResult(powf(0.75, 3.0f), 0.0f), + EWMATestScenario(1.0f, kZeros, 12, 0.25f) + .HasExpectedResult(powf(0.75, 12.0f), 0.0f), + EWMATestScenario(1.0f, kZeros, 13, 0.25f) + .HasExpectedResult(powf(0.75, 13.0f), 0.0f), + EWMATestScenario(1.0f, kZeros, 14, 0.25f) + .HasExpectedResult(powf(0.75, 14.0f), 0.0f), + EWMATestScenario(1.0f, kZeros, 15, 0.25f) + .HasExpectedResult(powf(0.75, 15.0f), 0.0f), + + // Smoothing factor of 1/4, constant full-amplitude signal. + EWMATestScenario(0.0f, kOnes, 1, 0.25f).HasExpectedResult(0.25f, 1.0f), + EWMATestScenario(0.0f, kOnes, 2, 0.25f) + .HasExpectedResult(0.4375f, 1.0f), + EWMATestScenario(0.0f, kOnes, 3, 0.25f) + .HasExpectedResult(0.578125f, 1.0f), + EWMATestScenario(0.0f, kOnes, 12, 0.25f) + .HasExpectedResult(0.96832365f, 1.0f), + EWMATestScenario(0.0f, kOnes, 13, 0.25f) + .HasExpectedResult(0.97624274f, 1.0f), + EWMATestScenario(0.0f, kOnes, 14, 0.25f) + .HasExpectedResult(0.98218205f, 1.0f), + EWMATestScenario(0.0f, kOnes, 15, 0.25f) + .HasExpectedResult(0.98663654f, 1.0f), + + // Smoothing factor of 1/4, checkerboard signal. + EWMATestScenario(0.0f, kCheckerboard, 1, 0.25f) + .HasExpectedResult(0.0f, 0.0f), + EWMATestScenario(0.0f, kCheckerboard, 2, 0.25f) + .HasExpectedResult(0.25f, 1.0f), + EWMATestScenario(0.0f, kCheckerboard, 3, 0.25f) + .HasExpectedResult(0.1875f, 1.0f), + EWMATestScenario(0.0f, kCheckerboard, 12, 0.25f) + .HasExpectedResult(0.55332780f, 1.0f), + EWMATestScenario(0.0f, kCheckerboard, 13, 0.25f) + .HasExpectedResult(0.41499585f, 1.0f), + EWMATestScenario(0.0f, kCheckerboard, 14, 0.25f) + .HasExpectedResult(0.56124689f, 1.0f), + EWMATestScenario(0.0f, kCheckerboard, 15, 0.25f) + .HasExpectedResult(0.42093517f, 1.0f), + + // Smoothing factor of 1/4, inverse checkerboard signal. + EWMATestScenario(0.0f, kInverseCheckerboard, 1, 0.25f) + .HasExpectedResult(0.25f, 1.0f), + EWMATestScenario(0.0f, kInverseCheckerboard, 2, 0.25f) + .HasExpectedResult(0.1875f, 1.0f), + EWMATestScenario(0.0f, kInverseCheckerboard, 3, 0.25f) + .HasExpectedResult(0.390625f, 1.0f), + EWMATestScenario(0.0f, kInverseCheckerboard, 12, 0.25f) + .HasExpectedResult(0.41499585f, 1.0f), + EWMATestScenario(0.0f, kInverseCheckerboard, 13, 0.25f) + .HasExpectedResult(0.56124689f, 1.0f), + EWMATestScenario(0.0f, kInverseCheckerboard, 14, 0.25f) + .HasExpectedResult(0.42093517f, 1.0f), + EWMATestScenario(0.0f, kInverseCheckerboard, 15, 0.25f) + .HasExpectedResult(0.56570137f, 1.0f), + + // Smoothing factor of 1/4, impluse signal. + EWMATestScenario(0.0f, kZeros, 3, 0.25f) + .WithImpulse(2.0f, 0) + .HasExpectedResult(0.562500f, 4.0f), + EWMATestScenario(0.0f, kZeros, 3, 0.25f) + .WithImpulse(2.0f, 1) + .HasExpectedResult(0.75f, 4.0f), + EWMATestScenario(0.0f, kZeros, 3, 0.25f) + .WithImpulse(2.0f, 2) + .HasExpectedResult(1.0f, 4.0f), + EWMATestScenario(0.0f, kZeros, 32, 0.25f) + .WithImpulse(2.0f, 0) + .HasExpectedResult(0.00013394f, 4.0f), + EWMATestScenario(0.0f, kZeros, 32, 0.25f) + .WithImpulse(2.0f, 1) + .HasExpectedResult(0.00017858f, 4.0f), + EWMATestScenario(0.0f, kZeros, 32, 0.25f) + .WithImpulse(2.0f, 2) + .HasExpectedResult(0.00023811f, 4.0f), + EWMATestScenario(0.0f, kZeros, 32, 0.25f) + .WithImpulse(2.0f, 3) + .HasExpectedResult(0.00031748f, 4.0f) + )); } // namespace media |