minikube usage

文档

安装

  • minikube install

  • docker install

    • 需要配置当前用户操作权限
  • kubectl 安装

    • ubuntu sudo snap install kubectl --classic
    • arch sudo pamac install kubectl

archilinux

driver=virtualbox

1
2
3
4
5
6
7
$ sudo pamac install virtualbox
$ sudo modprobe vboxdrv
$ sudo modprobe vboxnetadp
$ sudo modprobe vboxnetflt

# 启动 minikube
$ minikube start --driver=virtualbox

配置

启用私有仓库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ minikube addons configure registry-creds

Do you want to enable AWS Elastic Container Registry? [y/n]: n

Do you want to enable Google Container Registry? [y/n]: n

Do you want to enable Docker Registry? [y/n]: y
-- Enter docker registry server url: registry.cn-hangzhou.aliyuncs.com
-- Enter docker registry username: <username>
-- Enter docker registry password:

Do you want to enable Azure Container Registry? [y/n]: n
✅ registry-creds was successfully configured
$ minikube addons enable registry-creds

注册

1
2
3
4
5
6
7
8
9
10
11
$ kubectl create secret generic regcred \
--from-file=.dockerconfigjson=$HOME/.docker/config.json \
--type=kubernetes.io/dockerconfigjson \
--namespace=default

# 在 deployment.yml 中添加如下内容
spec:
...
spec:
imagePullSecrets:
- name: regcred

问题

driver=docker 时无法通过宿主机 ip 访问 service

  • service type 设置为 NodePort
  • 无法通过宿主机的 ip 或者 127.0.0.1 + service node port 访问服务

cuda

概念

核函数

核函数是运行在 GPU 设备上的函数,使用 __global__ 定义,且返回类型必须是 void

硬件相关

Streaming Multiprocessor(SM)

  • 多个 SP 加上其他资源组成一个 SM
  • 其他资源包括
    • warp scheduler
    • register
    • shared memery

Streaming Processor(SP)

  • 也称为 CUDA core
  • 最基本的处理单元

img

GPU 中的内存

  • 全局内存(Global Memory)
    • GPU 上所有线程共享访问
  • 共享内存(Shared Memory)
    • 位于 SM 中的一块高速缓存内存
    • 由线程块中的线程共享
  • 寄存器(Register)
    • 每个线程都有对应的本地寄存器缓存

通信

  • block 内的线程可以同步也可以通过 shared memory 通信
  • grid 内的线程共享 global memory

线程组织

  • thread
    • 最小任务单元
  • block
    • 分配给 SM 执行
    • 每个线程块最多有 1024 个线程
    • 可以是一维、二维、三维
  • warp
    • block 中的线程以 warp 为单位进行分组
    • warp 大小为 32(当前)
    • 同一个 warp 中的 thread 以不同数据资源执行相同指令(SIMT,单指令多线程)
    • GPU 执行程序时的调度、执行的基本单元
    • 由 SM 的硬件 warp scheduler 负责调度
  • grid
    • 单独的 kernel 启动的所有线程组成一个 grid
    • grid 中所有线程共享 global memory
    • 多个 block 组成一个线程网格 grid
    • 可以是一维、二维、三维

其他

  • 核函数中 <grid, block>
    • grid
      • block 的组织方式
      • 可以是一至三维
    • block
      • thread 的组织方式
      • 可以是一至三维
  • 指定网格结构
    • dim3 grid(8, 4, 2);

CHATGPT

如何提高 gpu 的吞吐能力

要提高 GPU 的吞吐能力,可以考虑以下几个方面的优化:

  1. 并行化和向量化:利用 GPU 的并行处理能力和 SIMD(Single Instruction, Multiple Data)特性,尽可能将任务划分为独立的子任务,并对数据进行向量化操作。这样可以最大程度地利用 GPU 的计算资源。
  2. 内存访问优化:合理管理数据的访问模式,尽量减少内存访问的延迟和带宽消耗。可以使用共享内存来提高访问速度,通过数据重用和局部性原则减少全局内存访问次数。
  3. 内核函数设计:设计高效的内核函数,在保持功能完整性的同时,最大限度地减少同步和分支等开销。避免线程间冲突和竞争条件,提高执行效率。
  4. 内存传输优化:尽量减少 CPU 和 GPU 之间的数据传输,可以通过使用异步内存传输、数据压缩、数据预取等技术来减少传输时间和带宽占用。
  5. 算法选择和优化:针对具体的问题和数据特点,选择适合 GPU 并行计算的算法和数据结构。根据硬件架构的特点,对算法进行调整和优化,例如减少不必要的计算步骤、减少数据依赖等。
  6. 合理利用异构计算:除了 GPU,可以结合 CPU 和其他加速器(如FPGA)进行协同计算,充分利用不同硬件之间的优势来提高整体系统的吞吐能力。
  7. 使用专门的优化工具和库:针对特定的 GPU 架构和编程模型,使用专门的优化工具和库,例如 NVIDIA 的 CUDA Toolkit 提供的性能分析工具和优化指南。

