抱歉,但你仍然需要思考
作者:皮奥特·萨尔纳基(Piotr Sarnacki),2026年10月11日 DHH,Ruby on Rails框架的创建者,决定将Campfire Once从Ruby on Rails重写为Rust。或者更确切地说,请一个外包人员为他完成,因为他无法忍受自己阅读或编写Rust代码。在此之后,他还用其他语言进行了重写,如Elixir和Go。这对我来说非常有趣,因为它很好的揭示了一个人在没有阅读代码的情况下使用AI代理的结果。在这个案例中,这个人也有很多编程经验。而结果是……好吧,如果你希望你能很快停止阅读代码或完全停止思考,那么这些结果并不是很令人鼓舞。重写存在多个问题,但我们先从非功能性差异开始,因为它们很好地展示了许多人似乎没有意识到的事情:如果你的提示不够具体,许多决策都是抛硬币。这是因为许多问题没有单一的正确答案。我们想保持向后兼容吗?我们更在乎延迟还是吞吐量?在负载下系统可以使用多少内存?在崩溃后丢失新内容通知是否可以接受?你可能不关心它们,或者至少某些问题,但它们将在一个大型语言模型实现你认为想要的内容时被隐式回答。如果你更仔细地查看重写,你会很快发现它们处理向后兼容性和其他约束的方式不同。例如,Rust版本并未保持100%的向后兼容性,它丢弃了CSRF令牌,以便更容易进行缓存。它还放弃了Redis,而选择了进程内队列,例如用于通知。Elixir重写与Rails原版更接近。这已经使得比较几乎没有意义,因为这些差异与语言无关。并不是说外包人员听到“用Elixir重写”就选择因为所用语言而保持100%向后兼容性。但情况甚至更糟!如果你查看代码,虽然我知道,我们不应该再阅读代码了,但你很快会注意到很多事情都不太好。例如,我见过人们抱怨Elixir版本只使用单个进程顺序处理所有SQL查询,即使这些查询是可以并发运行的读取操作。这是一个合理的抱怨,但DHH似乎认为,外包人员不能编写高性能代码对Elixir不是最好的印象。当我查看Rust版本时,我不确定是否同意这一点。因为在Rust版本中某些数据库操作并不是异步的,在某些情况下这可能更糟。你看,在Rust中,当使用异步运行时,调度不是抢占式的,而是合作式的。如果一个任务不让步,没有其他任务可以在同一个工作线程上运行。这在实际中意味着,任务所花费的时间应该尽可能短。例如,当你运行一个需要100毫秒的SQL查询时,你不希望所有其他任务都等待,因为发出查询的任务反正也在等待。因此,你应该理想地使用异步I/O操作,在等待数据库响应时让运行时得到控制。在Rust重写中,一些查询在工作线程中运行,但一些查询作为异步任务中的阻塞操作运行。锁也是如此。当使用异步运行时,最安全的选择是使用异步锁,例如tokio::sync::Mutex。如果你确定锁只被持有很短的时间,使用非异步版本是可以的,但如果你持有一个同步锁10毫秒,所有在同一线程上的任务都会为它等待,而阻塞任务本身并没有在做任何工作。因此,我很遗憾地告诉你,编码不太可能“解决”,你仍然需要知道自己在做什么。更进一步,事实证明,漂移基准测试也的确不太靠谱,因为它们只测量吞吐量,忽略系统的其他特性。例如,扎克·丹尼尔斯(Zach Daniels)测量了重载下新帖子通知的交付率,发现Rust版本在重载情况下的成功交付率为1%。这可不是一个好的结果。基准测试很难。但即使改进后的基准测试也可能并没有实际测试到你想要测试的内容,这取决于你系统的特征。你看,当你测试一个系统时,你可能希望强调在负载下的不同特性。DHH和扎克的基准测试都是闭环基准测试,因此它们在测试“系统在特定时间内可以处理多少请求?”测试时,使用了N个客户端,每当一个客户端得到前一个请求的响应时就会发送一条新消息。然而在许多情况下,系统上的负载增加可能来自许多用户同时执行一个操作,他们不会等其他用户完成他们的操作。在这种情况下,你可能会希望保持恒定的到达率,即你希望以恒定的速率发送请求,而不是使速率依赖于系统响应的速度。如果你想检查事件的可靠性...
本站免费、广告极少。如果觉得有帮助,可以请我们喝杯咖啡 —— 任何金额都对持续运营有实际帮助。
☕请我喝杯咖啡