C++ 中文周刊 2024-05-04 第156期

周刊项目地址

公众号

qq群 点击进入

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

欢迎投稿,推荐或自荐文章/软件/资源等,评论区留言

本期文章由 HNY 赞助


资讯

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

编译器信息最新动态推荐关注hellogcc公众号 本周更新 2024-05-01 第252期

文章

Mastering C++ with Google Benchmark

教了一些google测试使用案例,这里直接贴一下吧,还算常用

分段采集

static void sorting(bm::State &state) {

    auto count = static_cast<size_t>(state.range(0));
    auto include_preprocessing = static_cast<bool>(state.range(1));

    std::vector<int32_t> array(count);
    std::iota(array.begin(), array.end(), 1);

    for (auto _ : state) {

        if (!include_preprocessing)
            state.PauseTiming();

        // Reverse order is the most classical worst case,
        // but not the only one.
        std::reverse(array.begin(), array.end());
        if (!include_preprocessing)
            state.ResumeTiming();

        std::sort(array.begin(), array.end());
        bm::DoNotOptimize(array.size());
    }
}

BENCHMARK(sorting)->Args({3, false})->Args({3, true});
BENCHMARK(sorting)->Args({4, false})->Args({4, true});

不同策略压测


template <typename execution_policy_t>
static void super_sort(bm::State &state, execution_policy_t &&policy) {

    auto count = static_cast<size_t>(state.range(0));
    std::vector<int32_t> array(count);
    std::iota(array.begin(), array.end(), 1);

    for (auto _ : state) {
        std::reverse(policy, array.begin(), array.end());
        std::sort(policy, array.begin(), array.end());
        bm::DoNotOptimize(array.size());
    }

    state.SetComplexityN(count);
    state.SetItemsProcessed(count * state.iterations());
    state.SetBytesProcessed(count * state.iterations() * sizeof(int32_t));
}
BENCHMARK_CAPTURE(super_sort, seq, std::execution::seq)
    ->RangeMultiplier(8)
    ->Range(1l << 20, 1l << 32)
    ->MinTime(10)
    ->Complexity(bm::oNLogN);

BENCHMARK_CAPTURE(super_sort, par_unseq, std::execution::par_unseq)
    ->RangeMultiplier(8)
    ->Range(1l << 20, 1l << 32)
    ->MinTime(10)
    ->Complexity(bm::oNLogN);

perf

$ sudo ./build_release/tutorial --benchmark_perf_counters="CYCLES,INSTRUCTIONS"
$ sudo perf stat taskset 0xEFFFEFFFEFFFEFFFEFFFEFFFEFFFEFFF ./build_release/tutorial --benchmark_filter=super_sort

 Performance counter stats for 'taskset 0xEFFFEFFFEFFFEFFFEFFFEFFFEFFFEFFF ./build_release/tutorial --benchmark_filter=super_sort':

       23048674.55 msec task-clock                #   35.901 CPUs utilized          
           6627669      context-switches          #    0.288 K/sec                  
             75843      cpu-migrations            #    0.003 K/sec                  
         119085703      page-faults               #    0.005 M/sec                  
    91429892293048      cycles                    #    3.967 GHz                      (83.33%)
    13895432483288      stalled-cycles-frontend   #   15.20% frontend cycles idle     (83.33%)
     3277370121317      stalled-cycles-backend    #    3.58% backend cycles idle      (83.33%)
    16689799241313      instructions              #    0.18  insn per cycle         
                                                  #    0.83  stalled cycles per insn  (83.33%)
     3413731599819      branches                  #  148.110 M/sec                    (83.33%)
       11861890556      branch-misses             #    0.35% of all branches          (83.34%)

     642.008618457 seconds time elapsed

   21779.611381000 seconds user
    1244.984080000 seconds sys

Messing with lifetime

struct Point { int x, y; };

void foo(unsigned char* buf, size_t len) {
    assert(len == sizeof(Point));
    Point* p = reinterpret_cast<Point*>(buf);
    if (p->x == 0) {
        // ...
    }
}

这段代码是UB,主要问题是buf不能保证活着,所以通常有一种转移大法

void foo(unsigned char* buf, size_t len) {
    assert(len == sizeof(Point));
    Point point;                    // a Point is created
    std::memcpy(&point, buf, len);
    if (point.x == 0) {             // Ok
        // ...
    }
}

怎么避免这种拷贝?就比如说我作为写代码的人我确认这段buf绝对是活的,start_lifetime_as

void foo(unsigned char* buf, size_t len) {
    assert(len == sizeof(Point));
    Point* p = std::start_lifetime_as<Point>(buf);
    if (p->x == 0) {
        // ...
    }
}

我们再来回顾一下new和malloc的区别

new分配了空间(1)然后构造了对象(2),malloc没有构造

实际上从构造的那一刻开始 new还生成了一个活的对象,开始了一个对象的生命周期 start lifetime(3)

那对于placement new来说,就没有 (1)

对于这种buffer转POD对象的场景,就缺少 (3)

我们不知道他是不是活的,这也就是 start_lifetime_as

我们再回到start_lifetime_asreinterpret_cast

实际上reinterpret_cast就是过于强了

对于只读的这么玩只要作者能保证buffer没问题UB也就UB了对付用

但是reinterpret_cast不能保证对应的buffer是不是活的,而从c来的语法,malloc就直接用了,根本就没有UB这一说

struct X { int a, b; };
X *make_x() {
    X *p = (X*)malloc(sizeof(struct X));
    p->a = 1;  // before P0593: UB, no X lives at p
    p->b = 2;  // before P0593: UB, no X lives at p
    return p;
}

按照start_lifetime_as的设计,得这么写