以上是一些常见的方法,但具体的优化策略会取决于具体的应用场景和问题需求。在实际优化过程中,通过性能分析和测试来识别瓶颈,并基于具体情况采取相应的优化手段是非常重要的。

manjaro usage

包管理工具

  • pacman / pamac (GUI)
  • AUR
  • snap
  • flatpak

搜索包

初始化

1
2
3
4
# 安装并启用 sshd.service
$ sudo pacman install openssl openssh
$ sudo systemctl enable sshd.service
$ sudo systemctl start sshd.service

pacman

查看帮助

1
2
$ pacman -h
$ pacman -S -h

切换源

1
2
3
4
5
6
# manjaro
# 1. 刷新
$ sudo pacman-mirrors -i -c China -m rank
# 选择合适的源
# 2. 更新
$ sudo pacman -Syy

或选择最近源。

1
$ sudo pacman-mirrors --geoip && sudo pacman -Syyu

参数

1
2
3
4
5
6
7
# -S
-S 安装
-S --needed --noconfirm
--needed: 跳过已经安装到最新版本的包
--noconfirm: 跳过确认

-R 卸载

查看安装的包

1
2
3
4
# 所有安装的包
$ pacman -Q
# 查询指定包
$ pacman -Q vim

搜索包

1
$ pacman -Ss vim

AUR

AUR(Arch User Repository)。

AUR Helpers

AUR helpers 可以帮助我们搜索、下载、编译 AUR 包,常见的 AUR helpers 有 yay、paru等。

Paru

安装

1
2
3
4
$ sudo pacman -S --needed base-devel
$ git clone https://aur.archlinux.org/paru.git
$ cd paru
$ makepkg -si

使用示例

1
2
# 搜索
$ paru -Ss gcc7
1
2
3
4
5
6
7
8
9
10
paru <target> -- Interactively search and install <target>.
paru -- Alias for paru -Syu.
paru -S <target> -- Install a specific package.
paru -Sua -- Upgrade AUR packages.
paru -Qua -- Print available AUR updates.
paru -G <target> -- Download the PKGBUILD and related files of <target>.
paru -Gp <target> -- Print the PKGBUILD of <target>.
paru -Gc <target> -- Print the AUR comments of <target>.
paru --gendb -- Generate the devel database for tracking *-git packages. This is only needed when you initially start using paru.
paru -Bi . -- Build and install a PKGBUILD in the current directory.

其他

可以手动搜索包,然后使用 git clone,并使用命令 makepkg -s 编译,使用命令 makepkg -i 安装,或直接使用 makepkg -is 命令编译安装。也可以通过 pamac 命令使用 aur。

1
2
3
4
5
6
# 安装 pamac
$ pacman -S pamac-cli
# 搜索 aur 包
$ pamac search ttf-ms-fonts --aur
# 安装
$ pamac build ttf-ms-fonts

安装 AUR 包

pamac build pkgp

安装官方包

pamac install pkg

大语言模型小试

Vicuna

VicunaGithub - FastChat

运行

安装依赖

1
2
3
4
5
6
$ pip3 install torch transformers fschat
# 安装 lfs
## ubuntu
sudo apt install git-lfs
# 验证
$ git lfs install

下载

1
2
3
4
# llama 原始模型
$ git clone https://huggingface.co/decapoda-research/llama-13b-hf
# vicuna 模型
$ git clone https://huggingface.co/lmsys/vicuna-13b-delta-v1.1

更新参数

1
2
3
4
$ python3 -m fastchat.model.apply_delta \
--base-model-path /data/llama-13b-hf \
--target-model-path /data/vicuna-13b \
--delta-path /data/vicuna-13b-delta-v1.1

Serving

