C++ 中文周刊 第140期

2024-07-30 14:44:16 浏览数 (2)

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

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

最近在找工作准备面试题,更新可能有些拖沓,见谅

本周内容比较少

本期文章由 YellowHornby HNY 不语 黄亮Anthony 赞助

资讯

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

编译器信息最新动态推荐关注hellogcc公众号 OSDT Weekly 2023-11-29 第230期

文章/视频

  • • On harmful overuse of std::move https://devblogs.microsoft.com/oldnewthing/20231124-00/?p=109059

能返回值优化,不move

  • • 记一次深入内核的数据库高并发性能优化实践 https://zhuanlan.zhihu.com/p/669173594

定位到 mmap_sem 互斥锁冲突,查找来源

哪里会有mmap?

  • • 内存分配器 ptmalloc / tcmalloc 大页
  • • fopen/fseek?

逐个排除定位到是fseek内部有mmap,新版本glibc已经去掉了mmap改了回来,打patch

那么为什么fseek会调用mmap呢?就不能malloc吗 buffer能多大?

本台记者Anien分析是历史原因,为了初始化不清空0,用mmap偷懒了一下。原来大家都偷懒

  • • 深入浅出 LLVM之 DenseMap 源码解析 https://zhuanlan.zhihu.com/p/669307116

densemap就是flatmap-

  • • 写出让人不想维护的 C 代码之是给我 API 的人先动手的: manually_drop 和玩弄访问权限 https://zhuanlan.zhihu.com/p/667689874

看个乐,别学。还是friend注入那套玩意儿

  • • Integer overflow and arithmetic safety in C https://orodu.net/2023/11/29/overflow.html

整型溢出似乎没有好的解决办法,除了多注意/自定义类型加边界检查

介绍了其他语言的解决方法,比如rust 的 overflowing_mul overflowing_add

google内部也有类似的库设计 https://github.com/chromium/subspace/pull/410/files

似乎除了这些,没有更好的办法?

  • • Lightning Talk: Type-safe Dictionaries - Vincent Tourangeau - CppNorth 2023 https://www.youtube.com/watch?v=i9p_qFW6hJ4

一个幽默的typemap

说到typemap映射,第一反应是字符串/typeindex?是的他就这么实现的,不过用了匿名的类

typeindex计算是很慢的。数量大不建议这么玩,这里展示这种可能性

代码语言:javascript复制
#include <map>
#include <string>
#include <iostream>
#include <typeindex>
#include <any>
#include <optional>


using dict = std::map<std::type_index, std::any>;

template <class Name, class T>
struct key final { explicit key() = default;};

template <class Name, class T>
auto get(const dict& d, key<Name, T> k) ->std::optional<T> {
  if (auto pos = d.find(typeid(k)); pos!=d.end()) {
    return std::any_cast<T>(pos->second);
  }
  return std::nullopt;
}

template <class Name, class T, class V>
void set(dict& d, key<Name, T> k, V&& v) {
  constexpr bool convertible = std::is_convertible_v<V, T>;
  static_assert(convertible);
  if constexpr (convertible) {
    d.insert_or_assign(typeid(k), T{std::forward<V>(v)});
  }
}


// key里面的类可以只声明不实现,当tag用,只要唯一就行
using age_k = key<struct _age_, int>;
using gender_k = key<struct _gender_, std::pair<float,float>>;
using name_k = key<struct _name_, std::string>;

constexpr inline auto age = age_k{};
constexpr inline auto gender = gender_k{};
constexpr inline auto name = name_k{};

int main() {
  auto person = dict();
  set(person, age, 14);
  set(person, gender,std::pair{0.5,0.5});
  set(person, name,"ted");
  const auto a = get(person, age);
  const auto g = get(person, gender);
  const auto n = get(person, name);
  std::cout <<*a <<g->first << g->second << *n<<"n";
}

https://godbolt.org/z/z1hvxzf1e 可以自己玩一下

