## 错误的问题:为韧性而设计 许多工作面试失败并非由于缺乏技术知识,而是对*问题*本身的误解。作者反复遇到一个模糊的要求:“设计一个高韧性的数据库。” 这不是一个数据库问题,而是一个*产品*问题——在不了解特定应用及其需求的情况下无法回答。 作者用一个例子来说明:一个详细的、可用于生产的PostgreSQL解决方案(使用Kubernetes上的CloudNativePG)被否决,而一个简单的“Cassandra”答案却被接受。这突显了一个关键点:韧性并非数据库固有的,而是由其应用场景决定的。 数据库代表着权衡——一致性与可用性(CAP定理)。 Cassandra在写入密集型工作负载(如物联网数据)方面表现出色,但牺牲了强一致性。 这对于需要ACID合规性的金融交易来说是不可接受的。 在选择数据库之前,必须提出关键问题:存储什么数据?查询模式是什么?需要什么一致性和持久性?哪些故障模式至关重要? 选择错误的数据库可能导致合规性问题、数据丢失,最终导致业务失败。真正的专业知识不在于知道*一个*解决方案,而在于提出正确的问题来*找到*解决方案。
## Go 调度器总结
Go 调度器通过将 goroutine 多路复用到较少的操作系统线程来高效管理并发执行。它利用 GMP 模型:**G**oroutine(轻量级任务)、**M**achine(操作系统线程)和 **P**rocessor(具有本地运行队列和缓存的调度上下文)。
每个 P 允许 goroutine 在没有持续锁的情况下运行,并通过工作窃取将工作与阻塞的操作系统线程解耦。调度器的核心循环 `schedule()` 优先运行本地队列中的 goroutine,然后是全局队列(确保公平性),最后从其他 P 窃取。
Goroutine 通过在阻塞时(例如,通道操作)或完成时主动让步与调度器协作。 抢占式调度处理无响应的 goroutine。 由于其状态小,goroutine 之间的上下文切换非常快(50-100 纳秒),从而可以并发运行数百万个 goroutine。
主要特性包括用于响应速度的自旋线程、用于效率的堆栈增长以及协调所有内容的全局调度器状态 (`schedt`)。 该系统允许 Go 以最小的开销实现高并发。
## 发展的分歧:人工智能与动力的转变
人工智能辅助编码的兴起,揭示了开发者社区长期存在的分裂,此前这种分裂被共同的流程所掩盖。许多人哀叹着代码创作的丧失——“优雅”、调试的挣扎、个人烙印——而作者发现自己的悲伤源于不同的原因。
对一些人来说,编程一直关乎*创作过程*,代码本身就是一种艺术。而对作者这样的人来说,他们开始编码是为了*达成结果*,人工智能感觉就像一个自然的发展——另一个让计算机“做事情”的工具。这种动力的差异现在变得非常明显,开发者们在手工编写代码和指导人工智能之间做出选择。
作者承认对不断变化的互联网生态系统和职业前景感到悲伤,这些焦虑与编码行为本身无关。认识到你悲伤的是*什么*——是技术本身还是环境——至关重要。如果技术丧失,一些人可能需要在其他地方寻找满足感,而另一些人可以适应并积极塑造代码周围不断变化的世界。
最终,作者拥抱这种变化,即使到达那里的路径已经改变,也能在有效的产品中找到同样的满足感。
## TPU 与 GPU:深入探讨 Flash Attention
本文详细介绍了将为 GPU 开发的 Flash Attention 内核(在第 4 部分中)移植到 TPU 的尝试,揭示了令人惊讶的性能差异。虽然算法保持不变,但底层的硬件和编译器优化却极大地改变了结果。
最初移植到 JAX/TPU 的版本比融合的标准注意力实现要慢得多。这被追溯到 TPU 的架构:其矩阵乘法单元 (MXU) 专为平铺矩阵运算而设计,并且其大型片上存储器 (VMEM) 允许完整的注意力矩阵驻留在其中,用于较短的序列长度。最初的 JAX 实现,使用 `fori_loop`,阻碍了编译器优化并行性的能力。
切换到 `jax.vmap` – 信号查询块之间的独立性 – 释放了显著的性能提升,最终超越了融合的标准注意力,用于更长的序列。这突出了向编译器传达意图的重要性。关键要点是:TPU 硬件和 XLA 编译器通常会自动处理平铺和优化,使得手动干预变得不必要甚至有害。
进一步的研究表明,TPU 的收缩阵列设计本质上可以有效地执行平铺矩阵乘法。虽然通过 Pallas 进行自定义内核提供了细粒度的控制(DMA 流水线,显式内存放置),但对于许多用例,利用 XLA 的自动优化可以提供最佳性能。该项目强调,在 GPU 上有效的优化策略不一定对 TPU 有益,并且理解底层硬件对于编写高效代码至关重要。