1
2
3
nohup python3 -m fastchat.serve.controller &
nohup python3 -m fastchat.serve.model_worker --model-path /data/vicuna-13b --device cpu &
nohup python3 -m fastchat.serve.gradio_web_server &

LLM - startup

问题排查

nvidia-container-cli: initialization error: load library failed: libnvidia-ml.so.1 #154

详见这里

1
2
3
4
5
6
sudo apt-get purge docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin docker-ce-rootless-extras
sudo rm -rf /var/lib/docker
sudo rm -rf /var/lib/containerd
for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo systemctl restart docker

no compatible GPUs were discovered / Failed to initialize NVML: Unknown Error

Ollama docker 容器找不到 GPU。

如果使用 docker compose,配置如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
services:
ollama:
image: ollama/ollama:latest
restart: always
hostname: ollama
runtime: nvidia
user: root
ports:
- '11434:11434'
volumes:
- /data/docker/llm/ollama:/root/.ollama
networks:
- default
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 1
capabilities: [gpu]

解决。宿主机编辑 /etc/nvidia-container-runtime/config.toml

1
no-cgroups = false # 修改, true -> false

重启 docker。

1
sudo systemctl restart docker

vector

配置

source

file

1
2
3
4
5
6
7
[sources.test]
type = "file"
include = [ "/tmp/test.log" ]
line_delimiter = "$$\n"

# unicode
line_delimiter = "\u0003\u0004\n"

line_delimiter

1
2
3
4
5
6
7
8
9
\b         - backspace       (U+0008)
\t - tab (U+0009)
\n - linefeed (U+000A)
\f - form feed (U+000C)
\r - carriage return (U+000D)
\" - quote (U+0022)
\\ - backslash (U+005C)
\uXXXX - unicode (U+XXXX)
\UXXXXXXXX - unicode (U+XXXXXXXX)

example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[api]
enabled = true
address = "0.0.0.0:8686"

[sources.test]
type = "file"
include = [ "/tmp/test.log" ]
line_delimiter = "$$\n"

[sinks.console]
inputs = ["test"]
target = "stdout"
type = "console"
encoding.codec = "text"

安装

docker

文档

1
2
3
4
5
6
7
8
9
10
# pull image
$ docker pull timberio/vector:0.30.0-debian
# create container
$ docker run \
-d \
-v $PWD/vector.toml:/etc/vector/vector.toml:ro \
-v /tmp/test.log:/tmp/test.log \
--name vector \
-p 8686:8686 \
timberio/vector:0.30.0-debian

trouble shooting

日志滚动后无法更新

可能原因

vector 在日志文件轮转之后,默认会计算日志文件的前n个字节的 check sum, 如果值一直,则不会监听新的文件。如果日志文件有固定的前缀,那么每次日志轮转,check sum 会保持一致,那么会无法切换至新的文件。
比如,vector 一开始监听 debug.log 文件,文件到达一定条件后做轮转操作,移动 debug.logdebug.log.1,创建新的 debug.log 文件。如果 debug.logdebug.log.1 有相同的 check sum 值,那么 vector 不会监听新的文件。

解决方案

修改 fingerprinter 的计算策略为 device_and_inode

1
2
3
4
5
6
7
8
[sources.debug-log]
type = "file"
include = ["/app/name/debug.log"]
max_line_bytes = 409600 # optional, default, bytes
ignore_older = 10 #10s
line_delimiter = "\u001e\u001f\n"
[sources.debug-log.fingerprinting]
strategy = "device_and_inode"

相关 issues

rpc 基础

服务端

请求处理模型

  • 单线程阻塞模型
  • 多线程/进程模型
  • 线程池模型
  • 异步非阻塞模型
  • 协程模型

异步非阻塞模型

epoll

epoll(event poll) 是 Linux 内核提供的一种高级 IO 多路复用机制,可以监听多个文件描述符上的事件,并将这些事件通知给应用程序,从而实现异步的 IO 操作。

epoll 监听的 fd(file descriptor)集合是常驻内核的,它有 3 个系统调用 (epoll_create, epoll_wait, epoll_ctl),通过 epoll_wait 可以多次监听同一个 fd 集合,只返回可读写那部分。

来自 ChatGPT

epoll 是 Linux 内核提供的一种高级 IO 多路复用机制,它可以同时监听多个文件描述符上的事件,并将这些事件通知给应用程序。与传统的 Select 和 Poll 机制相比,epoll 具有更高的性能和更好的可扩展性。

