เรามีการอภิปรายที่คล้ายกันเกี่ยวกับทูเพิลและโครงสร้างและฉันเขียนเกณฑ์มาตรฐานง่ายๆด้วยความช่วยเหลือจากเพื่อนร่วมงานคนหนึ่งของฉันเพื่อระบุความแตกต่างในแง่ของประสิทธิภาพระหว่างทูเพิลและโครงสร้าง ก่อนอื่นเราเริ่มต้นด้วยโครงสร้างเริ่มต้นและทูเพิล
struct StructData {
int X;
int Y;
double Cost;
std::string Label;
bool operator==(const StructData &rhs) {
return std::tie(X,Y,Cost, Label) == std::tie(rhs.X, rhs.Y, rhs.Cost, rhs.Label);
}
bool operator<(const StructData &rhs) {
return X < rhs.X || (X == rhs.X && (Y < rhs.Y || (Y == rhs.Y && (Cost < rhs.Cost || (Cost == rhs.Cost && Label < rhs.Label)))));
}
};
using TupleData = std::tuple<int, int, double, std::string>;
จากนั้นเราใช้ Celero เพื่อเปรียบเทียบประสิทธิภาพของโครงสร้างและทูเพิลอย่างง่ายของเรา ด้านล่างนี้คือโค้ดมาตรฐานและผลการทำงานที่รวบรวมโดยใช้ gcc-4.9.2 และ clang-4.0.0:
std::vector<StructData> test_struct_data(const size_t N) {
std::vector<StructData> data(N);
std::transform(data.begin(), data.end(), data.begin(), [N](auto item) {
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> dis(0, N);
item.X = dis(gen);
item.Y = dis(gen);
item.Cost = item.X * item.Y;
item.Label = std::to_string(item.Cost);
return item;
});
return data;
}
std::vector<TupleData> test_tuple_data(const std::vector<StructData> &input) {
std::vector<TupleData> data(input.size());
std::transform(input.cbegin(), input.cend(), data.begin(),
[](auto item) { return std::tie(item.X, item.Y, item.Cost, item.Label); });
return data;
}
constexpr int NumberOfSamples = 10;
constexpr int NumberOfIterations = 5;
constexpr size_t N = 1000000;
auto const sdata = test_struct_data(N);
auto const tdata = test_tuple_data(sdata);
CELERO_MAIN
BASELINE(Sort, struct, NumberOfSamples, NumberOfIterations) {
std::vector<StructData> data(sdata.begin(), sdata.end());
std::sort(data.begin(), data.end());
}
BENCHMARK(Sort, tuple, NumberOfSamples, NumberOfIterations) {
std::vector<TupleData> data(tdata.begin(), tdata.end());
std::sort(data.begin(), data.end());
}
ผลการปฏิบัติงานที่รวบรวมด้วย clang-4.0.0
Celero
Timer resolution: 0.001000 us
-----------------------------------------------------------------------------------------------------------------------------------------------
Group | Experiment | Prob. Space | Samples | Iterations | Baseline | us/Iteration | Iterations/sec |
-----------------------------------------------------------------------------------------------------------------------------------------------
Sort | struct | Null | 10 | 5 | 1.00000 | 196663.40000 | 5.08 |
Sort | tuple | Null | 10 | 5 | 0.92471 | 181857.20000 | 5.50 |
Complete.
และผลการปฏิบัติงานที่รวบรวมโดยใช้ gcc-4.9.2
Celero
Timer resolution: 0.001000 us
-----------------------------------------------------------------------------------------------------------------------------------------------
Group | Experiment | Prob. Space | Samples | Iterations | Baseline | us/Iteration | Iterations/sec |
-----------------------------------------------------------------------------------------------------------------------------------------------
Sort | struct | Null | 10 | 5 | 1.00000 | 219096.00000 | 4.56 |
Sort | tuple | Null | 10 | 5 | 0.91463 | 200391.80000 | 4.99 |
Complete.
จากผลลัพธ์ข้างต้นเราจะเห็นได้ชัดเจนว่า
เราทุกคนรู้ดีว่าการเขียนตัวดำเนินการ a == หรือ <หรือ> สำหรับคำจำกัดความของโครงสร้างทุกตัวจะเป็นงานที่เจ็บปวดและมีปัญหา ให้แทนที่ตัวเปรียบเทียบที่กำหนดเองของเราโดยใช้ std :: tie และเรียกใช้เกณฑ์มาตรฐานของเราใหม่
bool operator<(const StructData &rhs) {
return std::tie(X,Y,Cost, Label) < std::tie(rhs.X, rhs.Y, rhs.Cost, rhs.Label);
}
Celero
Timer resolution: 0.001000 us
-----------------------------------------------------------------------------------------------------------------------------------------------
Group | Experiment | Prob. Space | Samples | Iterations | Baseline | us/Iteration | Iterations/sec |
-----------------------------------------------------------------------------------------------------------------------------------------------
Sort | struct | Null | 10 | 5 | 1.00000 | 200508.20000 | 4.99 |
Sort | tuple | Null | 10 | 5 | 0.90033 | 180523.80000 | 5.54 |
Complete.
ตอนนี้เราจะเห็นแล้วว่าการใช้ std :: tie ทำให้โค้ดของเราดูหรูหรามากขึ้นและยากที่จะทำผิดอย่างไรก็ตามประสิทธิภาพจะลดลงประมาณ 1% ฉันจะอยู่กับโซลูชัน std :: tie ในตอนนี้เนื่องจากฉันได้รับคำเตือนเกี่ยวกับการเปรียบเทียบตัวเลขทศนิยมกับตัวเปรียบเทียบที่กำหนดเอง
จนถึงขณะนี้เรายังไม่มีวิธีแก้ปัญหาใด ๆ ที่จะทำให้รหัส struct ของเราทำงานได้เร็วขึ้น ลองดูที่ฟังก์ชัน swap และเขียนใหม่เพื่อดูว่าเราจะได้รับประสิทธิภาพหรือไม่:
struct StructData {
int X;
int Y;
double Cost;
std::string Label;
bool operator==(const StructData &rhs) {
return std::tie(X,Y,Cost, Label) == std::tie(rhs.X, rhs.Y, rhs.Cost, rhs.Label);
}
void swap(StructData & other)
{
std::swap(X, other.X);
std::swap(Y, other.Y);
std::swap(Cost, other.Cost);
std::swap(Label, other.Label);
}
bool operator<(const StructData &rhs) {
return std::tie(X,Y,Cost, Label) < std::tie(rhs.X, rhs.Y, rhs.Cost, rhs.Label);
}
};
ผลการดำเนินงานที่รวบรวมโดยใช้ clang-4.0.0
Celero
Timer resolution: 0.001000 us
-----------------------------------------------------------------------------------------------------------------------------------------------
Group | Experiment | Prob. Space | Samples | Iterations | Baseline | us/Iteration | Iterations/sec |
-----------------------------------------------------------------------------------------------------------------------------------------------
Sort | struct | Null | 10 | 5 | 1.00000 | 176308.80000 | 5.67 |
Sort | tuple | Null | 10 | 5 | 1.02699 | 181067.60000 | 5.52 |
Complete.
และผลการปฏิบัติงานที่รวบรวมโดยใช้ gcc-4.9.2
Celero
Timer resolution: 0.001000 us
-----------------------------------------------------------------------------------------------------------------------------------------------
Group | Experiment | Prob. Space | Samples | Iterations | Baseline | us/Iteration | Iterations/sec |
-----------------------------------------------------------------------------------------------------------------------------------------------
Sort | struct | Null | 10 | 5 | 1.00000 | 198844.80000 | 5.03 |
Sort | tuple | Null | 10 | 5 | 1.00601 | 200039.80000 | 5.00 |
Complete.
ตอนนี้โครงสร้างของเราเร็วกว่าทูเปิลเล็กน้อยในขณะนี้ (ประมาณ 3% โดยมีเสียงดังและน้อยกว่า 1% เมื่อใช้ gcc) อย่างไรก็ตามเราจำเป็นต้องเขียนฟังก์ชัน swap ที่กำหนดเองสำหรับโครงสร้างทั้งหมดของเรา
tuple
คือการกำหนดการนำไปใช้งานดังนั้นจึงขึ้นอยู่กับการนำไปใช้ของคุณ โดยส่วนตัวผมไม่ขอนับมัน