针对我的应用场景,我需要非常快速地插入和删除数据包列表中的数据。
我认为有两种常见的解决方法:
- 通过插入/删除指向这些数据包的指针
- 使用移动语义来插入/删除副本
无论如何,我认为采用指针的解决方案应该是最有效的,即使有自己的垃圾回收的缺点。
我实现了3个测试用例以进行更好的比较:
- 使用副本
- 使用移动语义
- 使用指针
为了测量每个测试用例所花费的时间,我还实现了一个计时器类:
class HighPerformanceTimer {
public:
enum TimerResolution {
SECONDS = 1,
MILLISECONDS = SECONDS * 1000,
MICROSECONDS = MILLISECONDS * 1000,
NANOSECONDS = MICROSECONDS * 1000
};
explicit HighPerformanceTimer(const TimerResolution resolution = NANOSECONDS)
: m_resolution(resolution)
, m_frequency(0.0)
, m_startTime({0})
, m_stopTime({0}) {}
~HighPerformanceTimer(void) {}
bool Init(void) {
LARGE_INTEGER frequency;
if(0 == ::QueryPerformanceFrequency(&frequency)) {
return false;
}
/* Check for zero divisor. */
if(0 == frequency.QuadPart) {
return false;
}
/* Change frequency to double for internal timer resolution. */
switch(m_resolution) {
case NANOSECONDS:
m_frequency = frequency.QuadPart / (NANOSECONDS * 1.0);
break;
case MICROSECONDS:
m_frequency = frequency.QuadPart / (MICROSECONDS * 1.0);
break;
case MILLISECONDS:
m_frequency = frequency.QuadPart / (MILLISECONDS * 1.0);
break;
default:
/**
* SECONDS
* m_frequency has a resolution in seconds by default
*/
m_frequency = frequency.QuadPart * 1.0;
break;
}
return true;
}
void Start(void) {
::QueryPerformanceCounter(&m_startTime);
}
void Stop(double& intervall) {
::QueryPerformanceCounter(&m_stopTime);
intervall = ((m_stopTime.QuadPart - m_startTime.QuadPart) / m_frequency);
}
private:
const TimerResolution m_resolution;
double m_frequency;
LARGE_INTEGER m_startTime;
LARGE_INTEGER m_stopTime;
CRITICAL_SECTION m_timerLock;
};
以下是我的主要类:
class Packet {
public:
Packet(const uint8* data, uint16 length) {
if(0 != length) {
if(nullptr == data) {
m_data = std::vector<uint8>(length, 0x00);
} else {
m_data.assign(data, data + length);
}
}
}
Packet(const Packet& rhs) : m_data(rhs.m_data) {}
Packet& operator=(const Packet& rhs) {
m_data = rhs.m_data;
return *this;
}
Packet(const Packet&& rhs) : m_data(rhs.m_data) {}
Packet& operator=(const Packet&& rhs) {
m_data = rhs.m_data;
return *this;
}
std::vector<uint8> m_data;
};
void Measurement_1(void);
void Measurement_2(void);
void Measurement_3(void);
constexpr uint16 payloadLength = 15000;
uint8 payload[payloadLength];
/* Initialize high performance timer. */
HighPerformanceTimer hpt(HighPerformanceTimer::MICROSECONDS);
int main(void) {
hpt.Init();
/* Fill packet data. */
for(unsigned int j = 0; j < payloadLength; ++j) {
payload[j] = 0xFF;
}
Measurement_1();
Measurement_2();
Measurement_3();
return EXIT_SUCCESS;
}
void Measurement_1(void) {
/* Measurement with copies. */
double result[25];
for(unsigned int k = 0; k < 25; ++k) {
/* Start measurement. */
double timeElapsed = 0.0;
std::list<Packet> mylist;
hpt.Start();
/* Begin insertion. */
for(unsigned int i = 0; i < 1000; ++i) {
Packet f(payload, payloadLength);
mylist.push_back(f);
}
/* End insertion. */
/* Begin removal. */
for(unsigned int i = 0; i < 1000; ++i) {
Packet f = mylist.front();
mylist.pop_front();
}
/* End removal. */
hpt.Stop(timeElapsed);
result[k] = timeElapsed;
/* Stop measurement. */
}
for(unsigned int i = 0; i < 25; ++i) {
std::cout << "with copies: " << std::setprecision(3) << std::fixed << result[i] << std::endl;
}
}
void Measurement_2(void) {
/* Measurement with move semantics. */
double result[25];
for(unsigned int k = 0; k < 25; ++k) {
/* Start measurement. */
double timeElapsed = 0.0;
std::list<Packet> mylist;
hpt.Start();
/* Begin insertion. */
for(unsigned int i = 0; i < 1000; ++i) {
Packet f(payload, payloadLength);
mylist.push_back(std::move(f));
}
/* End insertion. */
/* Begin removal. */
for(unsigned int i = 0; i < 1000; ++i) {
Packet f = std::move(mylist.front());
mylist.pop_front();
}
/* End removal. */
hpt.Stop(timeElapsed);
result[k] = timeElapsed;
/* Stop measurement. */
}
for(unsigned int i = 0; i < 25; ++i) {
std::cout << "with moves: " << std::setprecision(3) << std::fixed << result[i] << std::endl;
}
}
void Measurement_3(void) {
/* Measurement with pointers. */
double result[25];
for(unsigned int k = 0; k < 25; ++k) {
/* Start measurement. */
double timeElapsed = 0.0;
std::list<Packet*> mylist;
hpt.Start();
/* Begin insertion. */
for(unsigned int i = 0; i < 1000; ++i) {
mylist.push_back(new Packet(payload, payloadLength));
}
/* End insertion. */
/* Begin removal. */
Packet* f = nullptr;
for(unsigned int i = 0; i < 1000; ++i) {
f = mylist.front();
if(nullptr != f) {
mylist.pop_front();
delete f;
}
}
/* End removal. */
hpt.Stop(timeElapsed);
result[k] = timeElapsed;
/* Stop measurement. */
}
for(unsigned int i = 0; i < 25; ++i) {
std::cout << "with pointers: " << std::setprecision(3) << std::fixed << result[i] << std::endl;
}
}
目前我遇到的问题是,我得到的指针版本、移动语义版本和复制版本几乎得出了相同的测量结果。
我哪里做错了吗?
问候!
Measurement_X
方法调用应该至少需要几秒钟(最好是几十秒钟)。 - ConstructorPacket(const Packet&& rhs) : m_data(rhs.m_data) {}
这个语句无法调用移动语义。请尝试使用Packet(Packet&& rhs) noexcept
。 - dypstd::move
:Packet(Packet&& rhs) noexcept : m_data( std::move(rhs.m_data) ) {}
。 - dyp