Skip to content

Commit

Permalink
More perf test
Browse files Browse the repository at this point in the history
  • Loading branch information
Segfault1602 committed Dec 4, 2023
1 parent 8843cb2 commit 8c18954
Show file tree
Hide file tree
Showing 9 changed files with 245 additions and 40 deletions.
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,8 @@
"shared_mutex": "cpp",
"regex": "cpp",
"coroutine": "cpp",
"span": "cpp"
"span": "cpp",
"__verbose_abort": "cpp"
},
"vscode-nmake-tools.workspaceBuildDirectories": [
"."
Expand Down
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ find_program(CLANG_TIDY_EXE NAMES "clang-tidy")
set(CLANG_TIDY_COMMAND "${CLANG_TIDY_EXE}")
set(CMAKE_OSX_DEPLOYMENT_TARGET "13")

set(CLANG_COMPILER_OPTION ${CLANG_COMPILER_OPTION} -Wall -Wpedantic -Werror)
set(CLANG_COMPILER_OPTION ${CLANG_COMPILER_OPTION} -Wall -Wpedantic -Werror -Fsave-optimization-record)
set(MSVC_COMPILER_OPTION /W4 /analyze)
set(GCC_COMPILER_OPTION -Wall -Wpedantic)

Expand Down
78 changes: 68 additions & 10 deletions src/basic_oscillators.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,12 +97,39 @@ float Tri(float phase)

float Tri(float phase, float phase_increment, float* out, size_t size)
{
for (size_t i = 0; i < size; ++i)
size_t write_ptr = 0;
size_t block_count = size / 4;
for (size_t i = 0; i < block_count; ++i)
{
phase = std::fmod(phase, 1.f);
const float t = -1.0f + (2.0f * phase);
out[i] = 2.0f * (fabsf(t) - 0.5f);
const float phase1 = fmodf(phase, 1.f);
phase += phase_increment;
const float phase2 = fmodf(phase, 1.f);
phase += phase_increment;
const float phase3 = fmodf(phase, 1.f);
phase += phase_increment;
const float phase4 = fmodf(phase, 1.f);
phase += phase_increment;

const float t1 = -1.0f + (2.0f * phase1);
const float t2 = -1.0f + (2.0f * phase2);
const float t3 = -1.0f + (2.0f * phase3);
const float t4 = -1.0f + (2.0f * phase4);

out[write_ptr] = 2.0f * (fabsf(t1) - 0.5f);
out[write_ptr + 1] = 2.0f * (fabsf(t2) - 0.5f);
out[write_ptr + 2] = 2.0f * (fabsf(t3) - 0.5f);
out[write_ptr + 3] = 2.0f * (fabsf(t4) - 0.5f);

write_ptr += 4;
}

// if size is not a multiple of 4, process the remaining samples
block_count = size % 4;
for (size_t i = 0; i < block_count; ++i)
{
out[write_ptr] = Tri(phase);
phase += phase_increment;
write_ptr += 1;
}

return phase;
Expand All @@ -113,6 +140,41 @@ float Saw(float phase)
return 2.f * phase - 1.f;
}

float Saw(float phase, float phase_increment, float* out, size_t size)
{
size_t write_ptr = 0;
size_t block_count = size / 4;
for (size_t i = 0; i < block_count; ++i)
{
const float phase1 = fmodf(phase, 1.f);
phase += phase_increment;
const float phase2 = fmodf(phase, 1.f);
phase += phase_increment;
const float phase3 = fmodf(phase, 1.f);
phase += phase_increment;
const float phase4 = fmodf(phase, 1.f);
phase += phase_increment;

out[write_ptr] = 2.f * phase1 - 1.f;
out[write_ptr + 1] = 2.f * phase2 - 1.f;
out[write_ptr + 2] = 2.f * phase3 - 1.f;
out[write_ptr + 3] = 2.f * phase4 - 1.f;

write_ptr += 4;
}

// if size is not a multiple of 4, process the remaining samples
block_count = size % 4;
for (size_t i = 0; i < block_count; ++i)
{
out[write_ptr] = 2.f * phase - 1.f;
phase += phase_increment;
write_ptr += 1;
}

return phase;
}

float Square(float phase)
{
return (phase < 0.5f) ? -1.f : 1.f;
Expand Down Expand Up @@ -203,12 +265,8 @@ void BasicOscillator::ProcessBlock(float* out, size_t size)
phase_ = std::fmod(phase_, 1.f);
break;
case OscillatorType::Saw:
for (size_t i = 0; i < size; ++i)
{
out[i] = Saw(phase_);
phase_ += phase_increment_;
phase_ = std::fmod(phase_, 1.f);
}
phase_ = Saw(phase_, phase_increment_, out, size);
phase_ = std::fmod(phase_, 1.f);
break;
case OscillatorType::Square:
for (size_t i = 0; i < size; ++i)
Expand Down
51 changes: 41 additions & 10 deletions tests/basic_oscillators_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ TEST(BasicOscillatorsTests, Triangle)
auto test_buffer = std::make_unique<float[]>(kSize);
size_t test_buffer_size = kSize;

LoadWavFile("waves/triangle_48k_440hz.wav", test_buffer, test_buffer_size, info);
ASSERT_TRUE(LoadWavFile("waves/triangle_48k_440hz.wav", test_buffer, test_buffer_size, info));

auto out = std::make_unique<float[]>(test_buffer_size);

Expand All @@ -143,33 +143,45 @@ TEST(BasicOscillatorsTests, Triangle)
}
}

