C++ C++;元组与结构
使用C++ C++;元组与结构,c++,struct,tuples,C++,Struct,Tuples,使用std::tuple和只使用数据的struct之间有什么区别吗 typedef std::tuple<int, double, bool> foo_t; struct bar_t { int id; double value; bool dirty; } typedef std::tuple foo\t; 结构钢筋{ int-id; 双重价值; 脏兮兮的; } 从我在网上发现的情况来看,我发现有两个主要区别:struct更具可读性,而tuple有许多
std::tuple
和只使用数据的struct
之间有什么区别吗
typedef std::tuple<int, double, bool> foo_t;
struct bar_t {
int id;
double value;
bool dirty;
}
typedef std::tuple foo\t;
结构钢筋{
int-id;
双重价值;
脏兮兮的;
}
从我在网上发现的情况来看,我发现有两个主要区别:struct
更具可读性,而tuple
有许多可以使用的通用函数。
是否存在任何显著的性能差异?
此外,数据布局是否相互兼容(可交换铸造)?嗯,POD结构通常可以(ab)用于低级连续块读取和序列化。正如您所说,元组在某些情况下可能会更优化,并支持更多函数
使用任何更适合这种情况的东西,没有普遍的偏好。我认为(但我还没有进行基准测试)性能差异不会很大。数据布局很可能不兼容,并且是特定于实现的。不应该有性能差异(即使是微不足道的差异)。至少在正常情况下,它们将导致相同的内存布局。尽管如此,在它们之间进行强制转换可能并不需要工作(尽管我想通常情况下很有可能是这样)。如果在代码中使用多个不同的元组,则可以不必压缩所使用的函子数量。我这样说是因为我经常使用以下形式的函子:
template<int N>
struct tuple_less{
template<typename Tuple>
bool operator()(const Tuple& aLeft, const Tuple& aRight) const{
typedef typename boost::tuples::element<N, Tuple>::type value_type;
BOOST_CONCEPT_REQUIRES((boost::LessThanComparable<value_type>));
return boost::tuples::get<N>(aLeft) < boost::tuples::get<N>(aRight);
}
};
boom,你的代码是完全可读的:
double guessWhatThisIs = boost::tuples::get<CURRENT_POT>(someJackPotTuple);
双重猜测whatthisis=boost::tuples::get(someJackPotTuple);
因为当你想要获取其中包含的项目时,它会描述自己。就“通用功能”而言,Boost.Fusion值得一些爱。。。尤其是
翻页:ABRACADBRA
这意味着所有融合算法现在都适用于structdemo::employee
编辑:关于性能差异或布局兼容性,
tuple
的布局是实现定义的,因此不兼容(因此您不应该在两种表示之间转换),一般来说,我认为性能方面没有差异(至少在发行版中是这样)由于get
的内联,Tuple已经内置了默认值(for==和!=它比较了每个元素,for我们对tuple和struct进行了类似的讨论,我在一位同事的帮助下编写了一些简单的基准测试,以确定tuple和struct在性能方面的差异。我们首先从默认的struct和tuple开始
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>;
以及使用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.
从以上结果我们可以清楚地看到
- 元组比默认结构快
- clang生成的二进制文件比gcc生成的二进制文件具有更高的性能。clang与gcc的比较不是本文讨论的目的,因此我不会深入讨论细节
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.
以及使用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.
现在,我们的结构比元组稍微快一点(使用clang时约为3%,使用gcc时不到1%),但是,我们确实需要为所有结构编写定制的交换函数。好吧,这里有一个基准,它不会在结构操作符==()。事实证明,使用元组会对性能产生相当大的影响,因为使用POD不会对性能产生任何影响。(地址解析器甚至在逻辑单元看到该值之前就在指令管道中找到了该值。) 使用VS2015CE在我的机器上使用默认“Release”设置运行此操作的常见结果:
Structs took 0.0814905 seconds.
Tuples took 0.282463 seconds.
请胡闹直到你满意为止
#include <iostream>
#include <string>
#include <tuple>
#include <vector>
#include <random>
#include <chrono>
#include <algorithm>
class Timer {
public:
Timer() { reset(); }
void reset() { start = now(); }
double getElapsedSeconds() {
std::chrono::duration<double> seconds = now() - start;
return seconds.count();
}
private:
static std::chrono::time_point<std::chrono::high_resolution_clock> now() {
return std::chrono::high_resolution_clock::now();
}
std::chrono::time_point<std::chrono::high_resolution_clock> start;
};
struct ST {
int X;
int Y;
double Cost;
std::string Label;
bool operator==(const ST &rhs) {
return
(X == rhs.X) &&
(Y == rhs.Y) &&
(Cost == rhs.Cost) &&
(Label == rhs.Label);
}
bool operator<(const ST &rhs) {
if(X > rhs.X) { return false; }
if(Y > rhs.Y) { return false; }
if(Cost > rhs.Cost) { return false; }
if(Label >= rhs.Label) { return false; }
return true;
}
};
using TP = std::tuple<int, int, double, std::string>;
std::pair<std::vector<ST>, std::vector<TP>> generate() {
std::mt19937 mt(std::random_device{}());
std::uniform_int_distribution<int> dist;
constexpr size_t SZ = 1000000;
std::pair<std::vector<ST>, std::vector<TP>> p;
auto& s = p.first;
auto& d = p.second;
s.reserve(SZ);
d.reserve(SZ);
for(size_t i = 0; i < SZ; i++) {
s.emplace_back();
auto& sb = s.back();
sb.X = dist(mt);
sb.Y = dist(mt);
sb.Cost = sb.X * sb.Y;
sb.Label = std::to_string(sb.Cost);
d.emplace_back(std::tie(sb.X, sb.Y, sb.Cost, sb.Label));
}
return p;
}
int main() {
Timer timer;
auto p = generate();
auto& structs = p.first;
auto& tuples = p.second;
timer.reset();
std::sort(structs.begin(), structs.end());
double stSecs = timer.getElapsedSeconds();
timer.reset();
std::sort(tuples.begin(), tuples.end());
double tpSecs = timer.getElapsedSeconds();
std::cout << "Structs took " << stSecs << " seconds.\nTuples took " << tpSecs << " seconds.\n";
std::cin.get();
}
#包括
#包括
#包括
#包括
#包括
#包括
#包括
班级计时器{
公众:
计时器(){reset();}
void reset(){start=now();}
双getElapsedSeconds(){
std::chrono::duration seconds=now()-开始;
返回秒数。count();
}
私人:
静态std::chrono::time_point now(){
返回std::chrono::高分辨率时钟::now();
}
std::chrono::时间点开始;
};
结构街{
int X;
int-Y;
双重成本;
字符串标签;
布尔运算符==(常量ST和rhs){
返回
(X==rhs.X)&&
(Y==rhs.Y)&&
(成本==rhs.成本)&&
(标签==右侧标签);
}
布尔运算符rhs.X){return false;}
如果(Y>rhs.Y){返回false;}
if(Cost>rhs.Cost){返回false;}
如果(Label>=rhs.Label){return false;}
返回true;
}
};
使用TP=std::tuple;
std::pair generate(){
std::mt19937 mt(std::random_设备{}());
标准:均匀分布区;
constexpr size_t SZ=1000000;
std::对p;
自动&s=p.first;
auto&d=p.second;
s、 储备(深圳);
d、 储备(深圳);
对于(大小i=0;i std::cout我知道这是一个古老的主题,但是我现在要决定我的项目的一部分:我应该走元组方式还是结构方式。
读完这篇文章后,我有了一些想法
关于wheaties和性能测试:请注意,对于structs.T,通常可以使用memcpy、memset和类似的技巧
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.
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);
}
};
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.
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.
Structs took 0.0814905 seconds.
Tuples took 0.282463 seconds.
#include <iostream>
#include <string>
#include <tuple>
#include <vector>
#include <random>
#include <chrono>
#include <algorithm>
class Timer {
public:
Timer() { reset(); }
void reset() { start = now(); }
double getElapsedSeconds() {
std::chrono::duration<double> seconds = now() - start;
return seconds.count();
}
private:
static std::chrono::time_point<std::chrono::high_resolution_clock> now() {
return std::chrono::high_resolution_clock::now();
}
std::chrono::time_point<std::chrono::high_resolution_clock> start;
};
struct ST {
int X;
int Y;
double Cost;
std::string Label;
bool operator==(const ST &rhs) {
return
(X == rhs.X) &&
(Y == rhs.Y) &&
(Cost == rhs.Cost) &&
(Label == rhs.Label);
}
bool operator<(const ST &rhs) {
if(X > rhs.X) { return false; }
if(Y > rhs.Y) { return false; }
if(Cost > rhs.Cost) { return false; }
if(Label >= rhs.Label) { return false; }
return true;
}
};
using TP = std::tuple<int, int, double, std::string>;
std::pair<std::vector<ST>, std::vector<TP>> generate() {
std::mt19937 mt(std::random_device{}());
std::uniform_int_distribution<int> dist;
constexpr size_t SZ = 1000000;
std::pair<std::vector<ST>, std::vector<TP>> p;
auto& s = p.first;
auto& d = p.second;
s.reserve(SZ);
d.reserve(SZ);
for(size_t i = 0; i < SZ; i++) {
s.emplace_back();
auto& sb = s.back();
sb.X = dist(mt);
sb.Y = dist(mt);
sb.Cost = sb.X * sb.Y;
sb.Label = std::to_string(sb.Cost);
d.emplace_back(std::tie(sb.X, sb.Y, sb.Cost, sb.Label));
}
return p;
}
int main() {
Timer timer;
auto p = generate();
auto& structs = p.first;
auto& tuples = p.second;
timer.reset();
std::sort(structs.begin(), structs.end());
double stSecs = timer.getElapsedSeconds();
timer.reset();
std::sort(tuples.begin(), tuples.end());
double tpSecs = timer.getElapsedSeconds();
std::cout << "Structs took " << stSecs << " seconds.\nTuples took " << tpSecs << " seconds.\n";
std::cin.get();
}