1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-06-29 06:38:38 +02:00

Speccache performance improvments

Resolve #191 issues

- Disable reassignment cache
- Free memory when reasonable
- Add assertion
- Remove unused variables
- Add some comments
This commit is contained in:
Darrell Walisser 2017-03-22 11:17:58 -04:00 committed by Paul Licameli
parent b6a1ca916b
commit 29df7e1ce3
3 changed files with 82 additions and 93 deletions

View File

@ -2447,11 +2447,12 @@ void TrackArtist::DrawClipSpectrum(WaveTrackCache &waveTrackCache,
? 0
: std::min(mid.width, (int)(zoomInfo.GetFisheyeRightBoundary(-leftOffset)));
const size_t numPixels = std::max(0, end - begin);
const size_t zeroPaddingFactor = settings.ZeroPaddingFactor();
SpecCache specCache
(numPixels, settings.algorithm, -1,
t0, settings.windowType,
settings.WindowSize(), zeroPaddingFactor, settings.frequencyGain);
SpecCache specCache;
// need explicit resize since specCache.where[] accessed before Populate()
specCache.Grow(numPixels, settings, -1, t0);
if (numPixels > 0) {
for (int ii = begin; ii < end; ++ii) {
const double time = zoomInfo.PositionToTime(ii, -leftOffset) - tOffset;

View File

@ -47,12 +47,6 @@
#ifdef _OPENMP
#include <omp.h>
#else
// Comment this out if you want to profile non OpenMP builds too.
#undef BEGIN_TASK_PROFILING
#undef END_TASK_PROFILING
#define BEGIN_TASK_PROFILING(TASK_DESCRIPTION)
#define END_TASK_PROFILING(TASK_DESCRIPTION)
#endif
class WaveCache {
@ -824,7 +818,7 @@ bool SpecCache::Matches
bool SpecCache::CalculateOneSpectrum
(const SpectrogramSettings &settings,
WaveTrackCache &waveTrackCache,
int xx, sampleCount numSamples,
const int xx, const sampleCount numSamples,
double offset, double rate, double pixelsPerSecond,
int lowerBoundX, int upperBoundX,
const std::vector<float> &gainFactors,
@ -1043,14 +1037,27 @@ bool SpecCache::CalculateOneSpectrum
return result;
}
void SpecCache::Allocate(const SpectrogramSettings &settings)
void SpecCache::Grow(size_t len_, const SpectrogramSettings& settings,
double pixelsPerSecond, double start_)
{
settings.CacheWindows();
// len columns, and so many rows, column-major.
// Don't take column literally -- this isn't pixel data yet, it's the
// raw data to be mapped onto the display.
freq.resize(len * settings.NBins());
freq.resize(len_ * settings.NBins());
// Sample counts corresponding to the columns, and to one past the end.
where.resize(len_ + 1);
len = len_;
algorithm = settings.algorithm;
pps = pixelsPerSecond;
start = start_;
windowType = settings.windowType;
windowSize = settings.WindowSize();
zeroPaddingFactor = settings.ZeroPaddingFactor();
frequencyGain = settings.frequencyGain;
}
void SpecCache::Populate
@ -1059,8 +1066,6 @@ void SpecCache::Populate
sampleCount numSamples,
double offset, double rate, double pixelsPerSecond)
{
Allocate( settings );
const int &frequencyGain = settings.frequencyGain;
const size_t windowSize = settings.WindowSize();
const bool autocorrelation =
@ -1167,7 +1172,6 @@ void SpecCache::Populate
#endif
for (auto xx = lowerBoundX; xx < upperBoundX; ++xx) {
float *const results = &freq[nBins * xx];
const auto hFFT = settings.hFFT.get();
for (size_t ii = 0; ii < nBins; ++ii) {
float &power = results[ii];
if (power <= 0)
@ -1191,18 +1195,8 @@ bool WaveClip::GetSpectrogram(WaveTrackCache &waveTrackCache,
size_t numPixels,
double t0, double pixelsPerSecond) const
{
BEGIN_TASK_PROFILING("GetSpectrogram");
const WaveTrack *const track = waveTrackCache.GetTrack();
const SpectrogramSettings &settings = track->GetSpectrogramSettings();
const int &frequencyGain = settings.frequencyGain;
const size_t windowSize = settings.WindowSize();
const int &windowType = settings.windowType;
#ifdef EXPERIMENTAL_ZERO_PADDED_SPECTROGRAMS
const size_t zeroPaddingFactor = settings.ZeroPaddingFactor();
#else
const size_t zeroPaddingFactor = 1;
#endif
bool match =
mSpecCache &&
@ -1216,17 +1210,25 @@ bool WaveClip::GetSpectrogram(WaveTrackCache &waveTrackCache,
spectrogram = &mSpecCache->freq[0];
where = &mSpecCache->where[0];
END_TASK_PROFILING("GetSpectrogram");
return false; //hit cache completely
}
// Caching is not implemented for reassignment, unless for
// a complete hit, because of the complications of time reassignment
if (settings.algorithm == SpectrogramSettings::algReassignment)
// Caching is not implemented for reassignment, unless for
// a complete hit, because of the complications of time reassignment
match = false;
std::unique_ptr<SpecCache> oldCache(std::move(mSpecCache));
// Free the cache when it won't cause a major stutter.
// If the window size changed, we know there is nothing to be copied
// If we zoomed out, or resized, we can give up memory. But not too much -
// up to 2x extra is needed at the end of the clip to prevent stutter.
if (mSpecCache->freq.capacity() > 2.1 * mSpecCache->freq.size() ||
mSpecCache->windowSize*mSpecCache->zeroPaddingFactor <
settings.WindowSize()*settings.ZeroPaddingFactor())
{
match = false;
mSpecCache = std::make_unique<SpecCache>();
}
const double tstep = 1.0 / pixelsPerSecond;
const double samplesPerPixel = mRate * tstep;
@ -1236,7 +1238,7 @@ bool WaveClip::GetSpectrogram(WaveTrackCache &waveTrackCache,
int copyBegin = 0, copyEnd = 0;
if (match) {
findCorrection(oldCache->where, oldCache->len, numPixels,
findCorrection(mSpecCache->where, mSpecCache->len, numPixels,
t0, mRate, samplesPerPixel,
oldX0, correction);
// Remember our first pixel maps to oldX0 in the old cache,
@ -1244,49 +1246,57 @@ bool WaveClip::GetSpectrogram(WaveTrackCache &waveTrackCache,
// For what range of pixels can data be copied?
copyBegin = std::min((int)numPixels, std::max(0, -oldX0));
copyEnd = std::min((int)numPixels, std::max(0,
(int)oldCache->len - oldX0
(int)mSpecCache->len - oldX0
));
}
if (!(copyEnd > copyBegin))
oldCache.reset(0);
// Resize the cache, keep the contents unchanged.
mSpecCache->Grow(numPixels, settings, pixelsPerSecond, t0);
auto nBins = settings.NBins();
mSpecCache = std::make_unique<SpecCache>(
numPixels, settings.algorithm, pixelsPerSecond, t0,
windowType, windowSize, zeroPaddingFactor, frequencyGain);
// Optimization: if the old cache is good and overlaps
// with the current one, re-use as much of the cache as
// possible
if (copyEnd > copyBegin)
{
// memmove is required since dst/src overlap
memmove(&mSpecCache->freq[nBins * copyBegin],
&mSpecCache->freq[nBins * (copyBegin + oldX0)],
nBins * (copyEnd - copyBegin) * sizeof(float));
}
// Reassignment accumulates, so it needs a zeroed buffer
if (settings.algorithm == SpectrogramSettings::algReassignment)
{
// The cache could theoretically copy from the middle, resulting
// in two regions to update. This won't happen in zoom, since
// old cache doesn't match. It won't happen in resize, since the
// spectrum view is pinned to left side of window.
wxASSERT(
(copyBegin >= 0 && copyEnd == numPixels) || // copied the end
(copyBegin == 0 && copyEnd <= numPixels) // copied the beginning
);
int zeroBegin = copyBegin > 0 ? 0 : copyEnd-copyBegin;
int zeroEnd = copyBegin > 0 ? copyBegin : numPixels;
memset(&mSpecCache->freq[nBins*zeroBegin], 0, nBins*(zeroEnd-zeroBegin)*sizeof(float));
}
// purposely offset the display 1/2 sample to the left (as compared
// to waveform display) to properly center response of the FFT
fillWhere(mSpecCache->where, numPixels, 0.5, correction,
t0, mRate, samplesPerPixel);
// Optimization: if the old cache is good and overlaps
// with the current one, re-use as much of the cache as
// possible
if (oldCache) {
mSpecCache->Allocate(settings);
auto nBins = settings.NBins();
memcpy(&mSpecCache->freq[nBins * copyBegin],
&oldCache->freq[nBins * (copyBegin + oldX0)],
nBins * (copyEnd - copyBegin) * sizeof(float));
}
BEGIN_TASK_PROFILING("Populate");
auto nBins = settings.NBins();
mSpecCache->Populate
(settings, waveTrackCache, copyBegin, copyEnd, numPixels,
mSequence->GetNumSamples(),
mOffset, mRate, pixelsPerSecond);
END_TASK_PROFILING("Populate");
mSpecCache->dirty = mDirty;
spectrogram = &mSpecCache->freq[0];
where = &mSpecCache->where[0];
END_TASK_PROFILING("GetSpectrogram");
return true;
}

View File

@ -46,36 +46,10 @@ public:
, start(-1.0)
, windowType(-1)
, frequencyGain(-1)
#if 0
, freq(NULL)
, where(NULL)
#endif
, dirty(-1)
{
}
// Make valid cache, to be filled in
SpecCache(size_t cacheLen, int algorithm_,
double pps_, double start_, int windowType_, size_t windowSize_,
unsigned zeroPaddingFactor_, int frequencyGain_)
: len(cacheLen)
, algorithm(algorithm_)
, pps(pps_)
, start(start_)
, windowType(windowType_)
, windowSize(windowSize_)
, zeroPaddingFactor(zeroPaddingFactor_)
, frequencyGain(frequencyGain_)
, freq{}
// Sample counts corresponding to the columns, and to one past the end.
, where(len + 1)
, dirty(-1)
{
where[0] = 0;
}
~SpecCache()
{
}
@ -83,32 +57,36 @@ public:
bool Matches(int dirty_, double pixelsPerSecond,
const SpectrogramSettings &settings, double rate) const;
// Calculate one column of the spectrum
bool CalculateOneSpectrum
(const SpectrogramSettings &settings,
WaveTrackCache &waveTrackCache,
int xx, sampleCount numSamples,
const int xx, sampleCount numSamples,
double offset, double rate, double pixelsPerSecond,
int lowerBoundX, int upperBoundX,
const std::vector<float> &gainFactors,
float* __restrict scratch,
float* __restrict out) const;
void Allocate(const SpectrogramSettings &settings);
// Grow the cache while preserving the (possibly now invalid!) contents
void Grow(size_t len_, const SpectrogramSettings& settings,
double pixelsPerSecond, double start_);
// Calculate the dirty columns at the begin and end of the cache
void Populate
(const SpectrogramSettings &settings, WaveTrackCache &waveTrackCache,
int copyBegin, int copyEnd, size_t numPixels,
sampleCount numSamples,
double offset, double rate, double pixelsPerSecond);
const size_t len { 0 }; // counts pixels, not samples
const int algorithm;
const double pps;
const double start;
const int windowType;
const size_t windowSize { 0 };
const unsigned zeroPaddingFactor { 0 };
const int frequencyGain;
size_t len { 0 }; // counts pixels, not samples
int algorithm;
double pps;
double start;
int windowType;
size_t windowSize { 0 };
unsigned zeroPaddingFactor { 0 };
int frequencyGain;
std::vector<float> freq;
std::vector<sampleCount> where;