簡體   English   中英

如何以最小的性能損失同步C和C ++庫?

[英]How to synchronize C & C++ libraries with minimal performance penalty?

我有一個帶有很多數學例程的C庫,用於處理矢量,矩陣,四元數等。 它需要保留在C中,因為我經常將它用於嵌入式工作和Lua擴展。 此外,我還有C ++類包裝器,可以使用C API為數學運算提供更方便的對象管理和運算符重載。 包裝器僅包含一個頭文件,並在內聯中盡可能多地使用它。

包裝C代碼與將實現直接移植和內聯到C ++類中是否有明顯的代價? 該庫用於時間緊迫的應用程序。 那么,消除間接帶來的好處是否可以彌補兩個端口的維護麻煩?

C接口示例:

typedef float VECTOR3[3];

void v3_add(VECTOR3 *out, VECTOR3 lhs, VECTOR3 rhs);

C ++包裝器示例:

class Vector3
{
private:
    VECTOR3 v_;

public:
    // copy constructors, etc...

    Vector3& operator+=(const Vector3& rhs)
    {
        v3_add(&this->v_, this->v_, const_cast<VECTOR3> (rhs.v_));
        return *this;
    }

    Vector3 operator+(const Vector3& rhs) const
    {
        Vector3 tmp(*this);
        tmp += rhs;
        return tmp;
    }

    // more methods...
};

如果只是將C庫調用包裝在C ++類函數中(換句話說,C ++函數除了調用C函數外什么也不做),那么編譯器將優化這些調用,以免降低性能。

與性能相關的任何問題一樣,系統會告訴您進行度量以獲取答案(這是嚴格正確的答案)。

但是根據經驗,對於實際上可以內聯的簡單內聯方法,您不會看到性能損失。 通常,僅將調用傳遞給另一個函數的內聯方法是內聯的理想選擇。

但是,即使沒有內聯您的包裝方法,我懷疑您不會注意到性能下降-甚至沒有可衡量的性能下降-除非在某些關鍵循環中調用包裝方法。 即使這樣,如果包裝的函數本身沒有做很多工作,也可能只能測量。

這種事情是最后要關注的事情。 首先要擔心使您的代碼正確,可維護以及您使用的是適當的算法。

與所有與優化相關的事情一樣,答案是,在知道優化是否值得之前,必須先測量性能本身。

  • 對兩個不同的函數進行基准測試,一個函數直接調用C樣式函數,另一個函數通過包裝器調用。 查看哪一個運行速度更快,或者差異是否在測量誤差范圍內(這意味着您可以測量的差異不大)。
  • 查看上一步中兩個函數生成的匯編代碼(在gcc上,使用-S-save-temps )。 查看編譯器是否做過一些愚蠢的事情,或者您的包裝器是否有任何性能錯誤。

除非性能差異太大而不支持不使用包裝程序,否則重新實現不是一個好主意,因為您可能會引入錯誤(甚至可能導致看起來很合理但錯誤的結果)。 即使差異很大,僅記住C ++與C高度兼容並且即使在C ++代碼中也可以使用C風格的庫,這樣會更簡單且風險更低。

包裝器本身將被內聯,但是,對C庫的方法調用通常不會被內聯。 (這將需要對鏈接時間進行優化,這在技術上是可行的,但在當今的工具中充其量只能滿足AFAIK的基本要求)

通常,這樣的函數調用不是很昂貴。 在過去的幾年中,周期成本已大大降低,並且可以輕松預測,因此通話費用可以忽略不計。

但是,內聯為更多優化打開了大門:如果您擁有v = a + b + c,則包裝器類將強制生成堆棧變量,而對於內聯調用,大多數數據可以保留在FPU堆棧中。 而且,內聯代碼允許簡化指令,考慮常量值等。

因此,盡管在投資規則之前措施適用,但我希望這里有一些改進的空間。


一種典型的解決方案是將C實現轉換為既可以用作內聯函數也可以用作“ C”主體的格式:

// V3impl.inl
void V3DECL v3_add(VECTOR3 *out, VECTOR3 lhs, VECTOR3 rhs)
{
    // here you maintain the actual implementations
    // ...
}

// C header
#define V3DECL 
void V3DECL v3_add(VECTOR3 *out, VECTOR3 lhs, VECTOR3 rhs);

// C body
#include "V3impl.inl"


// CPP Header
#define V3DECL inline
namespace v3core {
  #include "V3impl.inl"
} // namespace

class Vector3D { ... }

這僅對於具有相對簡單主體的選定方法才有意義。 我會將這些方法移到C ++實現的一個單獨的命名空間中,因為通常不需要直接使用它們。

(請注意,內聯只是編譯器提示,不會強制內聯該方法。但這很好:如果內部循環的代碼大小超出指令緩存,則內聯很容易影響性能)

是否可以解析通過引用/返回引用取決於編譯器的強度,我已經看到很多在foo(X * out)強制堆棧變量的地方,而X foo()確實將值保存在寄存器中。

我認為您不會注意到很多性能差異。 假設您的目標平台支持所有數據類型,

我正在為DS和其他一些ARM設備進行編碼,並且浮點數是有害的...我不得不將defdef類型輸入為FixedPoint <16,8>

如果您擔心調用函數的開銷會拖慢您的速度,為什么不測試內聯C代碼或將其轉換為宏呢?

另外,為什么不在使用C時提高C代碼的const正確性-確實應該謹慎使用const_cast,尤其是在您控制的接口上。

暫無
暫無

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

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