具体来说,epoll 通过以下几个关键部分来实现 IO 多路复用:

  1. epoll_create:创建一个 epoll 对象,并返回其对应的文件描述符。
  2. epoll_ctl:向 epoll 对象中添加或删除指定的文件描述符,以及设置相应的事件类型等参数。
  3. epoll_wait:等待指定文件描述符上的事件发生,并将这些事件收集起来返回给应用程序。

在使用 epoll 进行 IO 多路复用时,通常采取如下的处理流程:

  1. 创建 epoll 对象,并将需要监听的文件描述符添加到 epoll 的事件列表中。
  2. 使用 epoll_wait 等待事件的发生,当某个文件描述符上有事件发生时,就会触发 epoll_wait 返回。
  3. 根据事件类型进行相应的处理。如果是可读事件,则读取数据并进行处理;如果是可写事件,则进行写操作。
  4. 处理完毕后,将文件描述符从 epoll 的事件列表中删除。

需要注意的是,epoll 机制支持三种事件触发模式,包括边缘触发(Edge Triggered)、水平触发(Level Triggered)和一次性触发(One-Shot)。其中,边缘触发模式是最高效的一种模式,它只在文件描述符状态发生变化时才会通知应用程序;而水平触发和一次性触发模式需要更多的系统调用,因此相对较慢。

总的来说,epoll 机制具有高效、可扩展、灵活等优点,在高并发场景下被广泛使用。

epoll 如何通知应用程序

在 epoll 机制中,当 I/O 事件发生时,应用程序通过调用 epoll_wait 函数等待事件的发生。该函数会阻塞当前线程,直到有事件发生或者超时,然后将所有已经就绪的文件描述符加入到一个由 epoll_event 结构体组成的数组中,最后返回数组中的事件数量。

每个 epoll_event 结构体包含了一个就绪的文件描述符和该文件描述符上发生的事件类型。应用程序随后可以遍历这个数组,并根据文件描述符和其对应的事件类型进行相应的处理(如读取数据、写入数据等)。

需要注意的是,在边缘触发模式下,如果有多个事件同时发生,epoll_wait 只会返回一次通知。因此,应用程序在处理完某个文件描述符上的事件后,需要重新调用 epoll_wait 函数获取下一轮事件通知。

总之,epoll 机制的通知方式是通过将就绪的文件描述符和相应的事件类型打包成一个结构体,并返回给应用程序,从而实现了高效、可扩展、高并发的 IO 处理。

select

select 只有一个系统调用,每次要监听都要将其从用户态传到内核,有事件时返回整个集合。

来自 ChatGPT

select 是一种基于 IO 多路复用的系统调用,它可以同时监听多个文件描述符上的事件,并在其中有事件发生时通知应用程序进行处理。select 机制主要由以下三个部分组成:

  1. fd_set 数据结构:用于存储需要监听的文件描述符集合。
  2. select 函数:用于将需要监听的文件描述符集合传递给内核,以及等待事件的发生并返回事件列表。
  3. 相应的文件操作函数:例如 read、write 等,用于对文件描述符执行相应的 IO 操作。

在使用 select 进行 IO 多路复用时,通常采取如下的处理流程:

  1. 创建 fd_set 数据结构,并将需要监听的文件描述符添加到该数据结构中。
  2. 调用 select 函数等待事件的发生。当某个文件描述符上有事件发生时,就会触发 select 返回。
  3. 使用相应的文件操作函数对事件进行处理。如果是可读事件,则读取数据并进行相应的处理;如果是可写事件,则进行写操作。
  4. 循环执行上述过程,直到关闭连接或出现异常。

需要注意的是,在 select 模式下,文件描述符数量受到限制,通常不能超过系统默认值(例如 1024)。此外,在高并发情况下,select 的效率较低,因为每次都需要对所有监控的文件描述符进行扫描,无法有效利用 CPU 和内存等资源。因此,在实现高并发服务器时,通常采用更为高效的机制,如 epoll 和 kqueue 等。

brpc

自定义协议

示例

1
2
3
4
5
6
7
8
9
10
Protocol protocol = { ParseMessage,
SerializeRequest, PackRequest,
ProcessHttpRequest, ProcessResponse,
VerifyRequest, ParseServerAddress,
GetMethodName,
CONNECTION_TYPE_SINGLE,
"h2" };
if (RegisterProtocol(PROTOCOL_H2, protocol) != 0) {
exit(1);
}