如果真要考虑用,首先不能用typeindex,得实现一个类转字符串,还得保证唯一性,然后用hashmap存就行

另外不用std::any,用create函数之类的代替

这种需求感觉游戏行业有这种场景

  • • Compile-time Bounds Checking in Flux https://tristanbrindle.com/posts/compile-time-bounds-checking-in-flux

对于array,编译期检查越界应该是可能的

代码语言:javascript复制
int get_last_elem(const std::array<int, 5>& arr) {
    return arr.at(5); // oops, off-by-one
}

这种明显越界,编译期能不能抓到?能,但不报错,会直接抛异常,编译器比较信任你,觉得你喜欢异常

代码语言:javascript复制
get_last_elem(std::array<int, 5ul> const&):     # @get_last_elem(std::array<int, 5ul> const&)
        push    rax
        lea     rdi, [rip   .L.str]
        mov     esi, 5
        mov     edx, 5
        xor     eax, eax
        call    std::__throw_out_of_range_fmt(char const*, ...)@PLT

flux库写了个编译期检查的设计

代码语言:javascript复制
int get_last_elem(const std::array<int, 5>& arr) {
    return flux::read_at(arr, 5); // oops, off-by-one
}

编译期就抓到直接报错,如何实现?

代码语言:javascript复制
[[gnu::error("out-of-bounds sequence access detected")]]
void static_bounds_check_failed();