TEST(BasicOscillatorsTests, TriangleBlock)
class BasicOscillatorTestParam : public ::testing::TestWithParam<sfdsp::OscillatorType>
{
public:
BasicOscillatorTestParam() = default;
};

TEST_P(BasicOscillatorTestParam, ProcessBlock)
{
const sfdsp::OscillatorType type = GetParam();
constexpr size_t kSamplerate = 48000;
constexpr float kFreq = 440;
constexpr float kFreq = 750;
constexpr size_t kSize = kSamplerate;
SF_INFO info;

auto test_buffer = std::make_unique<float[]>(kSize);
size_t test_buffer_size = kSize;

LoadWavFile("waves/triangle_48k_440hz.wav", test_buffer, test_buffer_size, info);
sfdsp::BasicOscillator osc;
osc.Init(kSamplerate, kFreq, type);

for (auto i = 0; i < test_buffer_size; ++i)
{
test_buffer[i] = osc.Tick();
}

auto out = std::make_unique<float[]>(test_buffer_size);

sfdsp::BasicOscillator osc;
osc.Init(kSamplerate, kFreq, sfdsp::OscillatorType::Tri);
sfdsp::BasicOscillator osc2;
osc2.Init(kSamplerate, kFreq, type);

const size_t block_size = 512;
const size_t block_count = test_buffer_size / block_size;
for (auto i = 0; i < block_count; ++i)
{
osc.ProcessBlock(out.get() + i * block_size, block_size);
osc2.ProcessBlock(out.get() + i * block_size, block_size);
}

// Process reminder
const size_t reminder = test_buffer_size % block_size;
osc.ProcessBlock(out.get() + block_count * block_size, reminder);
osc2.ProcessBlock(out.get() + block_count * block_size, reminder);

for (auto i = 0; i < test_buffer_size; ++i)
{
Expand Down Expand Up @@ -203,4 +215,23 @@ TEST(BasicOscillatorsTests, Saw)
{
ASSERT_NEAR(out[i], test_buffer[i], 0.0001f);
}
}
}

