🌓

brpc(1)—bthread执行器

本人最近一个月由于绩效不错,奋斗劲反而没了,比较摆烂,深感惭愧。特此挖坑,给我好好学brpc!!!

阅读全文

计算(2)——GPU计算和大模型

前面提到,CPU运算的瓶颈往往不在于计算,而在于内存、存储和网络。相比于设计算法模型,CPU计算更侧重于工程。

相比下,GPU运算才是真正的高性能运算,虽然它的瓶颈同样可能来自GPU、显存、存储和网络,但提高计算能力、设计优秀的算法、编写高性能低开销的算子,是GPU运算的核心。GPU运算的典型场景就是大模型。

阅读全文

计算(1)——CPU计算和大数据

由于本人是存储方向,前面的文章大多数是关于存储的。计算机系统=计算+存储,其中计算包括计算单元(CPU、GPU)、cacheline和内存态的处理, 而存储特指IO处理。计算可分为CPU计算和GPU计算两部分,前者的典型场景是操作系统处理、进程执行、互联网业务处理和大数据和OLAP处理等。后者的典型场景是大模型的训练推理。

本文介绍CPU计算。CPU计算主要包括业务处理和大数据处理两部分,CPU运算的瓶颈往往不在于计算,而在于内存、存储和网络(可能这是ddia 把大数据计算也放到内的原因)。相比于算法模型,CPU计算更侧重于工程。

阅读全文

编程语言——C++右值和右值引用

C++右值和右值引用是最容易用错的特性之一,相关教程普遍写得不清晰。实际上,只有在所有权转移时才需要使用右值引用,移动语义移动的是所有权,完美转发转发的也是所有权。

总结

先放总结

  1. 左值引用的语义是绑定、const指针和不转移所有权(rust称为借用),右值引用的语义是绑定、const指针和转移所有权
  2. 是否使用右值引用,取决于是否要转移变量所有权。右值引用和左值引用在传参时都不会调用构造函数,性能一样高效。
  3. 如果是字面量, 如果只是读字面量,直接用const& 参数接收即可 (最常见)。如果要转移所有权,例如使用容器时,字面值在当前函数创建随即交给容器,字面量的生命周期后续由容器负责。这时候容器函数应该使用&&,例如emplace()的接口就是右值引用。const&和&&的开销都是一次字面量的构造函数
  4. 如果是左值,如果需要转移所有权,推荐使用&&和std::move()。如果不要, 用左值引用或指针即可。是不是要转移所有权,在于你想要这个左值变量在当前函数析构,还是在函数外析构
  5. 新建对象,通过传std::move()左值,可以通过移动构造加快构造速度。如果传字面量,通过可以通过移动构造加快速度。但如果传左值,则需要调用拷贝构造函数。
  6. 函数返回值不要考虑右值引用,右值引用只在函数参数上使用

阅读全文

编程语言——C++协程和高性能编程

C++可以写出性能高效的程序,一个原因来自语言本身的因素,例如

  1. C++程序编译器可以进行优化,编译直接得到机器码,这让编译后需要执行的指令更少(解释器性能比编译期差的主要原因就是是解释器单行编译执行,而编译器是文件编译执行,获得的信息更多,优化空间更大。明确的类型信息也让编译期获得内存信息,可以在无须创建对象情况下进行优化)
  2. 没有虚函数的C++程序编译后的执行码和C语言一样,没有golang interface{}, java 虚函数等额外的内存开销。C++的class, template等功能抽象不会带来额外的性能开销
  3. C++可以直接管理内存,轻松写出内存零拷贝的程序,无需GC额外的性能开销。

除了语言本身的因素,生态因素对于高性能同样重要。用户程序不可能每次都造轮子,如果没有高性能的库,C++不会成为性能高效程序的首选。例如Python语言的性能虽然差,但python有tensorflow, pytorch等高性能神经网络框架,这让python写出的神经网络性能同样高效。

dpdk和spdk工具链让C++开发高性能网络服务器和高性能存储服务变得容易,GPU等新硬件和cuda等生态让C++成为高性能计算的基础。C++20提供了协程支持,deepseek 开源的3FS 就是C++20高性能编程的典型例子。

阅读全文

存储——谈存储文件系统

文件系统是组织磁盘的形式, 文件系统提供了文件元数据信息,以及文件内容寻址能力(即将文件offset+length映射为磁盘位置);另一方面适配不同的磁盘硬件,让用户程序无须考虑硬件的区别。对用户程序来说,存储等于文件系统。

文件系统整体可以由客户端、元数据服务、数据服务三个部分组成, 包括read, write, setattr, getattr, lookup, readdir, create, rename, hardlink, symlink, readlink, remove等12个接口,支持随机读写文件。

为什么要有分布式文件系统? 1. 提供水平可扩展, 海量高性能存储 2. 提供多客户端的并发访问服务

阅读全文

leveldb(2)—线程模型和并发控制

leveldb前台可并发读,但只能串行写。

leveldb memtable通过无锁skiplist结构 支持读写并发

leveldb的后台线程只有一个,只处理compaction这一个任务, leveldb的minor compaction和major compaction是一起处理的

阅读全文

redis(2)——网络处理和持久化

redis 通过网络服务接收远程命令,进行处理,将执行结果返回。

redis 4.0版本监听网络请求,处理请求和命令都由一个主线程完成。

  1. 好处是redis 主IO路径无须加锁保护。
  2. 缺点是redis 不可处理耗时长的命令,这在使用自定义命令和复杂lua脚本里需要注意。此外redis 无法利用多核cpu,这对部署机器的cpu单核能力有要求。

redis的rdb和aof 都是经典的数据持久化/备份手段,rdb关注数据库的数据, aof关注数据库的操作。

阅读全文

redis(1)——数据结构

redis 是最值得学习的开源项目,

  1. 它广泛应用
  2. 它实现了单机缓存数据库,同时支持网络访问、复制、集群、订阅等高级特性
  3. redis依赖很少,数据结构、日志等基础库也是自行实现,有利于学习
  4. redis代码精悍、质量高,甚至很难找到优化点。

本文阐释redis数据结构,redis 数据结构设计的一大特点是节省内存。原因是redis作为内存数据库需要节省内存使用(连续内存的数组能减少内存碎片)。这对架构设计同样有借鉴意义,通过节省元数据内存使用来把更多元数据/索引放在内存上,可以大大提高处理的吞吐和延迟。

阅读全文

leveldb(1)—概览

leveldb 可分为五大块。db, iterator,version, memtable+log, tablefile。

leveldb 的架构图资料上有很多,可以参考网站

阅读全文