编辑
2026-06-23
undefined
00

在说LLM之前,我们先来了解下AI到底是什么,先从AIGC开始说起。

AIGC

当AIGC这个词在国内火爆的同时,海外更流行的是另外一个词:Generative AI,即生成式AI。 从字面上来看,生成式AI和AIGC之间关系是:生成式AI所生成的内容就是AIGC。

AIGC 又称生成式 AI (Generative AI),是继专业生产内容(PGC, Professional-generated Content)、用户生产内容(UGC, User-generated Content)之后的新型内容创作方式,可以在对话、故事、图像、视频和音乐制作等方面,打造新的数字内容生成与交互形式。

ChatGPT、GitHub Copilot、Midjourney等都属于生成式AI。

编辑
2026-06-23
undefined
00

操作系统知识和概念

并行与并发

并行并发其实是不同的概念,官方有专门的介绍视频,大家有兴趣可以去看看。我个人是这么理解:

  • 并行:同一时刻执行多个任务。通过多核的 CPU 可以让多个任务做到真正意义上的同时运行,每个任务都在不同的处理器核心或线程中运行。
  • 并发:同一时间段内执行多个任务。通过时间片或者让出控制权来实现任务切换,达到“同时”运行多个任务的目的。其本质是在任意时刻都只有一个任务处于执行态中,而在其余任务则是通过某种算法等待被唤醒执行。

我们以 Node.js 为例来理解并发和并行的概念。

  • 并发:在 Node.js 中,通过事件驱动的非阻塞I/O模型,实现了高并发的处理能力。当一个请求到达时,Node.js 将会创建一个事件,并将其放入事件队列中。然后,Node.js 会立即继续处理下一个请求,而不会等待前一个请求的I/O操作完成。这样,在等待I/O操作返回结果的过程中,Node.js 能够继续处理其他请求,充分利用了CPU的空闲时间,提高了系统的吞吐量和响应速度。这种利用事件循环的方式来处理多个请求的方式就是并发。
  • 并行:在 Node.js 中,可以通过像 PM2 这样的工具来创建多个 Node.js 进程或者集群,每个进程都可以独立处理请求。这样一来,不同的请求可以被分配到不同的进程上处理,从而实现真正的并行处理。通过这种方式,可以充分利用多核CPU的性能,提高系统的并发能力和处理能力。

在开发中,同步和异步是两种常见的方法调用方式,它们在处理任务时有着不同的执行方式和特点。

编辑
2026-06-23
undefined
00

在各种流传甚广的C语言葵花宝典里,一般都有这么一条神秘的规则,不能返回局部变量:

int * func(void) { int num = 1234; /* ... */ return # }

当函数返回后,函数的栈帧(stack frame)即被销毁,引用了被销毁位置的内存轻则数据错乱,重则 segmentation fault。

经过了八十一难,终于成为了 C 语言绝世高手,还是逃不过复杂的堆上对象引用关系导致的 dangling pointer:

编辑
2026-06-23
undefined
00

内存模型

内存模型描述了程序执行的规范。在golang中,这些规范由 goroutine 执行组成,而 goroutine 的执行又由一系列内存操作构成。golang的内存模型规定了多个goroutine读取变量时候,变量的可见性情况。

内存操作可以从以下四个方面进行详细描述:

  • 类型:包括普通数据读取、普通数据写入、同步操作、原子数据读取、互斥操作或通道操作等。
  • 代码中的位置:即操作在程序代码中的具体位置,通常用行号或函数名标识。
  • 内存位置:指正在访问的内存地址或变量。
  • 值:指正在读取或写入的具体数据值。

为了确保多核、多线程环境下的正确性和一致性,内存模型确保了以下几点:

  • 顺序一致性:在单线程情况下,操作的执行顺序与程序代码中出现的顺序一致。这是最简单的模型,假设所有的读写操作按代码中的顺序执行,但实际上现代处理器和编译器都会进行优化,例如乱序执行(Out-of-Order Execution)和指令重排(Instruction Reordering),这使得顺序一致性变得复杂。
  • 可见性:一个线程对变量的修改对于其他线程是可见的。
  • 原子性:保证某些操作(如读写)是不可分割的,即要么完全执行,要么完全不执行。

比较有意思的是,Golang Memory Model 文章作者并不希望读者通过 Memory Model 来理解自己程序的运行方式,如果必须要这样做的话,很可能是程序的编写方式有问题,很可能意味着高昂的维护成本)

编辑
2026-06-23
undefined
00

数组的延伸

在Go语言中,数组是一种固定长度的数据结构,一旦创建,长度就不能改变。这在某些情况下可能会限制它的使用。为了提供更灵活的数据结构,Go引入了切片(slice),它基于数组实现,但具有动态调整大小的能力。

切片的结构

切片的内部结构在src/runtime/slice.go中定义,如下图,它包含三个主要部分:

  • array:指向底层数组的指针。
  • len:切片的长度,即当前切片包含的元素数量。
  • cap:切片的容量,即底层数组能够容纳的元素数量。