Copying vs ghost-copying

Post Reply
bjkwon
Posts: 85
Joined: September 26th, 2018, 9:36 pm

Copying vs ghost-copying

Post by bjkwon » September 26th, 2019, 4:11 pm

Let's say there is a CSignals object x. And you define another object y based on x.

Code: Select all

CSignals x;
...
...
CSignals y=x; // or CSignals y(x);
Here the entire memory block for x is copied or duplicated to define y. This does no harm, if duplicating memory blocks injudiciously is considered “no harm.” While that’s how I have been coding in the past, I also haphazardly used pointers to objects whenever I felt like being mindful of the performance (i.e., speed) and memory uses.

Code: Select all

CSignals *py = &x;
In reality, that approach is not useful because if I make any modifications to py, it alters x as well. Often that’s not what I intended. While the use of

Code: Select all

const CSignals &
for a function argument prevents it from happening (i.e., protecting the content of the original object), it does not give me a solution if I really need to make some modification of py.

Here comes the idea of ghost copying. Copy/duplicate the memory block as little and re-use the existing block as much as possible, particularly the actual data buffer which could have a gigantic size. In other words, among member variables below,

Code: Select all

class body
{
public:
unsigned int nSamples;
unsigned int nGroups;
	unsigned char bufBlockSize;
	void* parg;
	union
	{
		char *strbuf;
		complex<double> *cbuf;
		double *buf;
		bool *logbuf;
	};
};
class CSignal : public body
{
public:
	double tmark;
	int fs;
};
class CTimeSeries : public CSignal
{
public:
	CTimeSeries *chain;
	bool ghost;
	vector<CTimeSeries> outarg;
};
class CSignals : public CTimeSeries
{
public:
	CTimeSeries *next;
	vector<CSignals> outarg2;
};
Copying occurs a lot throughout the code, calling one of the following implementations,

Code: Select all

body(const body& src);
body& operator=(const body& rhs);
then a new memory block is allocated replicating the content of buf every time this is called and cleaned when done. What a waste of efforts. So, let’s skip that and just make a ghost copy of rhs with the same buf (to reference the same data buffer), while all other member variables can be defined, used and cleaned as needed within the calling scope. If a CSignals object is created with ghost set true, it doesn’t bother deleting buf (it must not delete buf, because that should be done in the original object destruction).
  • This approach is an improvement (i.e., no need/desire to go back to the past). Why?
As an example, consider a fairly long audio variable which we want to play only part of, say from 1 sec time point to 58 sec time point. In the past, just to play the sound, a new CSignals object was created in the local scope and the data content between two time points were copied, then the newly crated CSignals was sent to PlayArray16, where which requires another memory allocation to make a proper memory preparation (e.g., for 8 bit, 16 bit or 32 bit, etc) to send to the sound card. This CSignals object is created and cleaned every time the sound is played. In the new approach, the new CSignals object created locally is ghost, using the original buf as is, but it has different tmark and nSamples, according to the boundary time points. That CSignals object is sent to PlayCSignals, where a new memory allocation (e.g., 16-bit) takes place using existing buf and appropriate indexing. When the new CSignals object goes out of scope, tmark and nSamples, which were used only for the scope, will be cleaned; buf will not, however, and will remain in the lifecycle of the original object.
  • This applies also to the following tasks:
  1. Saving the audio data to a file (.wav, .mp3, .aiff)
  2. Calculating the rms of the audio (for the whole duration of the screen display or only the selected range)
  3. Calculating the spectrum from the ghost object of the original
Last edited by bjkwon on September 26th, 2019, 5:05 pm, edited 2 times in total.

bjkwon
Posts: 85
Joined: September 26th, 2018, 9:36 pm

Re: CPlotDlg::GetGhost

Post by bjkwon » September 26th, 2019, 4:31 pm

Currently, ghost copying takes place only in PlotDlg.cpp, where void CPlotDlg::GetGhost(CSignals &out, CAxes* pax) gives you out, the ghost of the original object. If there’s no selection in pax, out represents the entire data in display; if a range is selected, out represents the audio data in the selected time range. GetGhost is slightly more complicated than simply handling indies according to the given time points, because there may be null portions in the middle which also apply to left or right channels separately.

(this may be out of place, but I may forget if I don’t say it here) GetGhost recognizes up to two audio signals (two CSignal objects, to be precise, because _ax ->m_ln is vector<CLine> with CLine::sig which is CSignal)—if there’s one in pax, you get a mono signal; if there are two, that’s a stereo signal. If pax is NULL, it works with registered CAxes. If there is only one registered, out will be either a mono or stereo, depending on the number of lines on the screen. If there are more than two CAxes objects registered with audio data, it will only look at up to the first two audio data (two lines) and take it as out.

Post Reply