Reimplementing NetEq4's AudioVector

The current implementation using std::vector is too slow.
This CL introduces a new implementation, using a regular
array as data container.

In AudioMultiVector::ReadInterleavedFromIndex, a special case for
1 channel was implemented, to further reduce runtime. Finally,
AudioMultiVector::Channels was reimplemented.

The changes in this CL reduces the runtime of neteq4_speed_test
by 33%.

BUG=1363
R=turaj@webrtc.org

Review URL: https://webrtc-codereview.appspot.com/3509004

git-svn-id: http://webrtc.googlecode.com/svn/trunk@5115 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
henrik.lundin@webrtc.org 2013-11-12 13:15:02 +00:00
parent 38599510df
commit e8433eb115
4 changed files with 113 additions and 83 deletions

View File

@ -24,6 +24,7 @@ AudioMultiVector::AudioMultiVector(size_t N) {
for (size_t n = 0; n < N; ++n) {
channels_.push_back(new AudioVector);
}
num_channels_ = N;
}
AudioMultiVector::AudioMultiVector(size_t N, size_t initial_size) {
@ -32,6 +33,7 @@ AudioMultiVector::AudioMultiVector(size_t N, size_t initial_size) {
for (size_t n = 0; n < N; ++n) {
channels_.push_back(new AudioVector(initial_size));
}
num_channels_ = N;
}
AudioMultiVector::~AudioMultiVector() {
@ -43,13 +45,13 @@ AudioMultiVector::~AudioMultiVector() {
}
void AudioMultiVector::Clear() {
for (size_t i = 0; i < Channels(); ++i) {
for (size_t i = 0; i < num_channels_; ++i) {
channels_[i]->Clear();
}
}
void AudioMultiVector::Zeros(size_t length) {
for (size_t i = 0; i < Channels(); ++i) {
for (size_t i = 0; i < num_channels_; ++i) {
channels_[i]->Clear();
channels_[i]->Extend(length);
}
@ -57,7 +59,7 @@ void AudioMultiVector::Zeros(size_t length) {
void AudioMultiVector::CopyFrom(AudioMultiVector* copy_to) const {
if (copy_to) {
for (size_t i = 0; i < Channels(); ++i) {
for (size_t i = 0; i < num_channels_; ++i) {
channels_[i]->CopyFrom(&(*copy_to)[i]);
}
}
@ -65,22 +67,22 @@ void AudioMultiVector::CopyFrom(AudioMultiVector* copy_to) const {
void AudioMultiVector::PushBackInterleaved(const int16_t* append_this,
size_t length) {
assert(length % Channels() == 0);
if (Channels() == 1) {
assert(length % num_channels_ == 0);
if (num_channels_ == 1) {
// Special case to avoid extra allocation and data shuffling.
channels_[0]->PushBack(append_this, length);
return;
}
size_t length_per_channel = length / Channels();
size_t length_per_channel = length / num_channels_;
int16_t* temp_array =
new int16_t[length_per_channel]; // Intermediate storage.
for (size_t channel = 0; channel < Channels(); ++channel) {
for (size_t channel = 0; channel < num_channels_; ++channel) {
// Copy elements to |temp_array|.
// Set |source_ptr| to first element of this channel.
const int16_t* source_ptr = &append_this[channel];
for (size_t i = 0; i < length_per_channel; ++i) {
temp_array[i] = *source_ptr;
source_ptr += Channels(); // Jump to next element of this channel.
source_ptr += num_channels_; // Jump to next element of this channel.
}
channels_[channel]->PushBack(temp_array, length_per_channel);
}
@ -88,9 +90,9 @@ void AudioMultiVector::PushBackInterleaved(const int16_t* append_this,
}
void AudioMultiVector::PushBack(const AudioMultiVector& append_this) {
assert(Channels() == append_this.Channels());
if (Channels() == append_this.Channels()) {
for (size_t i = 0; i < Channels(); ++i) {
assert(num_channels_ == append_this.num_channels_);
if (num_channels_ == append_this.num_channels_) {
for (size_t i = 0; i < num_channels_; ++i) {
channels_[i]->PushBack(append_this[i]);
}
}
@ -101,22 +103,22 @@ void AudioMultiVector::PushBackFromIndex(const AudioMultiVector& append_this,
assert(index < append_this.Size());
index = std::min(index, append_this.Size() - 1);
size_t length = append_this.Size() - index;
assert(Channels() == append_this.Channels());
if (Channels() == append_this.Channels()) {
for (size_t i = 0; i < Channels(); ++i) {
assert(num_channels_ == append_this.num_channels_);
if (num_channels_ == append_this.num_channels_) {
for (size_t i = 0; i < num_channels_; ++i) {
channels_[i]->PushBack(&append_this[i][index], length);
}
}
}
void AudioMultiVector::PopFront(size_t length) {
for (size_t i = 0; i < Channels(); ++i) {
for (size_t i = 0; i < num_channels_; ++i) {
channels_[i]->PopFront(length);
}
}
void AudioMultiVector::PopBack(size_t length) {
for (size_t i = 0; i < Channels(); ++i) {
for (size_t i = 0; i < num_channels_; ++i) {
channels_[i]->PopBack(length);
}
}
@ -138,8 +140,13 @@ size_t AudioMultiVector::ReadInterleavedFromIndex(size_t start_index,
if (length + start_index > Size()) {
length = Size() - start_index;
}
if (num_channels_ == 1) {
// Special case to avoid the nested for loop below.
memcpy(destination, &(*this)[0][start_index], length * sizeof(int16_t));
return length;
}
for (size_t i = 0; i < length; ++i) {
for (size_t channel = 0; channel < Channels(); ++channel) {
for (size_t channel = 0; channel < num_channels_; ++channel) {
destination[index] = (*this)[channel][i + start_index];
++index;
}
@ -156,12 +163,12 @@ size_t AudioMultiVector::ReadInterleavedFromEnd(size_t length,
void AudioMultiVector::OverwriteAt(const AudioMultiVector& insert_this,
size_t length,
size_t position) {
assert(Channels() == insert_this.Channels());
assert(num_channels_ == insert_this.num_channels_);
// Cap |length| at the length of |insert_this|.
assert(length <= insert_this.Size());
length = std::min(length, insert_this.Size());
if (Channels() == insert_this.Channels()) {
for (size_t i = 0; i < Channels(); ++i) {
if (num_channels_ == insert_this.num_channels_) {
for (size_t i = 0; i < num_channels_; ++i) {
channels_[i]->OverwriteAt(&insert_this[i][0], length, position);
}
}
@ -169,9 +176,9 @@ void AudioMultiVector::OverwriteAt(const AudioMultiVector& insert_this,
void AudioMultiVector::CrossFade(const AudioMultiVector& append_this,
size_t fade_length) {
assert(Channels() == append_this.Channels());
if (Channels() == append_this.Channels()) {
for (size_t i = 0; i < Channels(); ++i) {
assert(num_channels_ == append_this.num_channels_);
if (num_channels_ == append_this.num_channels_) {
for (size_t i = 0; i < num_channels_; ++i) {
channels_[i]->CrossFade(append_this[i], fade_length);
}
}
@ -185,7 +192,7 @@ size_t AudioMultiVector::Size() const {
void AudioMultiVector::AssertSize(size_t required_size) {
if (Size() < required_size) {
size_t extend_length = required_size - Size();
for (size_t channel = 0; channel < Channels(); ++channel) {
for (size_t channel = 0; channel < num_channels_; ++channel) {
channels_[channel]->Extend(extend_length);
}
}

View File

@ -106,7 +106,7 @@ class AudioMultiVector {
size_t fade_length);
// Returns the number of channels.
virtual size_t Channels() const { return channels_.size(); }
virtual size_t Channels() const { return num_channels_; }
// Returns the number of elements per channel in this AudioMultiVector.
virtual size_t Size() const;
@ -124,6 +124,7 @@ class AudioMultiVector {
protected:
std::vector<AudioVector*> channels_;
size_t num_channels_;
private:
DISALLOW_COPY_AND_ASSIGN(AudioMultiVector);

View File

@ -19,18 +19,24 @@
namespace webrtc {
void AudioVector::Clear() {
vector_.clear();
first_free_ix_ = 0;
}
void AudioVector::CopyFrom(AudioVector* copy_to) const {
if (copy_to) {
copy_to->vector_.assign(vector_.begin(), vector_.end());
copy_to->Reserve(Size());
assert(copy_to->capacity_ >= Size());
memcpy(copy_to->array_.get(), array_.get(), Size() * sizeof(int16_t));
copy_to->first_free_ix_ = first_free_ix_;
}
}
void AudioVector::PushFront(const AudioVector& prepend_this) {
vector_.insert(vector_.begin(), prepend_this.vector_.begin(),
prepend_this.vector_.end());
size_t insert_length = prepend_this.Size();
Reserve(Size() + insert_length);
memmove(&array_[insert_length], &array_[0], Size() * sizeof(int16_t));
memcpy(&array_[0], &prepend_this.array_[0], insert_length * sizeof(int16_t));
first_free_ix_ += insert_length;
}
void AudioVector::PushFront(const int16_t* prepend_this, size_t length) {
@ -39,83 +45,77 @@ void AudioVector::PushFront(const int16_t* prepend_this, size_t length) {
}
void AudioVector::PushBack(const AudioVector& append_this) {
vector_.reserve(vector_.size() + append_this.Size());
for (size_t i = 0; i < append_this.Size(); ++i) {
vector_.push_back(append_this[i]);
}
PushBack(append_this.array_.get(), append_this.Size());
}
void AudioVector::PushBack(const int16_t* append_this, size_t length) {
vector_.reserve(vector_.size() + length);
for (size_t i = 0; i < length; ++i) {
vector_.push_back(append_this[i]);
}
Reserve(Size() + length);
memcpy(&array_[first_free_ix_], append_this, length * sizeof(int16_t));
first_free_ix_ += length;
}
void AudioVector::PopFront(size_t length) {
if (length >= vector_.size()) {
if (length >= Size()) {
// Remove all elements.
vector_.clear();
Clear();
} else {
std::vector<int16_t>::iterator end_range = vector_.begin();
end_range += length;
// Erase all elements in range vector_.begin() and |end_range| (not
// including |end_range|).
vector_.erase(vector_.begin(), end_range);
size_t remaining_samples = Size() - length;
memmove(&array_[0], &array_[length], remaining_samples * sizeof(int16_t));
first_free_ix_ -= length;
}
}
void AudioVector::PopBack(size_t length) {
// Make sure that new_size is never negative (which causes wrap-around).
size_t new_size = vector_.size() - std::min(length, vector_.size());
vector_.resize(new_size);
// Never remove more than what is in the array.
length = std::min(length, Size());
first_free_ix_ -= length;
}
void AudioVector::Extend(size_t extra_length) {
vector_.insert(vector_.end(), extra_length, 0);
Reserve(Size() + extra_length);
memset(&array_[first_free_ix_], 0, extra_length * sizeof(int16_t));
first_free_ix_ += extra_length;
}
void AudioVector::InsertAt(const int16_t* insert_this,
size_t length,
size_t position) {
std::vector<int16_t>::iterator insert_position = vector_.begin();
Reserve(Size() + length);
// Cap the position at the current vector length, to be sure the iterator
// does not extend beyond the end of the vector.
position = std::min(vector_.size(), position);
insert_position += position;
// First, insert zeros at the position. This makes the vector longer (and
// invalidates the iterator |insert_position|.
vector_.insert(insert_position, length, 0);
// Write the new values into the vector.
for (size_t i = 0; i < length; ++i) {
vector_[position + i] = insert_this[i];
}
position = std::min(Size(), position);
int16_t* insert_position_ptr = &array_[position];
size_t samples_to_move = Size() - position;
memmove(insert_position_ptr + length, insert_position_ptr,
samples_to_move * sizeof(int16_t));
memcpy(insert_position_ptr, insert_this, length * sizeof(int16_t));
first_free_ix_ += length;
}
void AudioVector::InsertZerosAt(size_t length,
size_t position) {
std::vector<int16_t>::iterator insert_position = vector_.begin();
Reserve(Size() + length);
// Cap the position at the current vector length, to be sure the iterator
// does not extend beyond the end of the vector.
position = std::min(vector_.size(), position);
insert_position += position;
// Insert zeros at the position. This makes the vector longer (and
// invalidates the iterator |insert_position|.
vector_.insert(insert_position, length, 0);
position = std::min(capacity_, position);
int16_t* insert_position_ptr = &array_[position];
size_t samples_to_move = Size() - position;
memmove(insert_position_ptr + length, insert_position_ptr,
samples_to_move * sizeof(int16_t));
memset(insert_position_ptr, 0, length * sizeof(int16_t));
first_free_ix_ += length;
}
void AudioVector::OverwriteAt(const int16_t* insert_this,
size_t length,
size_t position) {
// Cap the insert position at the current vector length.
position = std::min(vector_.size(), position);
// Extend the vector if needed. (It is valid to overwrite beyond the current
// end of the vector.)
if (position + length > vector_.size()) {
Extend(position + length - vector_.size());
}
for (size_t i = 0; i < length; ++i) {
vector_[position + i] = insert_this[i];
// Cap the insert position at the current array length.
position = std::min(Size(), position);
Reserve(position + length);
memcpy(&array_[position], insert_this, length * sizeof(int16_t));
if (position + length > Size()) {
// Array was expanded.
first_free_ix_ += position + length - Size();
}
}
@ -135,7 +135,7 @@ void AudioVector::CrossFade(const AudioVector& append_this,
int alpha = 16384;
for (size_t i = 0; i < fade_length; ++i) {
alpha -= alpha_step;
vector_[position + i] = (alpha * vector_[position + i] +
array_[position + i] = (alpha * array_[position + i] +
(16384 - alpha) * append_this[i] + 8192) >> 14;
}
assert(alpha >= 0); // Verify that the slope was correct.
@ -146,11 +146,20 @@ void AudioVector::CrossFade(const AudioVector& append_this,
}
const int16_t& AudioVector::operator[](size_t index) const {
return vector_[index];
return array_[index];
}
int16_t& AudioVector::operator[](size_t index) {
return vector_[index];
return array_[index];
}
void AudioVector::Reserve(size_t n) {
if (capacity_ < n) {
scoped_ptr<int16_t[]> temp_array(new int16_t[n]);
memcpy(temp_array.get(), array_.get(), Size() * sizeof(int16_t));
array_.swap(temp_array);
capacity_ = n;
}
}
} // namespace webrtc

View File

@ -13,9 +13,8 @@
#include <string.h> // Access to size_t.
#include <vector>
#include "webrtc/system_wrappers/interface/constructor_magic.h"
#include "webrtc/system_wrappers/interface/scoped_ptr.h"
#include "webrtc/typedefs.h"
namespace webrtc {
@ -23,11 +22,18 @@ namespace webrtc {
class AudioVector {
public:
// Creates an empty AudioVector.
AudioVector() {}
AudioVector()
: array_(new int16_t[kDefaultInitialSize]),
first_free_ix_(0),
capacity_(kDefaultInitialSize) {}
// Creates an AudioVector with an initial size.
explicit AudioVector(size_t initial_size)
: vector_(initial_size, 0) {}
: array_(new int16_t[initial_size]),
first_free_ix_(initial_size),
capacity_(initial_size) {
memset(array_.get(), 0, initial_size * sizeof(int16_t));
}
virtual ~AudioVector() {}
@ -88,17 +94,24 @@ class AudioVector {
virtual void CrossFade(const AudioVector& append_this, size_t fade_length);
// Returns the number of elements in this AudioVector.
virtual size_t Size() const { return vector_.size(); }
virtual size_t Size() const { return first_free_ix_; }
// Returns true if this AudioVector is empty.
virtual bool Empty() const { return vector_.empty(); }
virtual bool Empty() const { return (first_free_ix_ == 0); }
// Accesses and modifies an element of AudioVector.
const int16_t& operator[](size_t index) const;
int16_t& operator[](size_t index);
private:
std::vector<int16_t> vector_;
static const size_t kDefaultInitialSize = 10;
void Reserve(size_t n);
scoped_ptr<int16_t[]> array_;
size_t first_free_ix_; // The first index after the last sample in array_.
// Note that this index may point outside of array_.
size_t capacity_; // Allocated number of samples in the array.
DISALLOW_COPY_AND_ASSIGN(AudioVector);
};