C++ 中文周刊 第106期

周刊项目地址

公众号

RSS https://github.com/wanghenshui/cppweeklynews/releases.atom

欢迎投稿,推荐或自荐文章/软件/资源等

提交 issue

难得线上稳定 T_T 不过本周内容依旧不多

之前给大家推荐的llvm中文邮件列表不知道大家关注没,发送一个标题为「subscribe」的邮件(正文可以为空)到 hellollvm-request@freelists.org

有人发LLVM的一些使用,还是挺有意思的


资讯

标准委员会动态/ide/编译器信息放在这里

编译器信息最新动态推荐关注hellogcc公众号 本周更新 2023-03-22 第194期

AMD EPYC Genoa-X CPU 曝光:配备 1.25GB 缓存,预计将于年内推出

3D Vcache服务器,1G多cache,离谱,zen3就很省钱,zen4这波又得扩大一波市场占用率,英特尔慌不慌

文章

class parse {
public:
  parse(std::string str) : str_(str) {}

  template<typename T>
  operator T() && {
    if constexpr (std::is_integral_v<T>) {
      T t = atoi(str_.data());
      return t;
    }
    else {
      return str_;
    }
  }

private:
  std::string str_;
};

std::string str = "42";
int i = parse(str);
assert(i == 42);

std::string s = parse(str);
assert(s == "42");

有趣

enum class error { runtime_error };

[[nodiscard]] auto foo() -> std::expected<int, error> {
    return std::unexpected(error::runtime_error);
}

int main() {
    const auto e = foo();
    e.and_then([](const auto& e) -> std::expected<int, error> {
         std::cout << int(e);  // not printed
         return {};
     }).or_else([](const auto& e) -> std::expected<int, error> {
        std::cout << int(e);  // prints 0
        return {};
    });
}

真不容易,之前就该有的东西

省流,替换unordered_map为tsl/robin_map.h,性能提升明显(2023了大家应该都知道标准库这个map垃圾了)

省流: 空基类优化

苹果M1平台下的perf counter https://gist.github.com/ibireme/173517c208c7dc333ba962c1f0d67d12

例子

#include "performancecounters/event_counter.h"

event_collector collector;

void f() {
  event_aggregate aggregate{};
  for (size_t i = 0; i < repeat; i++) {
   collector.start();
   function(); // benchmark this function
   event_count allocate_count = collector.end();
   aggregate << allocate_count;
  }
}

快一点,但不多,一般来说热点也不在这

监控服务,发现性能瓶颈,但发现监控服务本身成了瓶颈,修复监控服务的bug,有意思

看不懂,但感觉有点意思

实现了个限流桶算法。看个乐,直接贴了

https://github.com/mvorbrodt/blog/blob/master/src/token_bucket.hpp

#include <atomic>
#include <chrono>
#include <thread>
#include <stdexcept>
 
// Multi-Threaded Version of Token Bucket
class token_bucket_mt {
public:
	using clock = std::chrono::steady_clock;
	using duration = clock::duration;
	using time_point = clock::time_point;
 
	using atomic_duration = std::atomic<duration>;
	using atomic_time_point = std::atomic<time_point>;
 
	token_bucket_mt(std::size_t tokens_per_second, std::size_t token_capacity, bool full = true)
	: token_bucket_mt(std::chrono::duration_cast<duration>(std::chrono::seconds(1)) / tokens_per_second, token_capacity, full) {}
 
	token_bucket_mt(duration time_per_token, std::size_t token_capacity, bool full = true)
	: m_time{ full ? time_point{} : clock::now() }, m_time_per_token{ time_per_token }, m_time_per_burst{ time_per_token * token_capacity }
	{
		if (time_per_token.count() <= 0) throw std::invalid_argument("Invalid token rate!");
		if (!token_capacity) throw std::invalid_argument("Invalid token capacity!");
	}
 
	token_bucket_mt(const token_bucket_mt& other) noexcept
	: m_time{ other.m_time.load() },
	m_time_per_token{ other.m_time_per_token.load() },
	m_time_per_burst{ other.m_time_per_burst.load() } {}
 
	token_bucket_mt& operator = (const token_bucket_mt& other) noexcept {
		m_time = other.m_time.load();
		m_time_per_token = other.m_time_per_token.load();
		m_time_per_burst = other.m_time_per_burst.load();
		return *this;
	}
 
	void set_rate(std::size_t tokens_per_second) {
		set_rate(std::chrono::duration_cast<duration>(std::chrono::seconds(1)) / tokens_per_second);
	}
 
	void set_rate(duration time_per_token) {
		if (time_per_token.count() <= 0) throw std::invalid_argument("Invalid token rate!");
		m_time_per_token = time_per_token;
	}
 
