[英]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函數外什么也不做),那么編譯器將優化這些調用,以免降低性能。
與性能相關的任何問題一樣,系統會告訴您進行度量以獲取答案(這是嚴格正確的答案)。
但是根據經驗,對於實際上可以內聯的簡單內聯方法,您不會看到性能損失。 通常,僅將調用傳遞給另一個函數的內聯方法是內聯的理想選擇。
但是,即使沒有內聯您的包裝方法,我懷疑您不會注意到性能下降-甚至沒有可衡量的性能下降-除非在某些關鍵循環中調用包裝方法。 即使這樣,如果包裝的函數本身沒有做很多工作,也可能只能測量。
這種事情是最后要關注的事情。 首先要擔心使您的代碼正確,可維護以及您使用的是適當的算法。
與所有與優化相關的事情一樣,答案是,在知道優化是否值得之前,必須先測量性能本身。
-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.