公众号
欢迎投稿,推荐或自荐文章/软件/资源等
新年第一周
时隔多年,TIOBE年度语言又是c++了。看个乐
__builtin_clzll
.作者还讲了一些优化的东西,涨涨见识
总结了2022年来c++的各种进展,很全面了
直接看代码
constexpr auto foo() {
static constexpr auto value = 42; // error in C++20, okay in C++23
return value;
}
乍看没啥用
给个例子
template<char... Cs>
[[nodiscard]] constexpr auto to_string_view(){
static constexpr char p[sizeof...(Cs)] = {Cs...};
return std::string_view(p, sizeof...(Cs));
}
我发现这个特性和例子差距有点像 你已经学会1+1=2了来证明黎曼猜想吧
那种感觉
感谢热心网友CappucimoOffitial补充
constexpr 函数里允许 static constexpr 变量有一些更有意义的用途…… 譬如我要做个 std::visit 的类似物,通常需要一个手工虚表。而这个虚表理论上只在函数体里有用,不应暴露到外面。 C++23 的改动解决了“把表限制到仅在函数内部使用”和“支持常量求值”不可兼得的问题。
实现条件判断版本的none of any of
auto none_of(auto pred, auto... ts) {
const auto a = std::array{ts...};
return std::none_of(std::cbegin(a), std::cend(a), pred);
}
auto before(int a, int b, int c) {
if (a != 2 and b != 2 and c != 2) {
return 42;
}
return 0;
}
auto after(int a, int b, int c) {
if (none_of([](auto x) { return x == 2; }, a, b, c)) {
return 42;
}
return 0;
}
anyof怎么实现?
#include <utility>
#include <tuple>
#include <concepts>
template<class ... Args>
struct Comp {
template<class T>
auto operator==(T && other) {
return std::apply(
[&other](auto &&... data) {
return (((std::equality_comparable_with<decltype(data), T>) && data == other) || ...);
},
this -> data
);
}
std::tuple<Args...> data {};
};
template<class ...Args>
auto any_of(Args && ... args) {
return Comp<Args...> {
std::make_tuple(std::forward<Args>(args)...)
};
}
和上面差距有点大
实现了一个gcc插件支持 [[invariant]]
特性,代码在这里https://github.com/GavinRay97/gcc-invariant-plugin
博客记录了开发插件的方法和过程,挺有意思的
gdb调试和shell交互,复杂
介绍他的折腾
就是 std::execution::par
/std::execution::par_unseq
这玩意
std::vector<size_t> indices(num_pixels);
// initialize indices with 0, 1, ..
std::iota(indices.begin(), indices.end(), 0); // needs <numeric>
std::transform( // needs <algorithm>
std::execution::par, // <-- needs <execution>
indices.begin(), indices.end(), pixels.begin(),
[](size_t index){
return expensive_calculation(index);
}
);
老生常谈讲智能指针那套东西
检查有没有std::hash特化
struct HasStdHash {
private:
template <class T, class Dummy = decltype(std::hash<T>{})>
static constexpr bool exists(int) {
return true;
}
template <class T>
static constexpr bool exists(char) {
return false;
}
public:
template <class T>
static constexpr bool check() {
return exists<T>(42);
}
};
std::cout << "Does std::string have std::hash? " << HasStdHash::check<std::string>();
能不能更泛化一点?
template <template <class... InnerArgs> class Tmpl>
struct IsSpecialized {
private:
template <class... Args,
class dummy = decltype(Tmpl<Args...>{}.~Tmpl<Args...>())>
static constexpr bool exists(int) {
return true;
}
template <class... Args>
static constexpr bool exists(char) {
return false;
}
public:
template <class... Args>
static constexpr bool check() {
return exists<Args...>(42);
}
};
但这个代码对于这种场景是无效的
template<class T> struct SomeStruct;
bool test1 = IsSpecialized<SomeStruct>::check<std::string>();
template<> struct SomeStruct<std::string> {};
bool test2 = IsSpecialized<SomeStruct>::check<std::string>();
后面又讨论了一通ADL检测,我已经看不懂了
随便看看
cuda代码调优记录。看不懂
模拟器里玩模拟器,看不懂
msvc一个bug
#include <iostream>
#include <string>
template <typename T>
T galactus_the_devourer_of_const(const T& v) {
return false ? std::move(T{}) : std::move(v);
}
int main() {
const std::string food = "asdf";
std::cout << "before: " << food << '\n';
galactus_the_devourer_of_const(food);
std::cout << "after: " << food << '\n';
return 0;
}
// before: asdf
// after:
莫名其妙的被move了。解决办法,/permissive-
,默认是 /permissive
感兴趣可以看看
介绍这个库 https://github.com/iboB/xmem
可以分析智能指针引用,用法就不贴了。和hook malloc那种类似。不过要做很多很多适配代码
看代码
namespace winrt::Contoso::implementation {
struct ItemCollection : ItemCollectionT<ItemCollection>{
template<typename...Args> auto First(Args&&... args) {
return m_items.First(args...);
}
template<typename...Args> auto GetAt(Args&&... args) {
return m_items.GetAt(args...);
}
template<typename...Args> auto Size(Args&&... args) {
return m_items.Size(args...);
}
template<typename...Args> auto IndexOf(Args&&... args) {
return m_items.IndexOf(args...);
}
template<typename...Args> auto GetMany(Args&&... args) {
return m_items.GetMany(args...);
}
// And our bonus method
hstring ToJson();
private:
Windows::Foundation::Collections::IVector<Contoso::Item> m_items;
};
}
optvier做高频交易的,这个talk还是很有东西的
一些性能优化点
小对象尽可能紧凑,利用好cpu cache
能用vector用vector,甚至boost::stable_vector,unordered_map开销非常恐怖 workding set size有分析工具wss https://github.com/brendangregg/wss
seqlock怎么做更快?
作者实现了个基于奇偶版本号的lock,单生产者多消费者,T很小,这种写法没啥竞争,很值
template<typename T>
class SeqLock {
T mData;
std::atomic<uint32_t> mVersion;
};
template<typename T>
void SeqLock<T>::Store(const T& value) {
mVersion+=1;
std::memcpy(&mData, value, sizeof(T));
mVersion+=1;
}
template<typename T>
bool SeqLock<T>::Load(T& value) {
const auto version = mVersion.load();
if (version & 1 != 0) {
return false;
}
std::memcpy(&value, mData, sizeof(T));
return version == mVersion;
}
更快的SPMC?
考虑消费队列 SPSC
struct SPSCQueue {
alignas(64) std::atomic<uint64_t> mWriteIndex;
alignas(64) std::atomic<uint64_t> mReadIndex;
alignas(64) uint8_t mData[0];
};
就是一个循环buffer
SPMC那就不用维护mReadIndex,同时尽可能的让竞争更小
struct SPMCQueueV1 {
alignas(64) std::atomic<uint64_t> mIndex;
alignas(64) std::atomic<uint64_t> mPendingIndex;
alignas(64) uint8_t mData[0];
};
template <typename C>
void SPMCQueueV1::Push(MessageSize size, C WriteCallback) {
mPendingIndex += size;
std::memcpy(mCurrent, size, sizeof(MessageSize));
WriteCallback(mCurrent + sizeof(MessageSize));
mIndex += size;
}
template <typename C>
void SPMCQueueV1::Pop(C ReadCallback) {
if (mPendingIndex == mIndex) return;
MessageSize size;
std::memcpy(&size, mCurrent + sizeof(MessageSize), sizeof(MessageSize));
uint8_t buffer[size];
std::memcpy(buffer, mCurrent + sizeof(MessageSize), size);
ReadCallback(buffer, mSize);
}
性能不行
考虑seqlock的思路,使用版本号来替换index,降低index频繁修改的开销,均摊到每个槽的版本号上,性能直接起飞
<img src="https://user-images.githubusercontent.com/8872493/211016183-cc1ceb6f-0260-4c64-be89-0d3d00de54bf.png" alt="" width="80%">
每个槽都有mBlockCounter和mVersion,mVersion判定变化,mBlockCounter控制消费
struct Block {
alignas(64) std::atomic<uint64_t> mVersion;
alignas(64) std::atomic<uint64_t> mSize;
alignas(64) uint8_t mData[0];
};
struct Header {
alignas(64) std::atomic<uint64_t> mBlockCounter;
alignas(64) Block mData[0];
};
template <typename C>
void SPMCQueueV2::Push(MessageSize size, C WriteCallback) {
mVersion+=1
WriteCallback(&mCurrentBlock->mData[0]);
mVersion+=1
mBlockCounter+=1
}
系统调优,降了很多cpu cache利用,还有CPU隔离/用户态网络,NUMA绑定 大页等等
采集参数信息,保持观察
这随便说了一嘴,重要还是上面seqlock这套思路,将了大半个小时