	void set_capacity(std::size_t token_capacity) {
		if (!token_capacity) throw std::invalid_argument("Invalid token capacity!");
		m_time_per_burst = m_time_per_token.load() * token_capacity;
	}
 
	void drain() noexcept {
		m_time = clock::now();
	}
 
	void refill() noexcept {
		auto now = clock::now();
		m_time = now - m_time_per_burst.load();
	}
 
	[[nodiscard]] bool try_consume(std::size_t tokens = 1, duration* time_needed = nullptr) noexcept {
		auto now = clock::now();
		auto delay = tokens * m_time_per_token.load(std::memory_order_relaxed);
		auto min_time = now - m_time_per_burst.load(std::memory_order_relaxed);
		auto old_time = m_time.load(std::memory_order_relaxed);
		auto new_time = min_time > old_time ? min_time : old_time;
 
		while (true) {
			new_time += delay;
			if (new_time > now) {
				if (time_needed != nullptr)
					*time_needed = new_time - now;
 
				return false;
			}
 
			if (m_time.compare_exchange_weak(old_time, new_time, std::memory_order_relaxed, std::memory_order_relaxed))
				return true;
 
			new_time = old_time;
		}
	}
 
	void consume(std::size_t tokens = 1) noexcept {
		while (!try_consume(tokens))
			std::this_thread::yield();
	}
 
	void wait(std::size_t tokens = 1) noexcept {
		duration time_needed;
		while (!try_consume(tokens, &time_needed))
			std::this_thread::sleep_for(time_needed);
	}
 
private:
	atomic_time_point m_time;
	atomic_duration m_time_per_token;
	atomic_duration m_time_per_burst;
};
 
// Default Token Bucket
using token_bucket = token_bucket_mt;
#include <iostream>
#include <iomanip>
#include <vector>
#include <latch>
#include "token_bucket.hpp"
 
#define all(c) for(auto& it : c) it
 
int main() {
	using namespace std;
	using namespace std::chrono;
 
	try {
		auto N = 1;
		auto bucket = token_bucket(1ms, 1000, true);
		auto count = thread::hardware_concurrency() - 1;
		auto run = atomic_bool{ true };
		auto total = atomic_uint64_t{};
		auto counts = vector<atomic_uint64_t>(count);
		auto fair_start = latch(count + 2);
		auto threads = vector<thread>(count);
 
		thread([&] {
			fair_start.arrive_and_wait();
 
			auto start = steady_clock::now();
			auto sec = 0;
 
			while (run)
			{
				auto cnt = 1;
				for (auto& count : counts)
					cout << fixed << "Cnt " << cnt++ << ":\t" << count << "\t / \t" << (100.0 * count / total) << " % \n";
 
				cout << "Total:\t" << total << "\nTime:\t" << duration_cast<seconds>(steady_clock::now() - start).count() << "s\n" << endl;
 
				this_thread::sleep_until(start + seconds(++sec));
			}
		}).detach();
 
		auto worker = [&](auto x) {
			fair_start.arrive_and_wait();
 
			while (run) {
				bucket.consume(N);
				total += N;
				counts[x] += N;
			}
		};
 
		thread([&] {
			bucket.drain();
 
			fair_start.arrive_and_wait();
 
			this_thread::sleep_for(3s);
 
			bucket.set_rate(1s);
			bucket.set_capacity(1000000);
 
			this_thread::sleep_for(3s);
 
			bucket.refill();
		}).detach();
 
		all(threads) = thread(worker, --count);
 
		cin.get();
		run = false;
		all(threads).join();
	} catch (exception& ex) {
		cerr << ex.what() << endl;
	}
}

视频

看代码

#include <list>
#include <vector>

template <template <typename T, typename Alloc = std::allocator<T>>
          class Container>
[[nodiscard]] constexpr auto transform_into(auto f, const auto &input) noexcept(
    noexcept(f(input.front())))
  requires requires { f(input.front()); }
{
  Container<decltype(f(input.front()))> retval;
  retval.reserve(input.size());

  for (auto &&value : input) {
    retval.push_back(f(value));
  }

  return retval;
}

int main() {
  std::list<int> data{1, 2, 3, 4};

  const auto result =
      transform_into<std::vector>([](const auto i) { return i * 1.1; }, data);

  static_assert(std::is_same_v<decltype(result)::value_type, double>);
}

只能说,这个例子不配这种级别的抽象

新项目介绍/版本更新


本文永久链接

如果有疑问评论最好在上面链接到评论区里评论,这样方便搜索,微信公众号有点封闭/知乎吞评论

看到这里或许你有建议或者疑问或者指出错误,请留言评论! 多谢! 你的评论非常重要!也可以帮忙点赞收藏转发!多谢支持! 觉得写的不错那就给点吧, 在线乞讨 微信转账