客户端调用顺序

  • 去程
    • SerializeRequest
    • PackRequest
  • 回程
    • ParseMessage
    • ProcessResponse

bthread

初始化

1
2
3
4
5
6
7
8
9
10
bthread_t bid;
bthread_attr_t attr;
bthread_attr_init(&attr);

auto s = [](void *) -> void * {
auto r = bthread_usleep(1000 * 1000 * 3);
return nullptr;
};
bthread_start_urgent(&bid, &attr, s, nullptr);
bthread_join(bid, nullptr);

使用

1
2
3
4
5
6
7
8
#include "bthread/bthread.h"
auto s = [](void *) -> void * {
bthread_usleep(1000 * 15);
};

bthread_t th1;
bthread_start_urgent(&th1, &attr, s, nullptr);
bthread_join(th1, nullptr);

timer_thread

NOTE: 执行一次,且时间为时间戳(通常是未来时间),非间隔执行。

1
2
3
4
5
6
#include "bthread/timer_thread.h"
bthread::TimerThread timer;
bthread::TimerThreadOptions options;
timer.start(&options);
// 或者 timer.start(nullptr);
timer.schedule(ConfigData::load, this, {butil::seconds_from_now{5}}); // 5秒后执行一次

自定义 Period Timer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
// brpc_period_timer.h
typedef void (*BrpcPeriodTimerFunction)(void *);

struct BrpcTimerContext {
BrpcPeriodTimerFunction function;
void *argument;
int interval_s;
bthread::TimerThread *timer;
};

class BrpcPeriodTimer {
public:
BrpcPeriodTimer();
~BrpcPeriodTimer();

static void Wrapper(void *ctx);
void Schedule(BrpcTimerContext *);
private:
bthread::TimerThread timer_;
};

// brpc_period_timer.cpp
BrpcPeriodTimer::BrpcPeriodTimer() {
timer_.start(nullptr);
}

BrpcPeriodTimer::~BrpcPeriodTimer() {
timer_.stop_and_join();
}

void BrpcPeriodTimer::Wrapper(void *ctx) {
auto c = (BrpcTimerContext *) ctx;
c->function(c->argument);
c->timer->schedule(BrpcPeriodTimer::Wrapper, ctx, butil::seconds_from_now(c->interval_s));
}

void BrpcPeriodTimer::Schedule(BrpcTimerContext *ctx) {
auto c = (BrpcTimerContext *) ctx;
ctx->timer = &timer_;
timer_.schedule(BrpcPeriodTimer::Wrapper, ctx, butil::seconds_from_now(c->interval_s));
}

// test
TEST(Utils, PeriodTimer) {
auto payload = [](void *) {
std::cout << "run payload" << std::endl;
};

BrpcPeriodTimer timer;
BrpcTimerContext ctx{payload, nullptr, 1};
timer.Schedule(&ctx);

sleep(10);
}

// output
Testing started at 17:45 ...
run payload
run payload
run payload
run payload
run payload
run payload
run payload
run payload
run payload
Process finished with exit code 0

data struct

数据结构

概念 标签 说明
平衡树 二叉树 可以保证在插入、删除等操作后维持树的平衡性,以保证其查找效率
B树 多路搜索树、自平衡搜索树 每个节点可以拥有多于两个的子节点,常用于磁盘文件系统和数据库索引等场景
B+树 多路搜索树、自平衡搜索树 优化了B树的节点结构和叶子节点指针,提供了更好的范围查询性能。它在数据库索引和文件系统中广泛应用,能够高效地支持各种查询操作
B-树 多路搜索树、自平衡搜索树 具有多个关键字的非叶子节点和链表连接的叶子节点。它在节点结构、关键字排序和范围查询性能等方面与B树和B+树有所不同。B-树适用于大规模数据集的存储和检索,能够提供高效的访问性能
红黑树 平衡二叉查找树 gpt-3.5-turbo-0613由于其插入、删除和查找的时间复杂度均为O(log(n)),因此被广泛应用于STL中的set和map容器
Trie树(字典树) 多叉树结构 用于检索字符串数据集中的键值,常用于搜索引擎词频统计等场景
完全二叉树 连续内存存储,可用于实现优先队列等数据结构。堆分为大根堆和小根堆两种类型
Huffman树 二叉树 一种带权路径长度最短的二叉树,常用于数据压缩和编码等场景