X *p = std::start_lifetime_as<X>( malloc(sizeof(struct X)) );

说了这么一大堆,也没有什么最佳解法。知道这套东西傻逼就好了,只能修修补补

C++极致的性能压榨:assume属性

看一乐

Understand internals of std::expected

expected的大小?https://godbolt.org/z/xx3eWcP8x

#include <iostream>
#include <typeinfo>
#include <expected>
#include <cxxabi.h>

template <typename T, typename U>
void printSizes() {
    int status = 0;
    char *realnameT = abi::__cxa_demangle(typeid(T).name(), 0, 0, &status);
    char *realnameU = abi::__cxa_demangle(typeid(U).name(), 0, 0, &status);
    
    std::cout << "\nType: " << (realnameT ? realnameT : typeid(T).name()) << '\n';
    std::cout << "Size: " << sizeof(T) << '\n';
    
    std::cout << "Type: " << (realnameU ? realnameU : typeid(U).name()) << '\n';
    std::cout << "Size: " << sizeof(U) << '\n';
    
    std::cout << "Sizeof std::expected: ";              
    std::cout << sizeof(std::expected<T, U>) << '\n';
    
    free(realnameT);
    free(realnameU);
}


int main() {
    printSizes<int, std::string>();
    printSizes<int, int>();
    printSizes<int, double>();
    printSizes<int, std::pair<int, int>>();
}

/*
Type: int
Size: 4
Type: std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >
Size: 32
Sizeof std::expected: 40

Type: int
Size: 4
Type: int
Size: 4
Sizeof std::expected: 8

Type: int
Size: 4
Type: double
Size: 8
Sizeof std::expected: 16

Type: int
Size: 4
Type: std::pair<int, int>
Size: 8
Sizeof std::expected: 12
*/

Daily bit(e) of C++ | Optimizing code to run 87x faster

这个是之前的解析文件挑战,大概就是读到hashmap然后聚合一下

作者从map到hash到多线程优化,比用stl实现快很多倍。看个乐。感觉这就是面试题

File modes in C++20

直接贴godbolt和代码吧 https://godbolt.org/z/xc5MjTrob

#include <iostream>
#include <sys/stat.h>

namespace detail
{
    // Tag classes to get suggestive type names into error messages
    struct invalid_permission_character {};
    struct invalid_permission_string_length {};

    // Check a single character in the string, returning
    // the mode_t for that bit-position. Peculiar name means
    // we get a suggestive error message
    template<int position, char accept>
    consteval mode_t expected_character_at_position(const char * const permission_string)
    {
        const char c = permission_string[position];
        if(c == accept) { return 1 << (8-position); }
        if(c == '-') { return 0; }
        throw invalid_permission_character{};
    }

    // Worker function, assumes that it is called only by
    // from_readable_mode() or operator ""_mode, which have
    // done checking beforehand for the validity of s.
    //
    // It's wordy, but that does mean you get a suggestive
    // error message with the bit-position and expected character.
    consteval mode_t from_readable_mode_string(const char * const s)
    {
    return
        detail::expected_character_at_position<0, 'r'>(s) |
        detail::expected_character_at_position<1, 'w'>(s) |
        detail::expected_character_at_position<2, 'x'>(s) |
        detail::expected_character_at_position<3, 'r'>(s) |
        detail::expected_character_at_position<4, 'w'>(s) |
        detail::expected_character_at_position<5, 'x'>(s) |
        detail::expected_character_at_position<6, 'r'>(s) |
        detail::expected_character_at_position<7, 'w'>(s) |
        detail::expected_character_at_position<8, 'x'>(s);
    }
}

// Turns a 9-character permission string like you would get
// from `ls -l` into the mode_t that it represents.
//
// (the [10] is because of the trailing NUL byte)
consteval mode_t from_readable_mode(const char (&permission_string)[10])
{
    return detail::from_readable_mode_string(permission_string);
}

// Turns a 9-character permission string like you would get
// from `ls -l` into the mode_t that it represents.
consteval mode_t operator""_mode(const char *s, size_t len)
{
    if (len != 9)
    {
        throw detail::invalid_permission_string_length{};
    }
    return detail::from_readable_mode_string(s);
}
// Shorthand for printing a line with a string literal
// and also getting the consteval value of the mode_t
// represented by that literal.
#define EXAMPLE(x) \
    std::cout << x << '\t' << std::oct << from_readable_mode(x) << std::dec << '\n'

int main() {
    EXAMPLE("rw-rw----"); // 660
    EXAMPLE("rwxr-xr-x"); // 755
    EXAMPLE("r--------"); // 400
    EXAMPLE("-w-r---wx"); // 243??

    // These won't even compile, but show compile-time
    // bugs being caught.
    //
    // EXAMPLE("bug"); // Not a 9-character literal
    // EXAMPLE("rwxbadbug");

    // Using _mode to indicate what the string means
    // might be easier to read.
    std::cout << "rw-r--r--" << '\t' << std::oct << "rw-r--r--"_mode << std::dec << '\n';

    // These won't even compile, for the same reasons
    //
    // std::cout << "birb"_mode << "------uwu"_mode;

    return 0;
}

Raymood chen环节,看不懂

视频

这里放个预告哈,cppcon视频出完了。后面准备更新cppcon2023了

开源项目介绍

互动环节

时间真快,转眼间假期又结束了

最近有点忙可能更新很不及时。主要还是有内容的文章少,

加上干眼症加重,看屏幕眼睛酸的要死,想把眼珠子抠出来洗洗

最近也发生了很多事情。说不出啥话来

推荐大家都看一看了不起的盖茨比 不要做一个浪漫的傻瓜


上一期

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