簡體   English   中英

是否可以對變量進行編譯時“引用”?

[英]Is it possible to have a compile-time “reference” to a variable?

請考慮以下代碼:

Matrix4x4 perspective(const ViewFrustum &frustum) {
    float l = frustum.l;
    float r = frustum.r;
    float b = frustum.b;
    float t = frustum.t;
    float n = frustum.n;
    float f = frustum.f;

    return {
        { 2 * n / (r - l), 0,               (r + l) / (r - l),    0                      },
        { 0,               2 * n / (t - b), (t + b) / (t - b),    0                      },
        { 0,               0,               -((f + n) / (f - n)), -(2 * n * f / (f - n)) },
        { 0,               0,               -1,                   0                      }
    };
}

為了提高構造矩陣的可讀性,我必須從截頭結構中復制值,或者引用它們。 但是,我實際上也不需要復制或間接。

是否有可能在編譯時解決某種“引用”,有點像符號鏈接。 它會產生與以下相同的效果:

Matrix4x4 perspective(const ViewFrustum &frustum) {
    #define l frustum.l;
    #define r frustum.r;
    #define b frustum.b;
    #define t frustum.t;
    #define n frustum.n;
    #define f frustum.f;

    return {
        { 2 * n / (r - l), 0,               (r + l) / (r - l),    0                      },
        { 0,               2 * n / (t - b), (t + b) / (t - b),    0                      },
        { 0,               0,               -((f + n) / (f - n)), -(2 * n * f / (f - n)) },
        { 0,               0,               -1,                   0                      }
    };

    #undef l
    #undef r
    #undef b
    #undef t
    #undef n
    #undef f
}

沒有預處理器(或者它可以接受嗎?)。 我想這不是真的需要,或者在這種特殊情況下可以通過直接將這6個值參數化為函數來避免(雖然這樣調用函數會有點惱人 - 但即便如此,我也可以內聯代理功能)。

但我只是想知道這是否在某種程度上是可能的? 我找不到類似的東西。 我認為在本地縮短將被大量使用的描述性名稱會變得非常方便,而不必丟失原始名稱。

嗯,這就是C ++引用的用途:

const float &l = frustum.l;
const float &r = frustum.r;
const float &b = frustum.b;
const float &t = frustum.t;
const float &n = frustum.n;
const float &f = frustum.f;

大多數現代編譯器將優化引用,並通過在編譯時解析引用,在下面的表達式中逐字使用frustum對象中的值。

強制性免責聲明 :不要過早優化。

讓我比較你的天真perspective功能,包含

float l = frustum.l;
float r = frustum.r;
float b = frustum.b;
float t = frustum.t;
float n = frustum.n;
float f = frustum.f;

使用define和@Sam Varshavchik解決方案和引用。

我們假設我們的編譯器正在優化,並且至少優化得體。

所有三個版本的匯編輸出: https//godbolt.org/g/G06Bx8

您可以注意到引用和定義版本完全相同 - 正如預期的那樣。 但天真有很大不同。 它首先加載內存中的所有值:

    movss   (%rdi), %xmm2           # xmm2 = mem[0],zero,zero,zero
    movss   4(%rdi), %xmm1          # xmm1 = mem[0],zero,zero,zero
    movss   8(%rdi), %xmm0          # xmm0 = mem[0],zero,zero,zero
    movss   %xmm0, 12(%rsp)         # 4-byte Spill
    movss   12(%rdi), %xmm0         # xmm0 = mem[0],zero,zero,zero
    movss   %xmm0, 8(%rsp)          # 4-byte Spill
    movss   16(%rdi), %xmm3         # xmm3 = mem[0],zero,zero,zero
    movaps  %xmm3, 16(%rsp)         # 16-byte Spill
    movss   20(%rdi), %xmm0

然后再也不會引用%rdifrustrum )內存。 另一方面,引用和定義版本在需要時加載值。

發生這種情況是因為Vector4構造函數的實現對優化器是隱藏的,並且它不能假設構造函數不修改frustrum ,因此它必須插入負載,即使這樣的負載是多余的。

因此,在某些情況下,天真版本甚至比“優化版本” 更快

通常 ,只要您在本地范圍內,就可以使用普通引用。 現代編譯器“透視它們”並將它們視為別名(注意這實際上甚至適用於指針)。

但是,當處理小的東西時,復制到局部變量(如果有的話)通常是有益的。 frustnum.r是間接的一層( frustnum實際上是引擎蓋下的指針),因此訪問它比它看起來更昂貴,並且如果你在函數中間有函數調用,編譯器可能無法證明它的值不會改變,因此可能需要重復訪問。

局部變量通常直接在堆棧上(廉價)或直接在寄存器中(最便宜),最重要的是,假設它們通常沒有與“外部”交互,編譯器有更容易的時間推理它們,所以它優化可以更積極; 此外,當實際執行計算時,無論如何都要將這些值復制到寄存器和堆棧中。

所以繼續使用副本,在最壞的情況下,編譯器可能也會這樣做,最多可以幫助它優化內容。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM