1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-06-29 22:58:39 +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 ? 0
: std::min(mid.width, (int)(zoomInfo.GetFisheyeRightBoundary(-leftOffset))); : std::min(mid.width, (int)(zoomInfo.GetFisheyeRightBoundary(-leftOffset)));
const size_t numPixels = std::max(0, end - begin); const size_t numPixels = std::max(0, end - begin);
const size_t zeroPaddingFactor = settings.ZeroPaddingFactor();
SpecCache specCache SpecCache specCache;
(numPixels, settings.algorithm, -1,
t0, settings.windowType, // need explicit resize since specCache.where[] accessed before Populate()
settings.WindowSize(), zeroPaddingFactor, settings.frequencyGain); specCache.Grow(numPixels, settings, -1, t0);
if (numPixels > 0) { if (numPixels > 0) {
for (int ii = begin; ii < end; ++ii) { for (int ii = begin; ii < end; ++ii) {
const double time = zoomInfo.PositionToTime(ii, -leftOffset) - tOffset; const double time = zoomInfo.PositionToTime(ii, -leftOffset) - tOffset;

View File

@ -47,12 +47,6 @@
#ifdef _OPENMP #ifdef _OPENMP
#include <omp.h> #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 #endif
class WaveCache { class WaveCache {
@ -824,7 +818,7 @@ bool SpecCache::Matches
bool SpecCache::CalculateOneSpectrum bool SpecCache::CalculateOneSpectrum
(const SpectrogramSettings &settings, (const SpectrogramSettings &settings,
WaveTrackCache &waveTrackCache, WaveTrackCache &waveTrackCache,
int xx, sampleCount numSamples, const int xx, const sampleCount numSamples,
double offset, double rate, double pixelsPerSecond, double offset, double rate, double pixelsPerSecond,
int lowerBoundX, int upperBoundX, int lowerBoundX, int upperBoundX,
const std::vector<float> &gainFactors, const std::vector<float> &gainFactors,
@ -1043,14 +1037,27 @@ bool SpecCache::CalculateOneSpectrum
return result; return result;
} }
void SpecCache::Allocate(const SpectrogramSettings &settings) void SpecCache::Grow(size_t len_, const SpectrogramSettings& settings,
double pixelsPerSecond, double start_)
{ {
settings.CacheWindows(); settings.CacheWindows();
// len columns, and so many rows, column-major. // len columns, and so many rows, column-major.
// Don't take column literally -- this isn't pixel data yet, it's the // Don't take column literally -- this isn't pixel data yet, it's the
// raw data to be mapped onto the display. // 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 void SpecCache::Populate
@ -1059,8 +1066,6 @@ void SpecCache::Populate
sampleCount numSamples, sampleCount numSamples,
double offset, double rate, double pixelsPerSecond) double offset, double rate, double pixelsPerSecond)
{ {
Allocate( settings );
const int &frequencyGain = settings.frequencyGain; const int &frequencyGain = settings.frequencyGain;
const size_t windowSize = settings.WindowSize(); const size_t windowSize = settings.WindowSize();
const bool autocorrelation = const bool autocorrelation =
@ -1167,7 +1172,6 @@ void SpecCache::Populate
#endif #endif
for (auto xx = lowerBoundX; xx < upperBoundX; ++xx) { for (auto xx = lowerBoundX; xx < upperBoundX; ++xx) {
float *const results = &freq[nBins * xx]; float *const results = &freq[nBins * xx];
const auto hFFT = settings.hFFT.get();
for (size_t ii = 0; ii < nBins; ++ii) { for (size_t ii = 0; ii < nBins; ++ii) {
float &power = results[ii]; float &power = results[ii];
if (power <= 0) if (power <= 0)
@ -1191,18 +1195,8 @@ bool WaveClip::GetSpectrogram(WaveTrackCache &waveTrackCache,
size_t numPixels, size_t numPixels,
double t0, double pixelsPerSecond) const double t0, double pixelsPerSecond) const
{ {
BEGIN_TASK_PROFILING("GetSpectrogram");
const WaveTrack *const track = waveTrackCache.GetTrack(); const WaveTrack *const track = waveTrackCache.GetTrack();
const SpectrogramSettings &settings = track->GetSpectrogramSettings(); 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 = bool match =
mSpecCache && mSpecCache &&
@ -1216,17 +1210,25 @@ bool WaveClip::GetSpectrogram(WaveTrackCache &waveTrackCache,
spectrogram = &mSpecCache->freq[0]; spectrogram = &mSpecCache->freq[0];
where = &mSpecCache->where[0]; where = &mSpecCache->where[0];
END_TASK_PROFILING("GetSpectrogram");
return false; //hit cache completely return false; //hit cache completely
} }
if (settings.algorithm == SpectrogramSettings::algReassignment)
// Caching is not implemented for reassignment, unless for // Caching is not implemented for reassignment, unless for
// a complete hit, because of the complications of time reassignment // a complete hit, because of the complications of time reassignment
if (settings.algorithm == SpectrogramSettings::algReassignment)
match = false; 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 tstep = 1.0 / pixelsPerSecond;
const double samplesPerPixel = mRate * tstep; const double samplesPerPixel = mRate * tstep;
@ -1236,7 +1238,7 @@ bool WaveClip::GetSpectrogram(WaveTrackCache &waveTrackCache,
int copyBegin = 0, copyEnd = 0; int copyBegin = 0, copyEnd = 0;
if (match) { if (match) {
findCorrection(oldCache->where, oldCache->len, numPixels, findCorrection(mSpecCache->where, mSpecCache->len, numPixels,
t0, mRate, samplesPerPixel, t0, mRate, samplesPerPixel,
oldX0, correction); oldX0, correction);
// Remember our first pixel maps to oldX0 in the old cache, // 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? // For what range of pixels can data be copied?
copyBegin = std::min((int)numPixels, std::max(0, -oldX0)); copyBegin = std::min((int)numPixels, std::max(0, -oldX0));
copyEnd = std::min((int)numPixels, std::max(0, copyEnd = std::min((int)numPixels, std::max(0,
(int)oldCache->len - oldX0 (int)mSpecCache->len - oldX0
)); ));
} }
if (!(copyEnd > copyBegin)) // Resize the cache, keep the contents unchanged.
oldCache.reset(0); mSpecCache->Grow(numPixels, settings, pixelsPerSecond, t0);
auto nBins = settings.NBins();
mSpecCache = std::make_unique<SpecCache>( // Optimization: if the old cache is good and overlaps
numPixels, settings.algorithm, pixelsPerSecond, t0, // with the current one, re-use as much of the cache as
windowType, windowSize, zeroPaddingFactor, frequencyGain); // 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 // purposely offset the display 1/2 sample to the left (as compared
// to waveform display) to properly center response of the FFT // to waveform display) to properly center response of the FFT
fillWhere(mSpecCache->where, numPixels, 0.5, correction, fillWhere(mSpecCache->where, numPixels, 0.5, correction,
t0, mRate, samplesPerPixel); 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 mSpecCache->Populate
(settings, waveTrackCache, copyBegin, copyEnd, numPixels, (settings, waveTrackCache, copyBegin, copyEnd, numPixels,
mSequence->GetNumSamples(), mSequence->GetNumSamples(),
mOffset, mRate, pixelsPerSecond); mOffset, mRate, pixelsPerSecond);
END_TASK_PROFILING("Populate");
mSpecCache->dirty = mDirty; mSpecCache->dirty = mDirty;
spectrogram = &mSpecCache->freq[0]; spectrogram = &mSpecCache->freq[0];
where = &mSpecCache->where[0]; where = &mSpecCache->where[0];
END_TASK_PROFILING("GetSpectrogram");
return true; return true;
} }

View File

@ -46,36 +46,10 @@ public:
, start(-1.0) , start(-1.0)
, windowType(-1) , windowType(-1)
, frequencyGain(-1) , frequencyGain(-1)
#if 0
, freq(NULL)
, where(NULL)
#endif
, dirty(-1) , 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() ~SpecCache()
{ {
} }
@ -83,32 +57,36 @@ public:
bool Matches(int dirty_, double pixelsPerSecond, bool Matches(int dirty_, double pixelsPerSecond,
const SpectrogramSettings &settings, double rate) const; const SpectrogramSettings &settings, double rate) const;
// Calculate one column of the spectrum
bool CalculateOneSpectrum bool CalculateOneSpectrum
(const SpectrogramSettings &settings, (const SpectrogramSettings &settings,
WaveTrackCache &waveTrackCache, WaveTrackCache &waveTrackCache,
int xx, sampleCount numSamples, const int xx, sampleCount numSamples,
double offset, double rate, double pixelsPerSecond, double offset, double rate, double pixelsPerSecond,
int lowerBoundX, int upperBoundX, int lowerBoundX, int upperBoundX,
const std::vector<float> &gainFactors, const std::vector<float> &gainFactors,
float* __restrict scratch, float* __restrict scratch,
float* __restrict out) const; 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 void Populate
(const SpectrogramSettings &settings, WaveTrackCache &waveTrackCache, (const SpectrogramSettings &settings, WaveTrackCache &waveTrackCache,
int copyBegin, int copyEnd, size_t numPixels, int copyBegin, int copyEnd, size_t numPixels,
sampleCount numSamples, sampleCount numSamples,
double offset, double rate, double pixelsPerSecond); double offset, double rate, double pixelsPerSecond);
const size_t len { 0 }; // counts pixels, not samples size_t len { 0 }; // counts pixels, not samples
const int algorithm; int algorithm;
const double pps; double pps;
const double start; double start;
const int windowType; int windowType;
const size_t windowSize { 0 }; size_t windowSize { 0 };
const unsigned zeroPaddingFactor { 0 }; unsigned zeroPaddingFactor { 0 };
const int frequencyGain; int frequencyGain;
std::vector<float> freq; std::vector<float> freq;
std::vector<sampleCount> where; std::vector<sampleCount> where;