Skip to content

Commit

Permalink
refactor(util): make RampingValue vectorizer-friendly
Browse files Browse the repository at this point in the history
Previously, invocations of `RampingValue::getNext()` caused inter-
dependence between loop iterations, making vectorization
impossible. This approach removes the state from `RampingValue`
and thus also the loop-iteration interdependence. `getNext()`
has been replaced by `getNth(int step)`.
While multiplication (`getNth`)is technically more expensive than
addition (`getNext`), the vectorization possibility results in a
1.1 - 6x speedup depending on optimizer-agressiveness.
  • Loading branch information
Swiftb0y committed May 26, 2022
1 parent d76f5f4 commit 06e4108
Show file tree
Hide file tree
Showing 4 changed files with 26 additions and 18 deletions.
2 changes: 1 addition & 1 deletion lib/reverb/Reverb.cc
Original file line number Diff line number Diff line change
Expand Up @@ -454,7 +454,7 @@ void MixxxPlateX2::processBuffer(const sample_t* in, sample_t* out, const uint f

// loop through the buffer, processing each sample
for (uint i = 0; i + 1 < frames; i += 2) {
sample_t mono_sample = send.getNext() * (in[i] + in[i + 1]) / 2;
sample_t mono_sample = send.getNth(i / 2) * (in[i] + in[i + 1]) / 2;
PlateStub::process(mono_sample, decay, &out[i], &out[i+1]);
}
}
Expand Down
10 changes: 6 additions & 4 deletions src/effects/builtin/echoeffect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -191,12 +191,14 @@ void EchoEffect::processChannel(const ChannelHandle& handle, EchoGroupState* pGr
pGroupState->prev_feedback,
bufferParameters.framesPerBuffer());

int rampIndex = 0;
//TODO: rewrite to remove assumption of stereo buffer
for (SINT i = 0;
i < bufferParameters.samplesPerBuffer();
i += bufferParameters.channelCount()) {
CSAMPLE_GAIN send_ramped = send.getNext();
CSAMPLE_GAIN feedback_ramped = feedback.getNext();
i < engineParameters.samplesPerBuffer();
i += engineParameters.channelCount()) {
CSAMPLE_GAIN send_ramped = send.getNth(rampIndex);
CSAMPLE_GAIN feedback_ramped = feedback.getNth(rampIndex);
++rampIndex;

CSAMPLE bufferedSampleLeft = pGroupState->delay_buf[read_position];
CSAMPLE bufferedSampleRight = pGroupState->delay_buf[read_position + 1];
Expand Down
14 changes: 8 additions & 6 deletions src/effects/builtin/flangereffect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -195,13 +195,15 @@ void FlangerEffect::processChannel(const ChannelHandle& handle,
CSAMPLE* delayLeft = pState->delayLeft;
CSAMPLE* delayRight = pState->delayRight;

int rampIndex = 0;
for (SINT i = 0;
i < bufferParameters.samplesPerBuffer();
i += bufferParameters.channelCount()) {
CSAMPLE_GAIN mix_ramped = mixRamped.getNext();
CSAMPLE_GAIN regen_ramped = regenRamped.getNext();
double width_ramped = widthRamped.getNext();
double manual_ramped = manualRamped.getNext();
i < engineParameters.samplesPerBuffer();
i += engineParameters.channelCount()) {
CSAMPLE_GAIN mix_ramped = mixRamped.getNth(rampIndex);
CSAMPLE_GAIN regen_ramped = regenRamped.getNth(rampIndex);
double width_ramped = widthRamped.getNth(rampIndex);
double manual_ramped = manualRamped.getNth(rampIndex);
++rampIndex;

pState->lfoFrames++;
if (pState->lfoFrames >= lfoPeriodFrames) {
Expand Down
18 changes: 11 additions & 7 deletions src/util/rampingvalue.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,20 @@
template <typename T>
class RampingValue {
public:
RampingValue(const T& initial, const T& final, int steps) {
m_value = initial;
m_increment = (final - initial) / steps;
constexpr RampingValue(const T& initial, const T& final, int steps)
: m_start(initial),
m_increment((final - initial) / steps) {
}

T getNext() {
return m_value += m_increment;
/// this method is supposed to be used in hot audio-processing loops
/// which benefit greatly from vectorization. For this to work, loop-
/// iterations can't have any data-dependencies on each other. If `getNth`
/// were to modify its instance in between iterations, a data-dependency
/// would be introduced, and vectorization made impossible!
[[nodiscard]] constexpr T getNth(int step) const {
return m_start + m_increment * step;
}

private:
T m_value;
T m_start;
T m_increment;
};

0 comments on commit 06e4108

Please sign in to comment.