- Created a new SIMD interface header and source files for audio processing functions. - Implemented functions for filling buffers, mixing audio, applying gain, calculating RMS and peak values, normalizing audio, converting stereo to mono, limiting audio, fading audio, and a simple equalizer. - Added SSE-specific implementations for the audio processing functions to leverage SIMD for performance improvements. - Updated CMakeLists.txt files to include new libraries and link dependencies for the SIMD interface and SSE implementations. - Introduced a static test helper library for unit testing with Google Test framework.
728 lines
24 KiB
C++
728 lines
24 KiB
C++
|
||
#include <gtest/gtest.h>
|
||
#include <vector>
|
||
#include <cmath>
|
||
#include <limits>
|
||
#include <algorithm>
|
||
#include <numeric>
|
||
#include <chrono>
|
||
#include <iostream>
|
||
|
||
// 修正include路径
|
||
#include "simd_api.h"
|
||
#include "aligned_allocator.h"
|
||
#include "performance_timer.h"
|
||
|
||
using namespace shm_test;
|
||
|
||
namespace {
|
||
|
||
// ============================================================================
|
||
// 测试数据生成辅助函数
|
||
// ============================================================================
|
||
|
||
/**
|
||
* @brief 生成正弦波测试数据
|
||
* @param frequency 频率(0-1,相对于采样率)
|
||
* @param num_samples 样本数量
|
||
* @return 正弦波数据向量
|
||
*/
|
||
template<typename Allocator = std::allocator<float>>
|
||
std::vector<float, Allocator> generate_sine_wave(float frequency, size_t num_samples) {
|
||
std::vector<float, Allocator> data(num_samples);
|
||
for (size_t i = 0; i < num_samples; ++i) {
|
||
data[i] = std::sin(2.0f * 3.14159265f * frequency * i);
|
||
}
|
||
return data;
|
||
}
|
||
|
||
/**
|
||
* @brief 生成常数值测试数据
|
||
* @param value 常数值
|
||
* @param num_samples 样本数量
|
||
* @return 常数值数据向量
|
||
*/
|
||
template<typename Allocator = std::allocator<float>>
|
||
std::vector<float, Allocator> generate_constant_data(float value, size_t num_samples) {
|
||
return std::vector<float, Allocator>(num_samples, value);
|
||
}
|
||
|
||
/**
|
||
* @brief 检查两个浮点数是否接近
|
||
* @param a 第一个值
|
||
* @param b 第二个值
|
||
* @param tolerance 容差
|
||
* @return 是否接近
|
||
*/
|
||
bool float_near(float a, float b, float tolerance = 1e-5f) {
|
||
if (std::isnan(a) || std::isnan(b)) return false;
|
||
if (std::isinf(a) || std::isinf(b)) return a == b;
|
||
return std::abs(a - b) < tolerance;
|
||
}
|
||
|
||
/**
|
||
* @brief 检查两个缓冲区是否相等(允许浮点误差)
|
||
* @param expected 期望值
|
||
* @param actual 实际值
|
||
* @param tolerance 容差
|
||
* @return 是否相等
|
||
*/
|
||
template<typename Allocator1, typename Allocator2>
|
||
bool buffers_equal(const std::vector<float, Allocator1>& expected,
|
||
const std::vector<float, Allocator2>& actual,
|
||
float tolerance = 1e-5f) {
|
||
if (expected.size() != actual.size()) {
|
||
return false;
|
||
}
|
||
for (size_t i = 0; i < expected.size(); ++i) {
|
||
if (!float_near(expected[i], actual[i], tolerance)) {
|
||
return false;
|
||
}
|
||
}
|
||
return true;
|
||
}
|
||
|
||
// ============================================================================
|
||
// 功能单元测试 - 参数化测试
|
||
// ============================================================================
|
||
|
||
/**
|
||
* @brief 测试参数结构体
|
||
*/
|
||
struct AudioProcessingTestParams {
|
||
size_t buffer_size; // 缓冲区大小
|
||
int num_channels; // 通道数
|
||
float gain_value; // 增益值
|
||
};
|
||
|
||
/**
|
||
* @brief 填充缓冲区功能测试类
|
||
*/
|
||
class FillBufferTest : public ::testing::TestWithParam<AudioProcessingTestParams> {
|
||
protected:
|
||
void SetUp() override {
|
||
params = GetParam();
|
||
}
|
||
|
||
AudioProcessingTestParams params;
|
||
};
|
||
|
||
TEST_P(FillBufferTest, FillsBufferWithValue) {
|
||
const float fill_value = 3.14f;
|
||
size_t total_samples = params.buffer_size * params.num_channels;
|
||
|
||
std::vector<float, avx512_aligned_allocator> buffer(total_samples);
|
||
simd::fill_buffer(buffer.data(), fill_value, total_samples);
|
||
|
||
for (size_t i = 0; i < total_samples; ++i) {
|
||
EXPECT_FLOAT_EQ(buffer[i], fill_value)
|
||
<< "值不匹配于索引 " << i;
|
||
}
|
||
}
|
||
|
||
TEST_P(FillBufferTest, FillWithZero) {
|
||
size_t total_samples = params.buffer_size * params.num_channels;
|
||
std::vector<float, avx512_aligned_allocator> buffer(total_samples);
|
||
|
||
simd::fill_buffer(buffer.data(), 0.0f, total_samples);
|
||
|
||
for (size_t i = 0; i < total_samples; ++i) {
|
||
EXPECT_EQ(buffer[i], 0.0f);
|
||
}
|
||
}
|
||
|
||
TEST_P(FillBufferTest, FillWithNegativeValue) {
|
||
const float fill_value = -2.5f;
|
||
size_t total_samples = params.buffer_size * params.num_channels;
|
||
std::vector<float, avx512_aligned_allocator> buffer(total_samples);
|
||
|
||
simd::fill_buffer(buffer.data(), fill_value, total_samples);
|
||
|
||
for (size_t i = 0; i < total_samples; ++i) {
|
||
EXPECT_FLOAT_EQ(buffer[i], fill_value);
|
||
}
|
||
}
|
||
|
||
INSTANTIATE_TEST_SUITE_P(
|
||
FillBufferParametrized,
|
||
FillBufferTest,
|
||
::testing::Values(
|
||
AudioProcessingTestParams{64, 1, 1.0f},
|
||
AudioProcessingTestParams{256, 2, 1.0f},
|
||
AudioProcessingTestParams{1024, 4, 1.0f},
|
||
AudioProcessingTestParams{4096, 8, 1.0f},
|
||
AudioProcessingTestParams{8192, 1, 1.0f}
|
||
)
|
||
);
|
||
|
||
/**
|
||
* @brief 应用增益功能测试类
|
||
*/
|
||
class ApplyGainTest : public ::testing::TestWithParam<AudioProcessingTestParams> {
|
||
protected:
|
||
void SetUp() override {
|
||
params = GetParam();
|
||
}
|
||
|
||
AudioProcessingTestParams params;
|
||
};
|
||
|
||
TEST_P(ApplyGainTest, AppliesGainCorrectly) {
|
||
size_t total_samples = params.buffer_size * params.num_channels;
|
||
const float gain = params.gain_value;
|
||
|
||
std::vector<float, avx512_aligned_allocator> src(total_samples, 2.0f);
|
||
std::vector<float, avx512_aligned_allocator> dst(total_samples, 0.0f);
|
||
|
||
simd::apply_gain(src.data(), dst.data(), gain, total_samples);
|
||
|
||
const float expected_value = 2.0f * gain;
|
||
for (size_t i = 0; i < total_samples; ++i) {
|
||
EXPECT_FLOAT_EQ(dst[i], expected_value)
|
||
<< "增益应用错误于索引 " << i;
|
||
}
|
||
}
|
||
|
||
TEST_P(ApplyGainTest, GainZeroMakesOutputZero) {
|
||
size_t total_samples = params.buffer_size * params.num_channels;
|
||
std::vector<float, avx512_aligned_allocator> src(total_samples, 5.0f);
|
||
std::vector<float, avx512_aligned_allocator> dst(total_samples, 1.0f);
|
||
|
||
simd::apply_gain(src.data(), dst.data(), 0.0f, total_samples);
|
||
|
||
for (size_t i = 0; i < total_samples; ++i) {
|
||
EXPECT_FLOAT_EQ(dst[i], 0.0f);
|
||
}
|
||
}
|
||
|
||
TEST_P(ApplyGainTest, GainOnePreservesValues) {
|
||
size_t total_samples = params.buffer_size * params.num_channels;
|
||
auto raw_src = generate_sine_wave(0.1f, total_samples);
|
||
std::vector<float, avx512_aligned_allocator> src(raw_src.begin(), raw_src.end());
|
||
std::vector<float, avx512_aligned_allocator> dst(total_samples, 0.0f);
|
||
|
||
simd::apply_gain(src.data(), dst.data(), 1.0f, total_samples);
|
||
|
||
// 验证增益为1.0时,源数据应该保持不变
|
||
for (size_t i = 0; i < total_samples; ++i) {
|
||
EXPECT_FLOAT_EQ(src[i], raw_src[i]);
|
||
}
|
||
}
|
||
|
||
TEST_P(ApplyGainTest, NegativeGainInvertsSignal) {
|
||
size_t total_samples = params.buffer_size * params.num_channels;
|
||
std::vector<float, avx512_aligned_allocator> src(total_samples, 3.0f);
|
||
std::vector<float, avx512_aligned_allocator> dst(total_samples, 0.0f);
|
||
|
||
simd::apply_gain(src.data(), dst.data(), -1.0f, total_samples);
|
||
|
||
for (size_t i = 0; i < total_samples; ++i) {
|
||
EXPECT_FLOAT_EQ(dst[i], -3.0f);
|
||
}
|
||
}
|
||
|
||
INSTANTIATE_TEST_SUITE_P(
|
||
ApplyGainParametrized,
|
||
ApplyGainTest,
|
||
::testing::Values(
|
||
AudioProcessingTestParams{64, 1, 0.0f},
|
||
AudioProcessingTestParams{256, 2, 0.5f},
|
||
AudioProcessingTestParams{1024, 4, 1.0f},
|
||
AudioProcessingTestParams{4096, 8, 2.0f},
|
||
AudioProcessingTestParams{8192, 1, 0.1f}
|
||
)
|
||
);
|
||
|
||
/**
|
||
* @brief 混音功能测试类
|
||
*/
|
||
class MixAudioTest : public ::testing::TestWithParam<AudioProcessingTestParams> {
|
||
protected:
|
||
void SetUp() override {
|
||
params = GetParam();
|
||
}
|
||
|
||
AudioProcessingTestParams params;
|
||
};
|
||
|
||
TEST_P(MixAudioTest, MixesAudioCorrectly) {
|
||
size_t total_samples = params.buffer_size * params.num_channels;
|
||
std::vector<float, avx512_aligned_allocator> src1(total_samples, 1.0f);
|
||
std::vector<float, avx512_aligned_allocator> src2(total_samples, 2.0f);
|
||
std::vector<float, avx512_aligned_allocator> dst(total_samples, 0.0f);
|
||
|
||
simd::mix_audio(src1.data(), src2.data(), dst.data(), total_samples);
|
||
|
||
for (size_t i = 0; i < total_samples; ++i) {
|
||
EXPECT_FLOAT_EQ(dst[i], 3.0f)
|
||
<< "混音结果错误于索引 " << i;
|
||
}
|
||
}
|
||
|
||
TEST_P(MixAudioTest, MixWithZero) {
|
||
size_t total_samples = params.buffer_size * params.num_channels;
|
||
std::vector<float, avx512_aligned_allocator> src1 = generate_sine_wave<avx512_aligned_allocator>(0.1f, total_samples);
|
||
std::vector<float, avx512_aligned_allocator> src2(total_samples, 0.0f);
|
||
std::vector<float, avx512_aligned_allocator> dst(total_samples, 0.0f);
|
||
|
||
simd::mix_audio(src1.data(), src2.data(), dst.data(), total_samples);
|
||
|
||
EXPECT_TRUE(buffers_equal(src1, dst, 1e-5f));
|
||
}
|
||
|
||
TEST_P(MixAudioTest, MixSymmetry) {
|
||
size_t total_samples = params.buffer_size * params.num_channels;
|
||
std::vector<float, avx512_aligned_allocator> src1 = generate_sine_wave<avx512_aligned_allocator>(0.1f, total_samples);
|
||
std::vector<float, avx512_aligned_allocator> src2 = generate_sine_wave<avx512_aligned_allocator>(0.05f, total_samples);
|
||
std::vector<float, avx512_aligned_allocator> dst1(total_samples, 0.0f);
|
||
std::vector<float, avx512_aligned_allocator> dst2(total_samples, 0.0f);
|
||
|
||
simd::mix_audio(src1.data(), src2.data(), dst1.data(), total_samples);
|
||
simd::mix_audio(src2.data(), src1.data(), dst2.data(), total_samples);
|
||
|
||
EXPECT_TRUE(buffers_equal(dst1, dst2, 1e-5f));
|
||
}
|
||
|
||
INSTANTIATE_TEST_SUITE_P(
|
||
MixAudioParametrized,
|
||
MixAudioTest,
|
||
::testing::Values(
|
||
AudioProcessingTestParams{64, 1, 0.0f},
|
||
AudioProcessingTestParams{256, 2, 0.0f},
|
||
AudioProcessingTestParams{1024, 4, 0.0f},
|
||
AudioProcessingTestParams{4096, 8, 0.0f},
|
||
AudioProcessingTestParams{8192, 1, 0.0f}
|
||
)
|
||
);
|
||
|
||
// ============================================================================
|
||
// 边界条件测试
|
||
// ============================================================================
|
||
|
||
class BoundaryConditionTest : public ::testing::Test {
|
||
protected:
|
||
const float epsilon = 1e-5f;
|
||
};
|
||
|
||
TEST_F(BoundaryConditionTest, FillBufferZeroLength) {
|
||
std::vector<float, avx512_aligned_allocator> buffer(10, 1.0f);
|
||
simd::fill_buffer(buffer.data(), 0.0f, 0); // 不应该修改任何元素
|
||
|
||
for (auto val : buffer) {
|
||
EXPECT_EQ(val, 1.0f);
|
||
}
|
||
}
|
||
|
||
TEST_F(BoundaryConditionTest, FillBufferSingleSample) {
|
||
std::vector<float, avx512_aligned_allocator> buffer(1);
|
||
const float fill_value = 7.5f;
|
||
|
||
simd::fill_buffer(buffer.data(), fill_value, 1);
|
||
|
||
EXPECT_FLOAT_EQ(buffer[0], fill_value);
|
||
}
|
||
|
||
TEST_F(BoundaryConditionTest, ApplyGainZeroLength) {
|
||
std::vector<float, avx512_aligned_allocator> src(10, 5.0f);
|
||
std::vector<float, avx512_aligned_allocator> dst(10, 1.0f);
|
||
|
||
simd::apply_gain(src.data(), dst.data(), 2.0f, 0);
|
||
|
||
for (auto val : dst) {
|
||
EXPECT_EQ(val, 1.0f);
|
||
}
|
||
}
|
||
|
||
TEST_F(BoundaryConditionTest, ApplyGainSingleSample) {
|
||
std::vector<float, avx512_aligned_allocator> src = {3.0f};
|
||
std::vector<float, avx512_aligned_allocator> dst = {0.0f};
|
||
|
||
simd::apply_gain(src.data(), dst.data(), 4.0f, 1);
|
||
|
||
EXPECT_FLOAT_EQ(dst[0], 12.0f);
|
||
}
|
||
|
||
TEST_F(BoundaryConditionTest, MixAudioZeroLength) {
|
||
std::vector<float, avx512_aligned_allocator> src1(10, 1.0f);
|
||
std::vector<float, avx512_aligned_allocator> src2(10, 2.0f);
|
||
std::vector<float, avx512_aligned_allocator> dst(10, 0.0f);
|
||
|
||
simd::mix_audio(src1.data(), src2.data(), dst.data(), 0);
|
||
|
||
for (auto val : dst) {
|
||
EXPECT_EQ(val, 0.0f);
|
||
}
|
||
}
|
||
|
||
TEST_F(BoundaryConditionTest, MixAudioSingleSample) {
|
||
std::vector<float, avx512_aligned_allocator> src1 = {2.5f};
|
||
std::vector<float, avx512_aligned_allocator> src2 = {3.5f};
|
||
std::vector<float, avx512_aligned_allocator> dst = {0.0f};
|
||
|
||
simd::mix_audio(src1.data(), src2.data(), dst.data(), 1);
|
||
|
||
EXPECT_FLOAT_EQ(dst[0], 6.0f);
|
||
}
|
||
|
||
TEST_F(BoundaryConditionTest, ExtremeLargeValues) {
|
||
std::vector<float, avx512_aligned_allocator> buffer(100);
|
||
const float large_value = 1e6f;
|
||
|
||
simd::fill_buffer(buffer.data(), large_value, buffer.size());
|
||
|
||
for (auto val : buffer) {
|
||
EXPECT_FLOAT_EQ(val, large_value);
|
||
}
|
||
}
|
||
|
||
TEST_F(BoundaryConditionTest, ExtremeSmallValues) {
|
||
std::vector<float, avx512_aligned_allocator> buffer(100);
|
||
const float small_value = 1e-6f;
|
||
|
||
simd::fill_buffer(buffer.data(), small_value, buffer.size());
|
||
|
||
for (auto val : buffer) {
|
||
EXPECT_NEAR(val, small_value, 1e-12f);
|
||
}
|
||
}
|
||
|
||
TEST_F(BoundaryConditionTest, FillBufferWithMaxFloat) {
|
||
std::vector<float, avx512_aligned_allocator> buffer(10);
|
||
const float max_val = std::numeric_limits<float>::max();
|
||
|
||
simd::fill_buffer(buffer.data(), max_val, buffer.size());
|
||
|
||
for (auto val : buffer) {
|
||
EXPECT_EQ(val, max_val);
|
||
}
|
||
}
|
||
|
||
TEST_F(BoundaryConditionTest, ApplyGainWithVerySmallGain) {
|
||
std::vector<float, avx512_aligned_allocator> src(100, 1e6f);
|
||
std::vector<float, avx512_aligned_allocator> dst(100);
|
||
const float tiny_gain = 1e-7f;
|
||
|
||
simd::apply_gain(src.data(), dst.data(), tiny_gain, src.size());
|
||
|
||
for (size_t i = 0; i < src.size(); ++i) {
|
||
EXPECT_NEAR(dst[i], src[i] * tiny_gain, 1e-10f);
|
||
}
|
||
}
|
||
|
||
// ============================================================================
|
||
// 数据对齐测试
|
||
// ============================================================================
|
||
|
||
class AlignmentTest : public ::testing::Test {
|
||
protected:
|
||
template<size_t Alignment>
|
||
bool check_alignment(const void* ptr) const {
|
||
return reinterpret_cast<uintptr_t>(ptr) % Alignment == 0;
|
||
}
|
||
};
|
||
|
||
TEST_F(AlignmentTest, SSEAlignedBuffer) {
|
||
std::vector<float, aligned_allocator<float, ALIGNMENT_SSE>> buffer(1024);
|
||
EXPECT_TRUE(check_alignment<ALIGNMENT_SSE>(buffer.data()));
|
||
}
|
||
|
||
TEST_F(AlignmentTest, AVXAlignedBuffer) {
|
||
std::vector<float, aligned_allocator<float, ALIGNMENT_AVX>> buffer(1024);
|
||
EXPECT_TRUE(check_alignment<ALIGNMENT_AVX>(buffer.data()));
|
||
}
|
||
|
||
TEST_F(AlignmentTest, AVX512AlignedBuffer) {
|
||
std::vector<float, aligned_allocator<float, ALIGNMENT_AVX512>> buffer(1024);
|
||
EXPECT_TRUE(check_alignment<ALIGNMENT_AVX512>(buffer.data()));
|
||
}
|
||
|
||
TEST_F(AlignmentTest, FillBufferMaintainsAlignment) {
|
||
std::vector<float, avx512_aligned_allocator> buffer(1024);
|
||
EXPECT_TRUE(check_alignment<ALIGNMENT_AVX512>(buffer.data()));
|
||
|
||
simd::fill_buffer(buffer.data(), 1.0f, buffer.size());
|
||
|
||
// 数据仍然在原地,对齐不变
|
||
EXPECT_TRUE(check_alignment<ALIGNMENT_AVX512>(buffer.data()));
|
||
}
|
||
|
||
TEST_F(AlignmentTest, ApplyGainWithAlignedData) {
|
||
std::vector<float, avx512_aligned_allocator> src(256);
|
||
std::vector<float, avx512_aligned_allocator> dst(256);
|
||
|
||
EXPECT_TRUE(check_alignment<ALIGNMENT_AVX512>(src.data()));
|
||
EXPECT_TRUE(check_alignment<ALIGNMENT_AVX512>(dst.data()));
|
||
|
||
simd::apply_gain(src.data(), dst.data(), 0.5f, src.size());
|
||
|
||
// 对齐在操作后仍然有效
|
||
EXPECT_TRUE(check_alignment<ALIGNMENT_AVX512>(src.data()));
|
||
EXPECT_TRUE(check_alignment<ALIGNMENT_AVX512>(dst.data()));
|
||
}
|
||
|
||
TEST_F(AlignmentTest, MixAudioWithAlignedData) {
|
||
std::vector<float, avx512_aligned_allocator> src1(512);
|
||
std::vector<float, avx512_aligned_allocator> src2(512);
|
||
std::vector<float, avx512_aligned_allocator> dst(512);
|
||
|
||
EXPECT_TRUE(check_alignment<ALIGNMENT_AVX512>(src1.data()));
|
||
EXPECT_TRUE(check_alignment<ALIGNMENT_AVX512>(src2.data()));
|
||
EXPECT_TRUE(check_alignment<ALIGNMENT_AVX512>(dst.data()));
|
||
|
||
simd::mix_audio(src1.data(), src2.data(), dst.data(), src1.size());
|
||
|
||
EXPECT_TRUE(check_alignment<ALIGNMENT_AVX512>(dst.data()));
|
||
}
|
||
|
||
TEST_F(AlignmentTest, UnalignedBufferHandling) {
|
||
// 创建一个未对齐的缓冲区(通过跳过第一个字节)
|
||
std::vector<float, avx512_aligned_allocator> aligned_storage(1025);
|
||
float* unaligned_ptr = aligned_storage.data() + 1; // 偏移1个元素,破坏对齐
|
||
|
||
// 虽然指针未对齐,但函数应该仍然能工作(可能性能较低)
|
||
// 这测试了函数的鲁棒性,而不是对齐的要求
|
||
std::vector<float> expected(1024, 5.0f);
|
||
|
||
simd::fill_buffer(unaligned_ptr, 5.0f, 1024);
|
||
|
||
for (size_t i = 0; i < 1024; ++i) {
|
||
EXPECT_FLOAT_EQ(unaligned_ptr[i], 5.0f);
|
||
}
|
||
}
|
||
|
||
// ============================================================================
|
||
// 性能基准测试
|
||
// ============================================================================
|
||
|
||
class PerformanceBenchmark : public ::testing::Test {
|
||
protected:
|
||
const int ITERATIONS = 100;
|
||
};
|
||
|
||
TEST_F(PerformanceBenchmark, FillBufferThroughput64Samples) {
|
||
std::vector<float, avx512_aligned_allocator> buffer(64);
|
||
|
||
LatencyRecorder latency_recorder("FillBuffer_64");
|
||
|
||
for (int iter = 0; iter < ITERATIONS; ++iter) {
|
||
ScopedTimer timer("", false);
|
||
simd::fill_buffer(buffer.data(), 1.0f, buffer.size());
|
||
timer.stop();
|
||
latency_recorder.record(timer.elapsed_ns());
|
||
}
|
||
|
||
auto stats = latency_recorder.get_statistics();
|
||
double samples_per_sec = (64.0 * ITERATIONS * 1e9) / (stats.avg() * ITERATIONS);
|
||
|
||
std::cout << "FillBuffer (64 samples): " << samples_per_sec / 1e6 << " MSamples/sec" << std::endl;
|
||
EXPECT_GT(samples_per_sec, 0.0); // 基本健全性检查
|
||
}
|
||
|
||
TEST_F(PerformanceBenchmark, FillBufferThroughput1024Samples) {
|
||
std::vector<float, avx512_aligned_allocator> buffer(1024);
|
||
|
||
LatencyRecorder latency_recorder("FillBuffer_1024");
|
||
|
||
for (int iter = 0; iter < ITERATIONS; ++iter) {
|
||
ScopedTimer timer("", false);
|
||
simd::fill_buffer(buffer.data(), 1.0f, buffer.size());
|
||
timer.stop();
|
||
latency_recorder.record(timer.elapsed_ns());
|
||
}
|
||
|
||
auto stats = latency_recorder.get_statistics();
|
||
double samples_per_sec = (1024.0 * ITERATIONS * 1e9) / (stats.avg() * ITERATIONS);
|
||
|
||
std::cout << "FillBuffer (1024 samples): " << samples_per_sec / 1e6 << " MSamples/sec" << std::endl;
|
||
EXPECT_GT(samples_per_sec, 0.0);
|
||
}
|
||
|
||
TEST_F(PerformanceBenchmark, ApplyGainThroughput) {
|
||
std::vector<float, avx512_aligned_allocator> src(1024, 1.0f);
|
||
std::vector<float, avx512_aligned_allocator> dst(1024);
|
||
|
||
LatencyRecorder latency_recorder("ApplyGain_1024");
|
||
|
||
for (int iter = 0; iter < ITERATIONS; ++iter) {
|
||
ScopedTimer timer("", false);
|
||
simd::apply_gain(src.data(), dst.data(), 0.5f, src.size());
|
||
timer.stop();
|
||
latency_recorder.record(timer.elapsed_ns());
|
||
}
|
||
|
||
auto stats = latency_recorder.get_statistics();
|
||
double samples_per_sec = (1024.0 * ITERATIONS * 1e9) / (stats.avg() * ITERATIONS);
|
||
|
||
std::cout << "ApplyGain (1024 samples): " << samples_per_sec / 1e6 << " MSamples/sec" << std::endl;
|
||
EXPECT_GT(samples_per_sec, 0.0);
|
||
}
|
||
|
||
TEST_F(PerformanceBenchmark, MixAudioThroughput) {
|
||
std::vector<float, avx512_aligned_allocator> src1(1024, 1.0f);
|
||
std::vector<float, avx512_aligned_allocator> src2(1024, 2.0f);
|
||
std::vector<float, avx512_aligned_allocator> dst(1024);
|
||
|
||
LatencyRecorder latency_recorder("MixAudio_1024");
|
||
|
||
for (int iter = 0; iter < ITERATIONS; ++iter) {
|
||
ScopedTimer timer("", false);
|
||
simd::mix_audio(src1.data(), src2.data(), dst.data(), src1.size());
|
||
timer.stop();
|
||
latency_recorder.record(timer.elapsed_ns());
|
||
}
|
||
|
||
auto stats = latency_recorder.get_statistics();
|
||
double samples_per_sec = (1024.0 * ITERATIONS * 1e9) / (stats.avg() * ITERATIONS);
|
||
|
||
std::cout << "MixAudio (1024 samples): " << samples_per_sec / 1e6 << " MSamples/sec" << std::endl;
|
||
EXPECT_GT(samples_per_sec, 0.0);
|
||
}
|
||
|
||
// ============================================================================
|
||
// 跨实现一致性测试
|
||
// ============================================================================
|
||
|
||
class CrossImplementationConsistencyTest : public ::testing::Test {
|
||
protected:
|
||
void SetUp() override {
|
||
// 初始化测试数据
|
||
test_data_ = generate_sine_wave<avx512_aligned_allocator>(0.1f, 1024);
|
||
expected_result_ = test_data_;
|
||
}
|
||
|
||
std::vector<float, avx512_aligned_allocator> test_data_;
|
||
std::vector<float, avx512_aligned_allocator> expected_result_;
|
||
};
|
||
|
||
TEST_F(CrossImplementationConsistencyTest, FillBufferConsistency) {
|
||
std::vector<float, avx512_aligned_allocator> result(1024);
|
||
|
||
// 使用SIMD实现
|
||
simd::fill_buffer(result.data(), 5.0f, 1024);
|
||
|
||
// 验证所有元素都是5.0
|
||
for (size_t i = 0; i < 1024; ++i) {
|
||
EXPECT_FLOAT_EQ(result[i], 5.0f)
|
||
<< "SIMD实现结果不一致于索引 " << i;
|
||
}
|
||
}
|
||
|
||
TEST_F(CrossImplementationConsistencyTest, ApplyGainConsistency) {
|
||
std::vector<float, avx512_aligned_allocator> src(1024, 3.0f);
|
||
std::vector<float, avx512_aligned_allocator> result(1024);
|
||
|
||
// 使用SIMD实现
|
||
simd::apply_gain(src.data(), result.data(), 2.0f, 1024);
|
||
|
||
// 验证结果为6.0(3.0 * 2.0)
|
||
for (size_t i = 0; i < 1024; ++i) {
|
||
EXPECT_FLOAT_EQ(result[i], 6.0f)
|
||
<< "SIMD实现结果不一致于索引 " << i;
|
||
}
|
||
}
|
||
|
||
TEST_F(CrossImplementationConsistencyTest, MixAudioConsistency) {
|
||
std::vector<float, avx512_aligned_allocator> src1(1024, 1.0f);
|
||
std::vector<float, avx512_aligned_allocator> src2(1024, 2.0f);
|
||
std::vector<float, avx512_aligned_allocator> result(1024);
|
||
|
||
// 使用SIMD实现
|
||
simd::mix_audio(src1.data(), src2.data(), result.data(), 1024);
|
||
|
||
// 验证结果为3.0(1.0 + 2.0)
|
||
for (size_t i = 0; i < 1024; ++i) {
|
||
EXPECT_FLOAT_EQ(result[i], 3.0f)
|
||
<< "SIMD实现结果不一致于索引 " << i;
|
||
}
|
||
}
|
||
|
||
// ============================================================================
|
||
// 额外功能测试
|
||
// ============================================================================
|
||
|
||
/**
|
||
* @brief 计算RMS值的辅助函数
|
||
*/
|
||
float calculate_rms(const std::vector<float>& data) {
|
||
double sum = 0.0;
|
||
for (float val : data) {
|
||
sum += val * val;
|
||
}
|
||
return std::sqrt(sum / data.size());
|
||
}
|
||
|
||
/**
|
||
* @brief 计算峰值辅助函数
|
||
*/
|
||
float calculate_peak(const std::vector<float>& data) {
|
||
float peak = 0.0f;
|
||
for (float val : data) {
|
||
peak = std::max(peak, std::abs(val));
|
||
}
|
||
return peak;
|
||
}
|
||
|
||
class AdditionalFunctionalityTest : public ::testing::Test {
|
||
protected:
|
||
void SetUp() override {
|
||
test_data_ = generate_sine_wave<avx512_aligned_allocator>(0.1f, 1024);
|
||
}
|
||
|
||
std::vector<float, avx512_aligned_allocator> test_data_;
|
||
};
|
||
|
||
TEST_F(AdditionalFunctionalityTest, RMSCalculation) {
|
||
std::vector<float, avx512_aligned_allocator> src(test_data_.begin(), test_data_.end());
|
||
std::vector<float, avx512_aligned_allocator> dst(1024);
|
||
|
||
// 使用SIMD实现计算RMS
|
||
float rms = simd::calculate_rms(src.data(), 1024);
|
||
|
||
// 验证RMS值在合理范围内
|
||
EXPECT_GT(rms, 0.0);
|
||
EXPECT_LT(rms, 1.0);
|
||
|
||
// 验证与参考实现的一致性
|
||
std::vector<float> test_data_copy(test_data_.begin(), test_data_.end());
|
||
float expected_rms = calculate_rms(test_data_copy);
|
||
EXPECT_NEAR(rms, expected_rms, 0.01f);
|
||
}
|
||
|
||
TEST_F(AdditionalFunctionalityTest, PeakCalculation) {
|
||
std::vector<float, avx512_aligned_allocator> src(test_data_.begin(), test_data_.end());
|
||
|
||
// 使用SIMD实现计算峰值
|
||
float peak = simd::calculate_peak(src.data(), 1024);
|
||
|
||
// 验证峰值在合理范围内
|
||
EXPECT_GT(peak, 0.0);
|
||
EXPECT_LT(peak, 1.1f); // 允许一些误差
|
||
|
||
// 验证与参考实现的一致性
|
||
std::vector<float> test_data_copy(test_data_.begin(), test_data_.end());
|
||
float expected_peak = calculate_peak(test_data_copy);
|
||
EXPECT_NEAR(peak, expected_peak, 0.01f);
|
||
}
|
||
|
||
TEST_F(AdditionalFunctionalityTest, StereoToMono) {
|
||
// 创建交错的立体声测试数据 (LRLRLR...)
|
||
std::vector<float, avx512_aligned_allocator> stereo_interleaved(1024);
|
||
std::vector<float, avx512_aligned_allocator> mono_result(512);
|
||
|
||
// 填充交错数据:左声道=1.0,右声道=2.0
|
||
for (size_t i = 0; i < 512; ++i) {
|
||
stereo_interleaved[i * 2] = 1.0f; // 左声道
|
||
stereo_interleaved[i * 2 + 1] = 2.0f; // 右声道
|
||
}
|
||
|
||
// 转换为单声道
|
||
simd::stereo_to_mono(stereo_interleaved.data(), mono_result.data(), 512);
|
||
|
||
// 验证结果为平均值 (1.0 + 2.0) / 2 = 1.5
|
||
for (size_t i = 0; i < 512; ++i) {
|
||
EXPECT_FLOAT_EQ(mono_result[i], 1.5f);
|
||
}
|
||
}
|
||
|
||
} // namespace
|