INSTANTIATE_TEST_SUITE_P(BasicOscillatorTest, BasicOscillatorTestParam,
::testing::Values(sfdsp::OscillatorType::Sine, sfdsp::OscillatorType::Tri,
sfdsp::OscillatorType::Saw, sfdsp::OscillatorType::Square),
[](const testing::TestParamInfo<BasicOscillatorTestParam::ParamType>& info) {
switch (info.param)
{
case sfdsp::OscillatorType::Sine:
return "Sine";
case sfdsp::OscillatorType::Tri:
return "Triangle";
case sfdsp::OscillatorType::Saw:
return "Saw";
case sfdsp::OscillatorType::Square:
return "Square";
default:
return "Unknown";
}
});
5 changes: 4 additions & 1 deletion tests/perf/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ FetchContent_Declare(

FetchContent_MakeAvailable(nanobench doctest)

add_executable(perf_tests perf_tests.cpp basicosc_perf.cpp)
add_executable(perf_tests
perf_tests.cpp
basicosc_perf.cpp
phaseshaper_perf.cpp)
target_include_directories(perf_tests PRIVATE ${doctest_SOURCE_DIR}/doctest)
target_link_libraries(perf_tests PRIVATE nanobench dsp doctest)
70 changes: 56 additions & 14 deletions tests/perf/basicosc_perf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,38 @@ using namespace ankerl;
using namespace std::chrono_literals;

constexpr size_t kSamplerate = 48000;
constexpr size_t kOutputSize = kSamplerate * 5;
constexpr size_t kOutputSize = kSamplerate * 10;
constexpr float kFreq = 750;
constexpr size_t kBlockSize = 512;

// Render 5 second of audio
TEST_CASE("Sine")
constexpr const char* OscillatorTypeToString(sfdsp::OscillatorType type)
{
switch (type)
{
case sfdsp::OscillatorType::Sine:
return "Sine";
case sfdsp::OscillatorType::Tri:
return "Triangle";
case sfdsp::OscillatorType::Saw:
return "Saw";
case sfdsp::OscillatorType::Square:
return "Square";
default:
return "Unknown";
}
}

void RenderBlock(sfdsp::OscillatorType type, nanobench::Bench& bench)
{
sfdsp::BasicOscillator osc;
osc.Init(kSamplerate, kFreq, sfdsp::OscillatorType::Sine);
osc.Init(kSamplerate, kFreq, type);
auto out = std::make_unique<float[]>(kOutputSize);

nanobench::Bench bench;
bench.title("BasicOscillator (Sine)");
bench.relative(true);
bench.minEpochIterations(5);
bench.timeUnit(1ms, "ms");
constexpr const char* format_string = "BasicOscillator::ProcessBlock (%s)";
char buffer[128];
snprintf(buffer, sizeof(buffer), format_string, OscillatorTypeToString(type));

bench.run("BasicOscillator::ProcessBlock (Sine)", [&]() {
bench.run(buffer, [&]() {
const size_t block_count = kOutputSize / kBlockSize;
float* write_ptr = out.get();
for (size_t i = 0; i < block_count; ++i)
Expand All @@ -38,12 +52,40 @@ TEST_CASE("Sine")
const size_t reminder = kOutputSize % kBlockSize;
osc.ProcessBlock(write_ptr, reminder);
});
}

sfdsp::BasicOscillator osc2;
osc2.Init(kSamplerate, kFreq, sfdsp::OscillatorType::Sine);
void RenderTick(sfdsp::OscillatorType type, nanobench::Bench& bench)
{
sfdsp::BasicOscillator osc;
osc.Init(kSamplerate, kFreq, type);
auto out = std::make_unique<float[]>(kOutputSize);

bench.run("BasicOscillator::Tick (Sine)", [&]() {
constexpr const char* format_string = "BasicOscillator::Tick (%s)";
char buffer[128];
snprintf(buffer, sizeof(buffer), format_string, OscillatorTypeToString(type));

bench.run(buffer, [&]() {
for (auto i = 0; i < kOutputSize; ++i)
out[i] = osc2.Tick();
out[i] = osc.Tick();
});
}

// Render 5 second of audio
TEST_CASE("BasicOscillator")
{
nanobench::Bench bench;
bench.title("BasicOscillator");
bench.relative(true);
bench.minEpochIterations(5);
bench.timeUnit(1ms, "ms");

RenderBlock(sfdsp::OscillatorType::Sine, bench);
RenderBlock(sfdsp::OscillatorType::Tri, bench);
RenderBlock(sfdsp::OscillatorType::Saw, bench);
RenderBlock(sfdsp::OscillatorType::Square, bench);

RenderTick(sfdsp::OscillatorType::Sine, bench);
RenderTick(sfdsp::OscillatorType::Tri, bench);
RenderTick(sfdsp::OscillatorType::Saw, bench);
RenderTick(sfdsp::OscillatorType::Square, bench);
}
70 changes: 70 additions & 0 deletions tests/perf/phaseshaper_perf.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#include "doctest.h"
#include "nanobench.h"
#include <chrono>

#include "phaseshapers.h"

using namespace ankerl;
using namespace std::chrono_literals;

constexpr size_t kSamplerate = 48000;
constexpr size_t kOutputSize = kSamplerate * 10;
constexpr float kFreq = 750;
constexpr size_t kBlockSize = 512;

constexpr const char* PhaseshaperTypeToString(sfdsp::Phaseshaper::Waveform type)
{
switch (type)
{
case sfdsp::Phaseshaper::Waveform::VARIABLE_SLOPE:
return "Variable Slope";
case sfdsp::Phaseshaper::Waveform::WAVESLICE:
return "Waveslice";
case sfdsp::Phaseshaper::Waveform::SUPERSAW:
return "Supersaw";
case sfdsp::Phaseshaper::Waveform::RIPPLE:
return "Ripple";
case sfdsp::Phaseshaper::Waveform::SOFTSYNC:
return "Softsync";
case sfdsp::Phaseshaper::Waveform::TRIANGLE_MOD:
return "Triangle Mod";
default:
return "Unknown";
}
}

void RenderTick(sfdsp::Phaseshaper::Waveform type, nanobench::Bench& bench)
{
sfdsp::Phaseshaper ps;
ps.Init(kSamplerate);
ps.SetFreq(kFreq);
ps.SetWaveform(type);
auto out = std::make_unique<float[]>(kOutputSize);

constexpr const char* format_string = "Phaseshaper::Tick (%s)";
char buffer[128];
snprintf(buffer, sizeof(buffer), format_string, PhaseshaperTypeToString(type));

bench.run(buffer, [&]() {
for (auto i = 0; i < kOutputSize; ++i)
out[i] = ps.Process();
});
}

TEST_CASE("Phaseshaper")
{
nanobench::Bench bench;
bench.title("Phaseshaper");
bench.relative(true);
bench.minEpochIterations(5);
bench.timeUnit(1ms, "ms");

sfdsp::Phaseshaper ps;
ps.Init(kSamplerate);
ps.SetFreq(kFreq);

for (size_t i = 0; i < static_cast<size_t>(sfdsp::Phaseshaper::Waveform::NUM_WAVES); ++i)
{
RenderTick(static_cast<sfdsp::Phaseshaper::Waveform>(i), bench);
}
}
Loading

0 comments on commit 8c18954

Please sign in to comment.