[WIP] Implements reusable sample buffers across all components. This

produces a significant reduction in memory usage, especially for tuners
like the airspy and hackrf that produce 10/20 MSPS sample rates.  This
is observed to be around 66% reduction in peak memory usage and provides
a more stable memory utilization across time reducing stress on the
garbage collector.

Use of these reusable memory buffers will be pushed further down into
the processing chain modules to ensure we keep new object creation
to the lowest levels possible.
This commit is contained in:
Denny
2018-02-03 10:05:16 -05:00
parent fcd3c2fd46
commit f2f2c92afe
56 changed files with 1630 additions and 1255 deletions

View File

@@ -17,10 +17,10 @@ package io.github.dsheirer.source.tuner.test;
import io.github.dsheirer.dsp.mixer.IOscillator;
import io.github.dsheirer.dsp.mixer.LowPhaseNoiseOscillator;
import io.github.dsheirer.sample.Broadcaster;
import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.sample.complex.ComplexBuffer;
import io.github.dsheirer.sample.complex.TimestampedComplexBuffer;
import io.github.dsheirer.sample.complex.reusable.ReusableBufferBroadcaster;
import io.github.dsheirer.sample.complex.reusable.ReusableBufferQueue;
import io.github.dsheirer.sample.complex.reusable.ReusableComplexBuffer;
import io.github.dsheirer.util.ThreadPool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -32,7 +32,7 @@ public class SampleGenerator
{
private final static Logger mLog = LoggerFactory.getLogger(SampleGenerator.class);
private Broadcaster<ComplexBuffer> mComplexBufferBroadcaster = new Broadcaster<>();
private ReusableBufferBroadcaster mComplexBufferBroadcaster = new ReusableBufferBroadcaster();
private IOscillator mOscillator;
private int mSweepUpdateInterval;
private long mInterval;
@@ -79,21 +79,6 @@ public class SampleGenerator
this(sampleRate, frequency, interval, 0);
}
/**
* Instructs the sample generator to reuse the same sample buffer each time instead of generating new samples for
* each and every buffer. This is useful when you simply want to generate data to test or profile data flows and
* are not concerned about the content of the buffers.
*
* The generator will create a new buffer of data each time that it detects that the sample size changes or when
* the tone frequency changes.
*
* @param reuseBuffers set to true to turn on buffer reuse
*/
public void setReuseBuffers(boolean reuseBuffers)
{
mReuseBuffers = reuseBuffers;
}
/**
* Updates the number of complex samples generated per interval
*/
@@ -139,7 +124,7 @@ public class SampleGenerator
*
* @param listener to receive complex sample buffers
*/
public void addListener(Listener<ComplexBuffer> listener)
public void addListener(Listener<ReusableComplexBuffer> listener)
{
mComplexBufferBroadcaster.addListener(listener);
@@ -152,7 +137,7 @@ public class SampleGenerator
/**
* Removes the listener and stops the sample generator if there are no more listeners.
*/
public void removeListener(Listener<ComplexBuffer> listener)
public void removeListener(Listener<ReusableComplexBuffer> listener)
{
mComplexBufferBroadcaster.removeListener(listener);
@@ -200,31 +185,18 @@ public class SampleGenerator
public class Generator implements Runnable
{
private int mTriggerInterval = 0;
private long mOscillatorFrequency = 1;
private ComplexBuffer mReusableBuffer;
private ReusableBufferQueue mReusableBufferQueue = new ReusableBufferQueue("** Sample Generator **");
@Override
public void run()
{
if(mComplexBufferBroadcaster.hasListeners())
{
if(mReuseBuffers)
{
if(mReusableBuffer == null ||
mReusableBuffer.getSamples().length != mSamplesPerInterval ||
mOscillatorFrequency != mOscillator.getFrequency())
{
mOscillatorFrequency = (long)mOscillator.getFrequency();
mReusableBuffer = new TimestampedComplexBuffer(mOscillator.generateComplex(mSamplesPerInterval));
}
ReusableComplexBuffer reusableComplexBuffer = mReusableBufferQueue.getBuffer(mSamplesPerInterval);
mComplexBufferBroadcaster.receive(mReusableBuffer);
}
else
{
float[] samples = mOscillator.generateComplex(mSamplesPerInterval);
mComplexBufferBroadcaster.receive(new TimestampedComplexBuffer(samples));
}
mOscillator.generateComplex(reusableComplexBuffer);
mComplexBufferBroadcaster.broadcast(reusableComplexBuffer.incrementUserCount());
if(mSweepUpdateInterval != 0)
{

View File

@@ -41,9 +41,6 @@ public class TestTuner extends Tuner
/**
* Returns the tuner controller cast as a test tuner controller.
*
* Note: this is a temporary method until the tuner controller interface can be updated with the start(listener)
* and stop() methods.
*/
public TestTunerController getTunerController()
{

View File

@@ -19,7 +19,7 @@
package io.github.dsheirer.source.tuner.test;
import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.sample.complex.ComplexBuffer;
import io.github.dsheirer.sample.complex.reusable.ReusableComplexBuffer;
import io.github.dsheirer.source.SourceException;
import io.github.dsheirer.source.tuner.TunerController;
import io.github.dsheirer.source.tuner.configuration.TunerConfiguration;
@@ -34,7 +34,7 @@ public class TestTunerController extends TunerController
public static final long MAXIMUM_FREQUENCY = 1000000000l;
public static final int SAMPLE_RATE = 800000;
public static final int DC_NOISE_BANDWIDTH = 0;
public static final double USABLE_BANDWIDTH = 1.00;
public static final double USABLE_BANDWIDTH_PERCENTAGE = 1.00;
public static final int SPECTRAL_FRAME_RATE = 20;
public static final long SAMPLE_GENERATION_INTERVAL = 1000 / SPECTRAL_FRAME_RATE;
@@ -47,7 +47,7 @@ public class TestTunerController extends TunerController
*/
public TestTunerController()
{
super(MINIMUM_FREQUENCY, MAXIMUM_FREQUENCY, DC_NOISE_BANDWIDTH, USABLE_BANDWIDTH);
super(MINIMUM_FREQUENCY, MAXIMUM_FREQUENCY, DC_NOISE_BANDWIDTH, USABLE_BANDWIDTH_PERCENTAGE);
int sweepRate = 0; //Hz per interval
long initialToneFrequency = SAMPLE_RATE / 2 + 100;
@@ -65,25 +65,14 @@ public class TestTunerController extends TunerController
}
}
/**
* Sets the sample generator to reuse buffers instead of generating new buffers each time. Enable this setting
* when you simply want to test/profile buffer data flows for consumer processes.
*
* @param reuseBuffers set to true to turn on buffer reuse
*/
public void setReuseBuffers(boolean reuseBuffers)
{
mSampleGenerator.setReuseBuffers(reuseBuffers);
}
@Override
public void addComplexBufferListener(Listener<ComplexBuffer> listener)
public void addBufferListener(Listener<ReusableComplexBuffer> listener)
{
mSampleGenerator.addListener(listener);
}
@Override
public void removeComplexBufferListener(Listener<ComplexBuffer> listener)
public void removeBufferListener(Listener<ReusableComplexBuffer> listener)
{
mSampleGenerator.removeListener(listener);
}