【发布时间】:2019-01-06 18:44:03
【问题描述】:
下面的代码是我正在尝试做的演示,它与我的原始代码存在相同的问题(此处未包含)。我有频谱图代码,我正在尝试通过使用多个线程来提高其性能(我的计算机有 4 个内核)。频谱图代码基本上计算了许多重叠帧上的 FFT(这些帧对应于特定时间的声音样本)。
例如,假设我们有 1000 个帧,它们重叠了 50%。 如果我们使用 4 个线程,那么每个线程应该处理 250 帧。重叠帧只是意味着如果我们的帧长度为 1024 个样本,则第一个 帧的范围为 0-1023,第二帧为 512-1535,第三帧为 1024-2047 等(512 个样本的重叠)。
创建和使用线程的代码
void __fastcall TForm1::Button1Click(TObject *Sender)
{
numThreads = 4;
fftLen = 1024;
numWindows = 10000;
int startTime = GetTickCount();
numOverlappingWindows = numWindows*2;
overlap = fftLen/2;
const unsigned numElem = fftLen*numWindows+overlap;
rx = new float[numElem];
for(int i=0; i<numElem; i++) {
rx[i] = rand();
}
useThreads = true;
vWThread.reserve(numOverlappingWindows);
if(useThreads){
for(int i=0;i<numThreads;i++){
TWorkerThread *pWorkerThread = new TWorkerThread(true);
pWorkerThread->SetWorkerMethodCallback(&CalculateWindowFFTs);//this is called in TWorkerThread::Execute
vWThread.push_back(pWorkerThread);
}
pLock = new TCriticalSection();
for(int i=0;i<numThreads;i++){ //start the threads
vWThread.at(i)->Resume();
}
while(TWorkerThread::GetNumThreads()>0);
}else CalculateWindowFFTs();
int endTime = GetTickCount();
Label1->Caption = IntToStr(endTime-startTime);
}
void TForm1::CalculateWindowFFTs(){
unsigned startWnd = 0, endWnd = numOverlappingWindows, threadId;
if(useThreads){
threadId = TWorkerThread::GetCurrentThreadId();
unsigned wndPerThread = numOverlappingWindows/numThreads;
startWnd = (threadId-1)*wndPerThread;
endWnd = threadId*wndPerThread;
if(numThreads==threadId){
endWnd = numOverlappingWindows;
}
}
float *pReal, *pImg;
for(unsigned i=startWnd; i<endWnd; i++){
pReal = new float[fftLen];
pImg = new float[fftLen];
memcpy(pReal, &rx[i*overlap], fftLen*sizeof(float));
memset(pImg, '0', fftLen);
FFT(pReal, pImg, fftLen); //perform an in place FFT
pLock->Acquire();
vWndFFT.push_back(pReal);
vWndFFT.push_back(pImg);
pLock->Release();
}
}
void TForm1::FFT(float *rx, float *ix, int fftSize)
{
int i, j, k, m;
float rxt, ixt;
m = log(fftSize)/log(2);
int fftSizeHalf = fftSize/2;
j = k = fftSizeHalf;
for (i = 1; i < (fftSize-1); i++){
if (i < j) {
rxt = rx[j];
ixt = ix[j];
rx[j] = rx[i];
ix[j] = ix[i];
rx[i] = rxt;
ix[i] = ixt;
}
k = fftSizeHalf;
while (k <= j){
j = j - k;
k = k/2;
}
j = j + k;
} //end for
int le, le2, l, ip;
float sr, si, ur, ui;
for (k = 1; k <= m; k++) {
le = pow(2, k);
le2 = le/2;
ur = 1;
ui = 0;
sr = cos(PI/le2);
si = -sin(PI/le2);
for (j = 1; j <= le2; j++) {
l = j - 1;
for (i = l; i < fftSize; i += le) {
ip = i + le2;
rxt = rx[ip] * ur - ix[ip] * ui;
ixt = rx[ip] * ui + ix[ip] * ur;
rx[ip] = rx[i] - rxt;
ix[ip] = ix[i] - ixt;
rx[i] = rx[i] + rxt;
ix[i] = ix[i] + ixt;
} //end for
rxt = ur;
ur = rxt * sr - ui * si;
ui = rxt * si + ui * sr;
}
}
}
虽然很容易将此进程划分为多个线程,但与单线程版本相比,性能仅略有提高 (
起初我认为性能不佳的主要原因是写入向量对象的锁定,所以我尝试了一个向量数组(a 每个线程的向量),从而消除了对锁的需求,但性能几乎保持不变。
pVfft = new vector<float*>[numThreads];//create an array of vectors
//and then in CalculateWindowFFTs, do something like
vector<float*> &vThr = pVfft[threadId-1];
for(unsigned i=startWnd; i<endWnd; i++){
pReal = new float[fftLen];
pImg = new float[fftLen];
memcpy(pReal, &rx[i*overlap], fftLen*sizeof(float));
memset(pImg, '0', fftLen);
FFT(pReal, pImg, fftLen); //perform an in place FFT
vThr.push_back(pReal);
}
我想我在这里遇到了缓存问题,尽管我不确定如何更改我的设计以得到一个可扩展的解决方案。
如果您认为这很重要,我也可以提供 TWorkerThread 的代码。
非常感谢任何帮助。
谢谢
更新:
正如1201ProgramAlarm 所建议的那样,我删除了该while 循环并在我的系统上提高了大约15-20% 的速度。现在我的主线程并没有主动等待线程完成,而是在所有工作线程完成后(即当numThreads 达到 0 时),我通过 TThread::Synchronize 在主线程上执行代码 TWorkerThread。
虽然现在看起来更好,但仍远未达到最佳状态。
【问题讨论】:
-
好吧,与其猜测,我建议使用一个好的 C++ 分析器。有很多
-
您是在分析发布还是调试版本?
-
这是一个调试版本。
-
有人可以帮我解决这个问题吗?我知道这不是一个简单的问题,因为人们不能只用谷歌搜索答案。
标签: c++ multithreading optimization vcl