template <typename Index>
void bounds_check(Index idx, Index limit)
{
    if (__builtin_constant_p(idx) && __builtin_constant_p(limit)) {
        if (idx < Index{0} || idx >= limit) {
            /* ...report error at compile time... */
        }
    } else {
        /* ...perform normal run-time bounds check... */
    }

但存在问题,__builtin_constant_p并不是那么可信

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=112296

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=89029

只能说,缘分相信编译器,可能帮你一下

  • • Compiler Options Hardening Guide for C and C https://best.openssf.org/Compiler-Hardening-Guides/Compiler-Options-Hardening-Guide-for-C-and-C .html

安全加固的编译配置

代码语言:javascript复制
-O2 -Wall -Wformat=2 -Wconversion -Wtrampolines -Wimplicit-fallthrough 
-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=3 
-D_GLIBCXX_ASSERTIONS 
-fstrict-flex-arrays=3 
-fstack-clash-protection -fstack-protector-strong 
-Wl,-z,nodlopen -Wl,-z,noexecstack 
-Wl,-z,relro -Wl,-z,now

FORTIFY有性能影响

这些还是要关注一下性能影响,中间三行有影响。如果某些行业必须加固那也没办法

  • • Storing data in pointers https://muxup.com/2023q4/storing-data-in-pointers

tag pointer

指针一般8字节64bits,只用48bits表示内存空间,剩下的16bits有很多可以倒腾的空间,嵌入一些信息

提问: 48bit能表示多大内存?

剩下的这16bit能做什么文章?

  • • x86_64 加标志位区分内核内存还是用户态内存
  • • rsicv也有类似设计
  • • arm是49位内存,其他设计也是类似的

各种硬件系统还有自己的其他设计,利用bit,这里就不展开了

  • • Intel 有 Linear Address Masking (LAM)
  • • AMD有 Upper Address Ignore
  • • rsicv有 pointer masking extension
  • • arm有 Top Byte Ignore (TBI)

这波报菜名大部分都没什么接触的机会。

考虑自定义的场景

  • • fixie trie 用不到的16位用来做bitmap
  • • llvm pointpairpointer 其实有点union双关用法了 https://github.com/llvm/llvm-project/blob/dc8b055c71d2ff2f43c0f4cac66e15a210b91e3b/llvm/include/llvm/ADT/PointerIntPair.h#L64
    • • 暴论 tag pointer 就是union
  • • 显然 ZGC的pointer设计也是tag pointer https://dinfuehr.github.io/blog/a-first-look-into-zgc/
  • • 显然 objc和go的tag pointer自然都是,这俩比较有名,就不列了
  • • 当然 v8 中的pointer compression 自然也是 tag pointer https://v8.dev/blog/pointer-compression
  • • 那么异或链表自然也是tag pointer
  • • 当然酒店传单也是tag pointer,不仅可以订餐还可以点按摩是不是
  • • Lightning Talk: When C Managers Fail... Richard Shepherd - C on Sea 2023 https://www.youtube.com/watch?v=2T5dIwAXCqc

重新考虑单例实现

static 单例 static保证 成功 创建 一次

那么存在构造函数抛异常的可能性。注意你的单例的T是不是可能构造抛异常

可能挽救一下就成了这个德行

代码语言:javascript复制
class Manager {
  Resouce* resource_;
  Manager() : resource_{CreateResource()} {
    if (!resource_) {
      throw std::exception("Not ready")
    }
  }
 public:
  
  static Manager* Instance() {
    try {
      static Manager s;
      return &s;
    } catch (...) {
      return nullptr;
    }
  }
};

只展示代码段,没啥别的说的

  • • A Priority Scheduler for Coroutines https://www.modernescpp.com/index.php/a-priority-scheduler-for-coroutines/

简单加个优先级,是的,没错,用priority_queue

代码语言:javascript复制
#include <coroutine>
#include <iostream>
#include <queue>
#include <utility>

struct Task {
  struct promise_type {
    std::suspend_always initial_suspend() noexcept { return {}; }
    std::suspend_always final_suspend() noexcept { return {}; }

    Task get_return_object() { 
        return std::coroutine_handle<promise_type>::from_promise(*this); 
    }
    void return_void() {}
    void unhandled_exception() {}
  };

  Task(std::coroutine_handle<promise_type> handle): handle{handle}{}
  auto get_handle() { return handle; }
  std::coroutine_handle<promise_type> handle;
};

class Scheduler {
  std::priority_queue<std::pair<int, std::coroutine_handle<>>> _prioTasks;
  public: 
    void emplace(int prio, std::coroutine_handle<> task) {
      _prioTasks.push(std::make_pair(prio, task));
    }

    void schedule() {
      while(!_prioTasks.empty()) {
        auto [prio, task] = _prioTasks.top();
        _prioTasks.pop();
        task.resume();

        if(!task.done()) { 
          _prioTasks.push(std::make_pair(prio, task));
        }
        else {
          task.destroy();
        }
      }
    }

};


Task createTask(const std::string& name) {
  std::cout << name << " startn";
  co_await std::suspend_always();
  std::cout << name << " executen";
  co_await std::suspend_always();
  std::cout << name << " finishn";
}


int main() {
  Scheduler scheduler1;
  scheduler1.emplace(0, createTask("TaskA").get_handle());
  scheduler1.emplace(1, createTask("  TaskB").get_handle());
  scheduler1.schedule();
  Scheduler scheduler2;
  scheduler2.emplace(1, createTask("TaskA").get_handle());
  scheduler2.emplace(0, createTask("  TaskB").get_handle());
  scheduler2.schedule();
}

开源项目介绍

  • • asteria https://github.com/lhmouse/asteria 一个脚本语言,可嵌入,长期找人,希望胖友们帮帮忙,也可以加群384042845和作者对线
  • • Unilang https://github.com/linuxdeepin/unilang deepin的一个通用编程语言,点子有点意思,也缺人,感兴趣的可以github讨论区或者deepin论坛看一看。这里也挂着长期推荐了
  • • https://gitee.com/okstar-org/ok-edu-desktop 一个IM通信软件,做IM的可以关注,现在正在做全面整合阶段,开始组建商业团队阶段,年底开始融资,你参加了就快发财了,会的快来
  • • https://github.com/hanickadot/cthash 编译期sha算法

0 人点赞