威爾具有「功能通道」採取一擊,以性能?
最有可能不是。特別是因爲你在這裏處理模板,所以定義將全部可見並且易於編譯器內聯。看看這個鏈接:https://godbolt.org/g/cWR7N3
我所做的是編譯這兩個代碼片段。首先,調用Node類中的函數。
#include <vector>
// these functions are not defined, so that the compiler
// cannot inline them or optimize them out
void insert_impl(void const*);
void erase_impl(void const*);
void erase_impl_vec(void const*);
template<typename T>
class PointerSet
{
public:
void insert(T& v) { insert_impl(&v); }
void erase(T& v) { erase_impl(&v); }
void erase(const std::vector<T>& v) {
erase_impl_vec(&v);
}
};
template<typename T>
class Node
{
PointerSet<T> Links;
public:
void insertLink(T& p){ Links.insert(p); }
void eraseLink(T& p){ Links.erase(p); }
void eraseLink(const std::vector<T>& p){ Links.erase(p); }
};
int main()
{
Node<int> n;
int x;
n.insertLink(x);
n.eraseLink(x);
std::vector<int> v;
n.eraseLink(v);
}
然後直接從PointerSet
類中調用它們。
#include <vector>
// these functions are not defined, so that the compiler
// cannot inline them or optimize them out
void insert_impl(void const*);
void erase_impl(void const*);
void erase_impl_vec(void const*);
template<typename T>
class PointerSet
{
public:
void insert(T& v) { insert_impl(&v); }
void erase(T& v) { erase_impl(&v); }
void erase(const std::vector<T>& v) {
erase_impl_vec(&v);
}
};
int main()
{
PointerSet<int> n;
int x;
n.insert(x);
n.erase(x);
std::vector<int> v;
n.erase(v);
}
正如您在鏈接(https://godbolt.org/g/cWR7N3)中看到的,編譯器會爲每個輸出輸出相同的程序集。
main: # @main
push rbx
sub rsp, 48
lea rbx, [rsp + 12]
mov rdi, rbx
call insert_impl(void const*)
mov rdi, rbx
call erase_impl(void const*)
xorps xmm0, xmm0
movaps xmmword ptr [rsp + 16], xmm0
mov qword ptr [rsp + 32], 0
lea rdi, [rsp + 16]
call erase_impl_vec(void const*)
mov rdi, qword ptr [rsp + 16]
test rdi, rdi
je .LBB0_3
call operator delete(void*)
.LBB0_3:
xor eax, eax
add rsp, 48
pop rbx
ret
mov rbx, rax
mov rdi, qword ptr [rsp + 16]
test rdi, rdi
je .LBB0_6
call operator delete(void*)
.LBB0_6:
mov rdi, rbx
call _Unwind_Resume
GCC_except_table0:
.byte 255 # @LPStart Encoding = omit
.byte 3 # @TType Encoding = udata4
.byte 41 # @TType base offset
.byte 3 # Call site Encoding = udata4
.byte 39 # Call site table length
.long .Lfunc_begin0-.Lfunc_begin0 # >> Call Site 1 <<
.long .Ltmp0-.Lfunc_begin0 # Call between .Lfunc_begin0 and .Ltmp0
.long 0 # has no landing pad
.byte 0 # On action: cleanup
.long .Ltmp0-.Lfunc_begin0 # >> Call Site 2 <<
.long .Ltmp1-.Ltmp0 # Call between .Ltmp0 and .Ltmp1
.long .Ltmp2-.Lfunc_begin0 # jumps to .Ltmp2
.byte 0 # On action: cleanup
.long .Ltmp1-.Lfunc_begin0 # >> Call Site 3 <<
.long .Lfunc_end0-.Ltmp1 # Call between .Ltmp1 and .Lfunc_end0
.long 0 # has no landing pad
.byte 0 # On action: cleanup
那麼,如果這個實現是一個''vector'',就像一個'set'一樣,那麼你的公共接口將會很容易*錯誤地使用你的'PointerSet'類。在矢量中分配很多項目並要求對它們進行排序對於大數據來說效率非常低,因爲在插入或刪除項目時,您總是會複製大量數據。一種可能性是實現堆。除非您有大量小項目,並且要麼執行堆,要麼確保數據只在數據加載後被排序(並且不刪除項目),那麼如果您遇到性能問題,這可能是有意義的。 – Phil1970
@ Phil1970我也這麼認爲,但我在實際性能測試中發現,即使在一些非常大的數據上,vector也仍然優於set。無論如何,直到數據變得天文數字爲止。我猜移動語義幫助向量在切換元素時非常快速。也許你或某人可以澄清這一點?不過,我認爲這是一個不同的問題。 –
那麼,如果性能真的很重要,那麼你應該測量它,所以這個問題是毫無意義的,因爲你會知道真正的影響你的實際數據和用法......否則,你可以假設'std :: set'是適當而不是重新發明輪子。另外,您必須確保您以最糟糕的情況進行測試。一個矢量可以更快達到給定大小(取決於項目大小及其複製/移動實現)以及實際硬件(高速緩存大小...),主要是使用模式(插入/刪除與搜索次數的比率)。 – Phil1970