指针访问如此昂贵?

时间:2017-05-10 08:35:46

标签: c++ arrays performance pointers

我有一个Process()函数在我的DLL(VST插件)中被称为非常重的,在DAW(主机软件)中加载,例如:

for (int i = 0; i < nFrames; i++) {
    // ...

    for (int voiceIndex = 0; voiceIndex < PLUG_VOICES_BUFFER_SIZE; voiceIndex++) {
        Voice &voice = pVoiceManager->mVoices[voiceIndex];
        if (voice.mIsPlaying) {
            for (int envelopeIndex = 0; envelopeIndex < ENVELOPES_CONTAINER_NUM_ENVELOPE_MANAGER; envelopeIndex++) {
                Envelope &envelope = pEnvelopeManager[envelopeIndex]->mEnvelope;
                envelope.Process(voice);
            }           
        }
    }
}

void Envelope::Process(Voice &voice) {
    if (mIsEnabled) {
        // update value
        mValue[voice.mIndex] = (mBlockStartAmp[voice.mIndex] + (mBlockStep[voice.mIndex] * mBlockFraction[voice.mIndex]));
    }
    else {
        mValue[voice.mIndex] = 0.0;
    }
}

它主要占用主机内2%的CPU(这很好)。

现在,如果我稍微将代码更改为此(基本上是增量和赋值):

void Envelope::Process(Voice &voice) {
    if (mIsEnabled) {
        // update value
        mValue[voice.mIndex] = (mBlockStartAmp[voice.mIndex] + (mBlockStep[voice.mIndex] * mBlockFraction[voice.mIndex]));

        // next phase
        mBlockStep[voice.mIndex] += mRate;
        mStep[voice.mIndex] += mRate;
    }
    else {
        mValue[voice.mIndex] = 0.0;
    }

    // connectors
    mOutputConnector_CV.mPolyValue[voice.mIndex] = mValue[voice.mIndex];
}

CPU达到6/7%(注意,那些var不与代码的其他部分交互,或者至少我认为如此)。

我能想到的唯一原因是对指针的访问很重?如何减少这个CPU数量?

这些数组是基本的双“指针”数组(最轻的C ++ container):

double mValue[PLUG_VOICES_BUFFER_SIZE];
double mBlockStartAmp[PLUG_VOICES_BUFFER_SIZE];
double mBlockFraction[PLUG_VOICES_BUFFER_SIZE];
double mBlockStep[PLUG_VOICES_BUFFER_SIZE];
double mStep[PLUG_VOICES_BUFFER_SIZE];

OutputConnector mOutputConnector_CV;

有什么建议吗?

2 个答案:

答案 0 :(得分:2)

你可能会认为“指针数组”是最轻的容器。但CPU并不考虑容器。他们只是通过指针读取和写入值。

这里的问题很可能是知道两个容器没有重叠(没有“子容器”)。但是编译器可能没有告诉CPU。写入mBlockStep可能会影响mBlockFraction。编译器没有运行时值,因此需要处理它的情况。这意味着引入更多的内存读取,并减少寄存器中值的缓存。

答案 1 :(得分:1)

  1. 将所有数据项打包到结构中并创建结构数组。我只想使用vector
  2. Process函数中,从该向量中获取单个元素,并使用其参数。在高速缓存行/指令级别,所有项目(有效地)将被带入本地高速缓存(L1),作为连续的数据元素(结构的成员)。使用struct类型的引用或指针来避免复制。
  3. 尝试使用整数数据类型,除非需要double
  4. 修改

    struct VoiceInfo 
    { 
       double mValue; 
       ...
    }; 
    VoiceInfo voices[PLUG_VOICES_BUFFER_SIZE];
    // Or vector<VoiceInfo> voices;
    

    ...

    void Envelope::Process(Voice &voice) 
    {
         // Get the object (by ref/pointer)
         VoiceInfo& info = voices[voice.mIndex];
         // Work with reference 'info'
      ...
    }