From e8433eb115c2018c837104d87dec5d28107b79aa Mon Sep 17 00:00:00 2001 From: "henrik.lundin@webrtc.org" Date: Tue, 12 Nov 2013 13:15:02 +0000 Subject: [PATCH] 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 --- .../audio_coding/neteq4/audio_multi_vector.cc | 55 +++++---- .../audio_coding/neteq4/audio_multi_vector.h | 3 +- .../audio_coding/neteq4/audio_vector.cc | 111 ++++++++++-------- .../audio_coding/neteq4/audio_vector.h | 27 +++-- 4 files changed, 113 insertions(+), 83 deletions(-) diff --git a/webrtc/modules/audio_coding/neteq4/audio_multi_vector.cc b/webrtc/modules/audio_coding/neteq4/audio_multi_vector.cc index d3f03a1c63..baa912c860 100644 --- a/webrtc/modules/audio_coding/neteq4/audio_multi_vector.cc +++ b/webrtc/modules/audio_coding/neteq4/audio_multi_vector.cc @@ -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); } } diff --git a/webrtc/modules/audio_coding/neteq4/audio_multi_vector.h b/webrtc/modules/audio_coding/neteq4/audio_multi_vector.h index 15dc1dc50d..2d0a749491 100644 --- a/webrtc/modules/audio_coding/neteq4/audio_multi_vector.h +++ b/webrtc/modules/audio_coding/neteq4/audio_multi_vector.h @@ -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 channels_; + size_t num_channels_; private: DISALLOW_COPY_AND_ASSIGN(AudioMultiVector); diff --git a/webrtc/modules/audio_coding/neteq4/audio_vector.cc b/webrtc/modules/audio_coding/neteq4/audio_vector.cc index 9711995fd4..cbd4616306 100644 --- a/webrtc/modules/audio_coding/neteq4/audio_vector.cc +++ b/webrtc/modules/audio_coding/neteq4/audio_vector.cc @@ -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::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::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::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 temp_array(new int16_t[n]); + memcpy(temp_array.get(), array_.get(), Size() * sizeof(int16_t)); + array_.swap(temp_array); + capacity_ = n; + } } } // namespace webrtc diff --git a/webrtc/modules/audio_coding/neteq4/audio_vector.h b/webrtc/modules/audio_coding/neteq4/audio_vector.h index d8a72ebe85..66bd518a80 100644 --- a/webrtc/modules/audio_coding/neteq4/audio_vector.h +++ b/webrtc/modules/audio_coding/neteq4/audio_vector.h @@ -13,9 +13,8 @@ #include // Access to size_t. -#include - #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 vector_; + static const size_t kDefaultInitialSize = 10; + + void Reserve(size_t n); + + scoped_ptr 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); };