auto-cfs-cores C++ 版 automaxprocs

之前写了一篇分析 uber 的 automaxprocs 的源码,后面抽个了时间写了一个 C++ 版本的。

其一是为了加深对原库的理解;其二是避免太长时间没写 C++ 手生,以及顺带体验一下 C++ 17 的感觉。

代码在:https://github.com/kingsamchen/Eureka/tree/master/auto-cfs-cores

Read More

uber automaxprocs 源码分析

最近直播内部的 golang 服务都使用了 uber 出品的 automaxprocs 这个库。

据伟大的 @ice 马总说,这个库解决了一个困扰B站整个golang(container)技术栈一年多的问题。

出于好奇这个库到底做了什么 magic,能够解决这个持续了一年多的 pain in the ass,抽了一点时间,稍微翻了一下库的源代码,记录如下。

automaxprocs 解决了什么问题

线上容器里的服务通常都对 CPU 资源做了限制,例如默认的 4C。

但是在容器里通过 lscpu 仍然能看到宿主机的所有 CPU 核心:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
rchitecture:          x86_64
CPU op-mode(s): 32-bit, 64-bit
Byte Order: Little Endian
CPU(s): 48
On-line CPU(s) list: 0-47
Thread(s) per core: 2
Core(s) per socket: 12
Socket(s): 2
NUMA node(s): 2
Vendor ID: GenuineIntel
CPU family: 6
Model: 79
Model name: Intel(R) Xeon(R) CPU E5-2650 v4 @ 2.20GHz
Stepping: 1

这就导致 golang 服务默认会拿宿主机的 CPU 核心数来调用 runtime.GOMAXPROCS(),导致 P 数量远远大于可用的 CPU 核心,引起频繁上下文切换,影响高负载情况下的服务性能。

Read More

一个轮子:基于 token bucket 的 rate-limit

地址:https://github.com/kingsamchen/Eureka/tree/master/token-bucket-rate-limit

Read More

Windows 上使用 Git Tips 两则

Windows Terminal 中 git log 显示 UTF-8 编码的中文

这个准确的说其实是 powershell 自己的问题。

Read More

复习:DCLP 和 Memory Reordering

本文是对 Jeff Preshing 的 Double Checked Locking Is Fixed in C++ 11 笔记。

0x00 传统实现

因为 synchronization 只需要在实例第一次创建时保证;此后( instance != nullptr 时)都不需要锁来保证 synchronization。

在第一次判断实例为空和上锁之间存在一个 potential race,因此上锁后需要再一次判断实例是否为空。

这也是 double checked 的来由。

所以一个传统但并不100%正确的 DCLP 实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Singleton {
public:
static Singleton* GetInstance()
{
if (instance == nullptr) {
std::lock_guard<std::mutex> lock(mtx);
if (instance == nullptr) {
instance = new Singleton(); // <-- key point
}
}

return instance;
}

private:
// omit explicit static field initialization.
static std::mutex mtx;
static Singleton* instance;
};

这基本是早起 C++ DCLP 的实现架子。

0x01 问题:内存乱序以及为什么锁帮不了忙

语句

1
instance = new Singleton();	

在 C++ 中实际上等效于

1
2
3
tmp = operator new(sizeof(Singleton));  // step 1: allocate memory via operator new
new(tmp) Singleton; // step 2: placement-new for construction
instance = tmp; // step 3: assign addr to instance

注意:其中除了分配内存是固定第一步之外,构造对象和赋值内存地址的生成代码顺序是由编译器自己决定。这里的顺序只是一种可能性。

Read More

ASIO Buffer 使用简记

近段有相当一部分时间在熟悉和练习 ASIO

练习过程中发现 ASIO 中如何使用&管理 buffer 是新手大概率会遇到的问题。

结合最近几个 practice demo,稍微简单总结了一下使用经验:

0x00

const_buffermutable_buffer 是两个 fundamental buffer classes。二者的区别在语义上表达的很明显了。

实现上二者提供的接口非常一致,除了一个面向 const void*,另一个面向 void*。这点可以从 ctor 和 data() 中看出。

另外,为了和 C++ 现有的 const cast semantics 保持一致,一个 mutable_buffer 对象可以 implicitly converted to const_buffer

Read More

避免在 Linux 上使用 signals

首先承认这个标题乍看之下很像 troll,但真的不是 troll;“避免使用”总的来说更接近 whenever possible 的意思。

另外,这篇 post 面向的主要是偏底层的、直接使用 system calls 或其 runtime wrapper(glibc)的开发者,最典型的比如 C/C++ 开发者。

其他语言的开发者通常因为要么 runtime “屏蔽”了这部分内容(如 Java);要么 runtime 自身对这部分做了较大的抽象/改造(如 Golang),因此很难对此 post 提到的各种观点/做法产生共鸣。

FYI:自从工作后中文写作能力一直在退化,因此这篇文章如果存在语句不通顺或者用词不当的地方,烦请见谅。

0x00 背景回顾:什么是 signal?

signal 源自 Unix,后来成为 POSIX 标准的一部分,现在则被几乎所有的 *nix 系统支持。

signal 本质上是一种通讯机制,用于系统在某个事件(event)发生时通知某个进程(或线程)。

Read More