| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 // MSVC++ requires this to be set before any other includes to get M_PI. | 5 // MSVC++ requires this to be set before any other includes to get M_PI. |
| 6 #define _USE_MATH_DEFINES | 6 #define _USE_MATH_DEFINES |
| 7 #include <cmath> | 7 #include <cmath> |
| 8 | 8 |
| 9 #include "base/bind.h" | 9 #include "base/bind.h" |
| 10 #include "base/bind_helpers.h" | 10 #include "base/bind_helpers.h" |
| 11 #include "base/command_line.h" | |
| 12 #include "base/memory/aligned_memory.h" | |
| 13 #include "base/memory/scoped_ptr.h" | 11 #include "base/memory/scoped_ptr.h" |
| 14 #include "base/memory/scoped_vector.h" | 12 #include "base/memory/scoped_vector.h" |
| 15 #include "base/string_number_conversions.h" | |
| 16 #include "media/base/audio_renderer_mixer.h" | 13 #include "media/base/audio_renderer_mixer.h" |
| 17 #include "media/base/audio_renderer_mixer_input.h" | 14 #include "media/base/audio_renderer_mixer_input.h" |
| 18 #include "media/base/fake_audio_render_callback.h" | 15 #include "media/base/fake_audio_render_callback.h" |
| 19 #include "media/base/mock_audio_renderer_sink.h" | 16 #include "media/base/mock_audio_renderer_sink.h" |
| 20 #include "testing/gmock/include/gmock/gmock.h" | 17 #include "testing/gmock/include/gmock/gmock.h" |
| 21 #include "testing/gtest/include/gtest/gtest.h" | 18 #include "testing/gtest/include/gtest/gtest.h" |
| 22 | 19 |
| 23 namespace media { | 20 namespace media { |
| 24 | 21 |
| 25 // Parameters which control the many input case tests. | 22 // Parameters which control the many input case tests. |
| 26 static const int kMixerInputs = 8; | 23 static const int kMixerInputs = 8; |
| 27 static const int kMixerCycles = 3; | 24 static const int kMixerCycles = 3; |
| 28 | 25 |
| 29 // Parameters used for testing. | 26 // Parameters used for testing. |
| 30 static const int kBitsPerChannel = 16; | 27 static const int kBitsPerChannel = 16; |
| 31 static const ChannelLayout kChannelLayout = CHANNEL_LAYOUT_STEREO; | 28 static const ChannelLayout kChannelLayout = CHANNEL_LAYOUT_STEREO; |
| 32 static const int kHighLatencyBufferSize = 8192; | 29 static const int kHighLatencyBufferSize = 8192; |
| 33 static const int kLowLatencyBufferSize = 256; | 30 static const int kLowLatencyBufferSize = 256; |
| 34 static const int kSampleRate = 48000; | 31 static const int kSampleRate = 48000; |
| 35 | 32 |
| 36 // Number of full sine wave cycles for each Render() call. | 33 // Number of full sine wave cycles for each Render() call. |
| 37 static const int kSineCycles = 4; | 34 static const int kSineCycles = 4; |
| 38 | 35 |
| 39 // Command line switch for runtime adjustment of VectorFMACBenchmark iterations. | |
| 40 static const char kVectorFMACIterations[] = "vector-fmac-iterations"; | |
| 41 | |
| 42 // Test parameters for VectorFMAC tests. | |
| 43 static const float kScale = 0.5; | |
| 44 static const float kInputFillValue = 1.0; | |
| 45 static const float kOutputFillValue = 3.0; | |
| 46 | |
| 47 // Ensure various optimized VectorFMAC() methods return the same value. | |
| 48 TEST(AudioRendererMixerTest, VectorFMAC) { | |
| 49 // Initialize a dummy mixer. | |
| 50 scoped_refptr<MockAudioRendererSink> sink = new MockAudioRendererSink(); | |
| 51 EXPECT_CALL(*sink, Start()); | |
| 52 EXPECT_CALL(*sink, Stop()); | |
| 53 AudioParameters params( | |
| 54 AudioParameters::AUDIO_PCM_LINEAR, kChannelLayout, kSampleRate, | |
| 55 kBitsPerChannel, kHighLatencyBufferSize); | |
| 56 AudioRendererMixer mixer(params, params, sink); | |
| 57 | |
| 58 // Initialize input and output vectors. | |
| 59 scoped_ptr_malloc<float, base::ScopedPtrAlignedFree> input_vector( | |
| 60 static_cast<float*>( | |
| 61 base::AlignedAlloc(sizeof(float) * kHighLatencyBufferSize, 16))); | |
| 62 scoped_ptr_malloc<float, base::ScopedPtrAlignedFree> output_vector( | |
| 63 static_cast<float*>( | |
| 64 base::AlignedAlloc(sizeof(float) * kHighLatencyBufferSize, 16))); | |
| 65 | |
| 66 // Setup input and output vectors. | |
| 67 std::fill(input_vector.get(), input_vector.get() + kHighLatencyBufferSize, | |
| 68 kInputFillValue); | |
| 69 std::fill(output_vector.get(), output_vector.get() + kHighLatencyBufferSize, | |
| 70 kOutputFillValue); | |
| 71 mixer.VectorFMAC_C( | |
| 72 input_vector.get(), kScale, kHighLatencyBufferSize, output_vector.get()); | |
| 73 for(int i = 0; i < kHighLatencyBufferSize; ++i) { | |
| 74 ASSERT_FLOAT_EQ(output_vector.get()[i], | |
| 75 kInputFillValue * kScale + kOutputFillValue); | |
| 76 } | |
| 77 | |
| 78 #if defined(ARCH_CPU_X86_FAMILY) && defined(__SSE__) | |
| 79 // Reset vectors, and try with SSE. | |
| 80 std::fill(output_vector.get(), output_vector.get() + kHighLatencyBufferSize, | |
| 81 kOutputFillValue); | |
| 82 mixer.VectorFMAC_SSE( | |
| 83 input_vector.get(), kScale, kHighLatencyBufferSize, output_vector.get()); | |
| 84 for(int i = 0; i < kHighLatencyBufferSize; ++i) { | |
| 85 ASSERT_FLOAT_EQ(output_vector.get()[i], | |
| 86 kInputFillValue * kScale + kOutputFillValue); | |
| 87 } | |
| 88 #endif | |
| 89 } | |
| 90 | |
| 91 // Benchmark for the various VectorFMAC() methods. Make sure to build with | |
| 92 // branding=Chrome so that DCHECKs are compiled out when benchmarking. Original | |
| 93 // benchmarks were run with --vector-fmac-iterations=200000. | |
| 94 TEST(AudioRendererMixerTest, VectorFMACBenchmark) { | |
| 95 // Initialize a dummy mixer. | |
| 96 scoped_refptr<MockAudioRendererSink> sink = new MockAudioRendererSink(); | |
| 97 EXPECT_CALL(*sink, Start()); | |
| 98 EXPECT_CALL(*sink, Stop()); | |
| 99 AudioParameters params( | |
| 100 AudioParameters::AUDIO_PCM_LINEAR, kChannelLayout, kSampleRate, | |
| 101 kBitsPerChannel, kHighLatencyBufferSize); | |
| 102 AudioRendererMixer mixer(params, params, sink); | |
| 103 | |
| 104 // Initialize input and output vectors. | |
| 105 scoped_ptr_malloc<float, base::ScopedPtrAlignedFree> input_vector( | |
| 106 static_cast<float*>( | |
| 107 base::AlignedAlloc(sizeof(float) * kHighLatencyBufferSize, 16))); | |
| 108 scoped_ptr_malloc<float, base::ScopedPtrAlignedFree> output_vector( | |
| 109 static_cast<float*>( | |
| 110 base::AlignedAlloc(sizeof(float) * kHighLatencyBufferSize, 16))); | |
| 111 | |
| 112 // Retrieve benchmark iterations from command line. | |
| 113 int vector_fmac_iterations = 10; | |
| 114 std::string iterations(CommandLine::ForCurrentProcess()->GetSwitchValueASCII( | |
| 115 kVectorFMACIterations)); | |
| 116 if (!iterations.empty()) | |
| 117 base::StringToInt(iterations, &vector_fmac_iterations); | |
| 118 | |
| 119 printf("Benchmarking %d iterations:\n", vector_fmac_iterations); | |
| 120 | |
| 121 // Benchmark VectorFMAC_C(). | |
| 122 std::fill(input_vector.get(), input_vector.get() + kHighLatencyBufferSize, | |
| 123 kInputFillValue); | |
| 124 std::fill(output_vector.get(), output_vector.get() + kHighLatencyBufferSize, | |
| 125 kOutputFillValue); | |
| 126 base::TimeTicks start = base::TimeTicks::HighResNow(); | |
| 127 for (int i = 0; i < vector_fmac_iterations; ++i) { | |
| 128 mixer.VectorFMAC_C(input_vector.get(), static_cast<float>(M_PI), | |
| 129 kHighLatencyBufferSize, output_vector.get()); | |
| 130 } | |
| 131 double total_time_c_ms = | |
| 132 (base::TimeTicks::HighResNow() - start).InMillisecondsF(); | |
| 133 printf("VectorFMAC_C took %.2fms.\n", total_time_c_ms); | |
| 134 | |
| 135 #if defined(ARCH_CPU_X86_FAMILY) && defined(__SSE__) | |
| 136 // Benchmark VectorFMAC_SSE() with unaligned size; I.e., size % 4 != 0. | |
| 137 ASSERT_NE((kHighLatencyBufferSize - 1) % 4, 0); | |
| 138 std::fill(output_vector.get(), output_vector.get() + kHighLatencyBufferSize, | |
| 139 kOutputFillValue); | |
| 140 start = base::TimeTicks::HighResNow(); | |
| 141 for (int j = 0; j < vector_fmac_iterations; ++j) { | |
| 142 mixer.VectorFMAC_SSE(input_vector.get(), M_PI, kHighLatencyBufferSize - 1, | |
| 143 output_vector.get()); | |
| 144 } | |
| 145 double total_time_sse_unaligned_ms = | |
| 146 (base::TimeTicks::HighResNow() - start).InMillisecondsF(); | |
| 147 printf("VectorFMAC_SSE (unaligned size) took %.2fms; which is %.2fx faster" | |
| 148 " than VectorFMAC_C.\n", total_time_sse_unaligned_ms, | |
| 149 total_time_c_ms / total_time_sse_unaligned_ms); | |
| 150 | |
| 151 // Benchmark VectorFMAC_SSE() with aligned size; I.e., size % 4 == 0. | |
| 152 ASSERT_EQ(kHighLatencyBufferSize % 4, 0); | |
| 153 std::fill(output_vector.get(), output_vector.get() + kHighLatencyBufferSize, | |
| 154 kOutputFillValue); | |
| 155 start = base::TimeTicks::HighResNow(); | |
| 156 for (int j = 0; j < vector_fmac_iterations; ++j) { | |
| 157 mixer.VectorFMAC_SSE(input_vector.get(), M_PI, kHighLatencyBufferSize, | |
| 158 output_vector.get()); | |
| 159 } | |
| 160 double total_time_sse_aligned_ms = | |
| 161 (base::TimeTicks::HighResNow() - start).InMillisecondsF(); | |
| 162 printf("VectorFMAC_SSE (aligned size) took %.2fms; which is %.2fx faster than" | |
| 163 " VectorFMAC_C and %.2fx faster than VectorFMAC_SSE (unaligned size)." | |
| 164 "\n", | |
| 165 total_time_sse_aligned_ms, total_time_c_ms / total_time_sse_aligned_ms, | |
| 166 total_time_sse_unaligned_ms / total_time_sse_aligned_ms); | |
| 167 #endif | |
| 168 } | |
| 169 | |
| 170 // Tuple of <input sampling rate, output sampling rate, epsilon>. | 36 // Tuple of <input sampling rate, output sampling rate, epsilon>. |
| 171 typedef std::tr1::tuple<int, int, double> AudioRendererMixerTestData; | 37 typedef std::tr1::tuple<int, int, double> AudioRendererMixerTestData; |
| 172 class AudioRendererMixerTest | 38 class AudioRendererMixerTest |
| 173 : public testing::TestWithParam<AudioRendererMixerTestData> { | 39 : public testing::TestWithParam<AudioRendererMixerTestData> { |
| 174 public: | 40 public: |
| 175 AudioRendererMixerTest() | 41 AudioRendererMixerTest() |
| 176 : epsilon_(std::tr1::get<2>(GetParam())), | 42 : epsilon_(std::tr1::get<2>(GetParam())), |
| 177 half_fill_(false) { | 43 half_fill_(false) { |
| 178 // Create input and output parameters based on test parameters. | 44 // Create input and output parameters based on test parameters. |
| 179 input_parameters_ = AudioParameters( | 45 input_parameters_ = AudioParameters( |
| (...skipping 347 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 527 // No resampling. | 393 // No resampling. |
| 528 std::tr1::make_tuple(44100, 44100, 0.00000048), | 394 std::tr1::make_tuple(44100, 44100, 0.00000048), |
| 529 | 395 |
| 530 // Upsampling. | 396 // Upsampling. |
| 531 std::tr1::make_tuple(44100, 48000, 0.033), | 397 std::tr1::make_tuple(44100, 48000, 0.033), |
| 532 | 398 |
| 533 // Downsampling. | 399 // Downsampling. |
| 534 std::tr1::make_tuple(48000, 41000, 0.042))); | 400 std::tr1::make_tuple(48000, 41000, 0.042))); |
| 535 | 401 |
| 536 } // namespace media | 402 } // namespace media |
| OLD | NEW |