Bug 815643 - Part 6: Optimize FFTBlock to avoid recreating kiss_fftr_cfg objects all the time; r=roc
authorEhsan Akhgari <ehsan@mozilla.com>
Mon, 10 Jun 2013 16:09:25 -0400
changeset 134590 22d7a1784228
parent 134589 90c849ba5baf
child 134591 7cb2712f0237
push id29285
push usereakhgari@mozilla.com
push dateTue, 11 Jun 2013 00:10:14 +0000
treeherdermozilla-inbound@63386b71d1b5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs815643
milestone24.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 815643 - Part 6: Optimize FFTBlock to avoid recreating kiss_fftr_cfg objects all the time; r=roc Setting up the FFT tables turns out to be quite expensive because of the large number of calls to sin() and cos(). In the future we may want to look into having a global cache of FFT tables per size, in order to pay the cost of setting each one of them up only once.
content/media/webaudio/FFTBlock.h
--- a/content/media/webaudio/FFTBlock.h
+++ b/content/media/webaudio/FFTBlock.h
@@ -14,33 +14,38 @@
 namespace mozilla {
 
 // This class defines an FFT block, loosely modeled after Blink's FFTFrame
 // class to make sharing code with Blink easy.
 // Currently it's implemented on top of KissFFT on all platforms.
 class FFTBlock {
 public:
   explicit FFTBlock(uint32_t aFFTSize)
-    : mFFTSize(aFFTSize)
+    : mFFT(nullptr)
+    , mIFFT(nullptr)
+    , mFFTSize(aFFTSize)
   {
     mOutputBuffer.SetLength(aFFTSize / 2 + 1);
     PodZero(mOutputBuffer.Elements(), aFFTSize / 2 + 1);
   }
+  ~FFTBlock()
+  {
+    free(mFFT);
+    free(mIFFT);
+  }
 
   void PerformFFT(const float* aData)
   {
-    kiss_fftr_cfg fft = kiss_fftr_alloc(mFFTSize, 0, nullptr, nullptr);
-    kiss_fftr(fft, aData, mOutputBuffer.Elements());
-    free(fft);
+    EnsureFFT();
+    kiss_fftr(mFFT, aData, mOutputBuffer.Elements());
   }
-  void PerformInverseFFT(float* aData) const
+  void PerformInverseFFT(float* aData)
   {
-    kiss_fftr_cfg fft = kiss_fftr_alloc(mFFTSize, 1, nullptr, nullptr);
-    kiss_fftri(fft, mOutputBuffer.Elements(), aData);
-    free(fft);
+    EnsureIFFT();
+    kiss_fftri(mIFFT, mOutputBuffer.Elements(), aData);
     for (uint32_t i = 0; i < mFFTSize; ++i) {
       aData[i] /= mFFTSize;
     }
   }
   void Multiply(const FFTBlock& aFrame)
   {
     BufferComplexMultiply(reinterpret_cast<const float*>(mOutputBuffer.Elements()),
                           reinterpret_cast<const float*>(aFrame.mOutputBuffer.Elements()),
@@ -58,32 +63,48 @@ public:
     PerformFFT(paddedData.Elements());
   }
 
   void SetFFTSize(uint32_t aSize)
   {
     mFFTSize = aSize;
     mOutputBuffer.SetLength(aSize / 2 + 1);
     PodZero(mOutputBuffer.Elements(), aSize / 2 + 1);
+    mFFT = mIFFT = nullptr;
   }
 
   uint32_t FFTSize() const
   {
     return mFFTSize;
   }
   float RealData(uint32_t aIndex) const
   {
     return mOutputBuffer[aIndex].r;
   }
   float ImagData(uint32_t aIndex) const
   {
     return mOutputBuffer[aIndex].i;
   }
 
 private:
+  void EnsureFFT()
+  {
+    if (!mFFT) {
+      mFFT = kiss_fftr_alloc(mFFTSize, 0, nullptr, nullptr);
+    }
+  }
+  void EnsureIFFT()
+  {
+    if (!mIFFT) {
+      mIFFT = kiss_fftr_alloc(mFFTSize, 1, nullptr, nullptr);
+    }
+  }
+
+private:
+  kiss_fftr_cfg mFFT, mIFFT;
   nsTArray<kiss_fft_cpx> mOutputBuffer;
   uint32_t mFFTSize;
 };
 
 }
 
 #endif