layout | title |
---|---|
post |
第32期 |
从reddit/hackernews/lobsters/meetingcpp摘抄一些c++动态
本期把国庆节这两周的断更补上
欢迎投稿,推荐或自荐文章/软件/资源等,请提交 issue
十月了,cppcon要在十月底。到时候会有很多视频分享
多维数组的支持!c++23提案。说不定会过。Compiler Explorer试玩儿
template <class T, auto Dimensions> class mdarray2 {
public:
template <class I1, class I2> constexpr T &operator[](I1 i1, I2 i2) {
return vs_[i1][i2];
}
private:
std::array<std::array<T, 2>, Dimensions> vs_{};
};
int main() {
mdarray2<int, 2> a{};
a[1, 1] = 42;
assert(0 == (a[0, 0]));
assert(42 == (a[1, 1]));
}
一个demangle小工具
xmake是一个类似bazel的编译构建工具,这里列了一个用xmake组织导入库的方法
range based 遍历涉及到临时变量的时候会有一些问题。
这种场景要了解,不踩坑
for(auto e :getTmpColl());// 就这个可以,其他场景都会挂
for(auto e :getTmpColl().getRef());
for(char c :getVectorOfStrings()[0]);
for(auto e :getOptionalVector().value());
for(auto e :std::get<0>(getTuple()));
for(auto e :std::span{getColl().data(), 5});
这个提案是建议标准委员会去修复,不过没啥进展,这里是周知一下这个坑
介绍了一些使用libcurl遇到的错误
- 不看文档
- 不检查返回值
- 不设置verbose日志看不全
- 没调用global_init
- 重定向设置?
- 不要让用户设定/拼接网址
- 不要禁止证书
- 检查\0
- c++ string和c string要注意区分
- 多线程问题,global_init不是线程安全的
- CURLOPT_NOSIGNAL默认是设置的,会忽略所有信号
- 用-DCURL_STATICLIB,尽量静态编译
还有一些c++相关的就不介绍了。基本函数使用
这个之前讲过,return一个值是不需要std::move就有copy elision 复制消除优化的,但是有些场景就优化不到,还需要std::move
比如这种
Foo update( Foo f, int const value )
{
f.value = value;
return f;
}
改了一下,就用不上copy elision了。这里的解决方案就是强制调用std::move(f)
c++20引入simpler implicit moves这个提案来修正,目前clang gcc都支持了
之前说过google 浏览器内核团队做的GC,这个文章详细的介绍了olipan这个GC的设计,以及相关的资料整理
用any_of和none_of,all_of实现同样的语义,如果要判断 != end()
, 用 any_of
, 如果判断 == end()
, 用 none_of
,如果用find_if_not
且判断== end()
使用 all_of
比较经典的overload trick了。再复读一次,代码这样
template<typename ... Ts> // (7)
struct Overload : Ts ... {
using Ts::operator() ...;
};
template<class... Ts> Overload(Ts...) -> Overload<Ts...>;
std::vector<std::variant<char, long, float, int, double, long long>> // (1)
vecVariant = {5, '2', 5.4, 100ll, 2011l, 3.5f, 2017};
auto TypeOfIntegral = Overload { // (2)
[](char) { return "char"; },
[](int) { return "int"; },
[](unsigned int) { return "unsigned int"; },
[](long int) { return "long int"; },
[](long long int) { return "long long int"; },
[](auto) { return "unknown type"; },
};
for (auto v : vecVariant) { // (3)
std::cout << std::visit(TypeOfIntegral, v) << '\n';
}
std::cout << '\n';
std::vector<std::variant<std::vector<int>, double, std::string>> // (4)
vecVariant2 = { 1.5, std::vector<int>{1, 2, 3, 4, 5}, "Hello "};
auto DisplayMe = Overload { // (5)
[](std::vector<int>& myVec) {
for (auto v: myVec) std::cout << v << " ";
std::cout << '\n';
},
[](auto& arg) { std::cout << arg << '\n';},
};
for (auto v : vecVariant2) { // (6)
std::visit(DisplayMe, v);
}
std::to_string() / std::stingstream / Boost’s lexical_cast()
复习一下UDL
using namespace std::literals::chrono_literals;
auto threeSeconds = 3s;
auto tenMinutes = 10min;
auto twoHours = 2h;
auto oneMillisecond = 1ms;
auto oneMicroSecond = 1us;
auto oneNanoSecond = 1ns;
using namespace std::literals::string_view_literals;
auto myStringView = "hello"sv;
这个是作者的一些代码review经验。其实根据google代码规范来review就好
同样一个算法,float和int的效果差距非常大
void hash_add(size_t& hash, size_t new_hash)
{
// taken from boost::hash_combine
hash ^= new_hash + 0x9e3779b9 + (hash << 6) + (hash >> 2);
}
size_t myhash_float(float x, float y, float z)
{
size_t h = /* some fixed seed */;
hash_add(h, std::bit_cast<uint32_t>(x));
hash_add(h, std::bit_cast<uint32_t>(y));
hash_add(h, std::bit_cast<uint32_t>(z));
return h;
}
size_t myhash_int(float x, float y, float z)
{
size_t h = /* some fixed seed */;
hash_add(h, int32_t(256 * x));
hash_add(h, int32_t(256 * y));
hash_add(h, int32_t(256 * z));
return h;
}
float 有3%的碰撞率int只有%0.2,所以有严重的性能问题
作者用xorshirf和xxhash,放在float数据集上,基本没有碰撞。所以还是这个算法的问题
如何估算碰撞?
template <class Map>
double unordered_map_badness(Map const& map)
{
auto const lambda = map.size() / double(map.bucket_count());
auto cost = 0.;
for (auto const& [k, _] : map)
cost += map.bucket_size(map.bucket(k));
cost /= map.size();
return std::max(0., cost / (1 + lambda) - 1);
}
针对const的operator(),有了个新的语法糖提案
struct ByAuthor {
bool operator()(const Book& a, const Book& b) const {
return a.author() < b.author();
}
};
// 新的写法
struct ByAuthor {
static bool operator()(const Book& a, const Book& b) {
return a.author() < b.author();
}
};
如何用上这个特性又不改代码,作者给了个宏
#if __cplusplus > 202002L
#define CALL_OPERATOR(...) static operator()(__VA_ARGS__)
#else
#define CALL_OPERATOR(...) operator()(__VA_ARGS__) const
#endif
struct ByAuthor {
bool CALL_OPERATOR(const Book& a, const Book& b) {
return a.author() < b.author();
}
};
读这篇文章,需要了解循环优化的基本原理,也就是这篇文章Loop Optimizations: how does the compiler do it?
先简单介绍一下基本原理
- 循环变量全放到寄存器
- 去掉多余的计算
- 循环不变量优化
for (int i = 0; i < n; i++) { switch (operation) { case ADD: a[i]+= x * x; break; case SUB: a[i]-= x * x; break; } } auto x_2 = x * x; if (operation == ADD) { for (int i = 0; i < n; i++) { a[i] += x_2; } } else if (operation == SUB) { for (int i = 0; i < n; i++) { a[i] -= x_2; } }
- 替换简单的指令,指令改写
- 循环展开
- 流水线执行pipeline
- 向量话
- Loop Interchange 修改访问方式让内存访问更友好,感觉有点像循环展开的另一种表达?
- Loop Distribution 拆开循环
比如
for (int i = 0; i < n; i++) { a[i] = a[i - 1] * b[i]; c[i] = a[i] + e[i]; } // for (int i = 0; i < n; i++) { a[i] = a[i - 1] * b[i]; } for (int i = 0; i < n; i++) { c[i] = a[i] + e[i]; }
- Loop Fusion合并循环,上面的反过来
循环的性能杀手就是循环内部的函数调用以及指针引用
函数调用
for (int i = 0; i < n; i++) {
...
if (debug) {
printf("The data is NaN\n");
}
}
这种,编译器会生成两个循环,一个有调用的一个没调用的
指针问题
for (int i = 0; i < n; i++) {
b[i] = 0;
for (int j = 0; j < n; j++) {
b[i] += a[i][j];
}
}
//bad
double** a = new double*[n];
double* b = new double[n];
for (int i = 0; i < n; i++) {
a[i] = b;
}
这种没法做优化,因为指针没法做分析,不能确定是不是地址被改了
解决方案__restrict__
for (int i = 0; i < n; i++) {
double sum = 0;
double* __restrict__ a_row = a[i];
for (int j = 0; j < n; j++) {
sum += a_row[j];
}
b[i] = sum;
}
循环展开优化怎么做
llvm有 #pragma clang loop unroll
向量化怎么做?
#pragma clang loop vectorize(enable)
#pramga omp simd
得装openmp -fopenmp
-fopenmp-simd
如果编译器没做循环展开,哪里出了问题?
没看懂要啥需求
讨论如何让模版参数的入参必须是const的
不让用const_cast,用std::as_const来替代
其实是老生常谈了。错误处理场景,用goto还是很干净的。
-
Core C++ 2021 放出了他们的所有talk。这个看完需要一点时间,还好,就20几个, 这里留个预告,后面发出来
一个谷歌浏览器开发者做的入门视频。没看
分析虚函数代价,结论,如果是长的函数,虚函数和非虚函数开销差距不大,如果是短的快的函数,虚函数浪费18%左右。如果有机会,可以研究一下他的测试方法复现一下