Felix Geisendörfer 和 Michael Knyszek 与 Natalie 一起讨论 Go 执行跟踪:为什么它们很棒、常见的用例、它们最近如何变得更好等等。
本篇内容是根据2024年3月份#310 The magic of a trace[1]音频录制内容的整理与翻译
过程中为符合中文惯用表达有适当删改, 版权归原作者所有.
Natalie Pistunovich: 我是 Natalie Pistunovich[2],今天我邀请了两位同样姓氏复杂的嘉宾:Felix Geisendörfer[3] 和 Michael Knyszek[4]。Felix,你是 Datadog 的高级工程师,负责产品中的持续性能分析,还为 Go 运行时做贡献。而 Michael,你是 Google 的软件工程师,主要在 Go 编译器和运行时团队工作。你们最近怎么样?
(译者注: Felix 的github上有关多trace相关的开源项目,值得一看)
Felix Geisendörfer: 一切顺利。
Michael Knyszek: 是的,我也很好。
Natalie Pistunovich: 很好。Felix,你今天来这里是想回应之前在播客中提到的一些内容,对吗?
Felix Geisendörfer: 是的。大约在 2022 年 12 月,我听了一期 Go Time 播客,当时我正在研究执行追踪(execution tracer),想解决在 Datadog 遇到的一个问题。我们试图简化调试慢请求的过程,尤其是在没有添加额外的遥测数据的情况下,能够识别出一些被遗忘的东西,比如未被监控的外部网络请求,这可能是导致延迟的原因。此外,我们还需要处理一些很难甚至无法监控的延迟来源,比如互斥锁争用或低效的 goroutine 通信模式等问题。
无论如何,我提到的那期播客是第 260 期。
Natalie Pistunovich: 主题是 "让 Go 更高效"。
Felix Geisendörfer: 没错,"让 Go 更高效"。在那期节目中,Christian Simon 曾说过一句话:“我总是在绝望的时候才开始使用执行追踪,但从来没有通过它找到问题。” 当时我听到这句话,心想:“是啊,我完全理解,因为这是个难用的工具。” 那时我也在用执行追踪,刚刚开始了解它。不过另一方面,我也逐渐被这个工具的潜力所吸引,因为它可以回答很多其他 Go 工具无法解答的问题。所以,我希望有一天能来 Go Time 播客挑战这个看法,认为这个工具只是为那些绝望的人准备的,并且对他们也没有帮助。
此外,自那之后也发生了很多事情。Michael 最近在他的博客文章《更强大的执行追踪》中总结了 Go 1.21 和 1.22 中对执行追踪的改进。今天我们希望能够打破一些关于执行追踪的误解,并告诉大家如何更高效地使用它。
Natalie Pistunovich: 所以你并不是一个人来谈论这句话的,你还带来了 Michael,来为你助阵。Michael,你能给我们讲讲那篇博客吗?
Michael Knyszek: 当然可以。这篇博客主要讲的是过去一年执行追踪方面的改进,这些改进很多。这一切都始于 Felix 和他的同事 Nick 在我分享了一些关于未来方向的文档后联系了我---
我已经不记得是通过哪个渠道了。当时我们都对追踪很感兴趣,想弄清楚其他公司和 Go 社区对追踪的需求。我记得收到 Felix 和 Nick 发来的邮件,内容大概是“是的,我们想做这个,并且愿意提供帮助。”我当时心想:“太好了,我们要开始做了。”这一切大概发生在 2022 年 12 月左右。
不过,执行追踪的历史可以追溯到很早的 Go 1.5,当时它首次推出,并附带了一对 API。我们希望 Go 运行时能够生成追踪数据,并开发了一个工具叫做 go tool trace
,它让你可以可视化查看程序的不同活动。执行追踪的独特之处在于,它是唯一一个可以展示 goroutine 在任何时间点上做了什么的工具。对于 goroutine 执行的不同操作,你可以看到堆栈追踪---
这有点涉及到实际的使用场景了,但这个工具的核心功能就是,你不仅能看到程序在做什么,还能看到它没有做什么。我觉得这有点“魔力”。我们对程序的行为有很多假设,但现实往往与这些假设不同。每当我使用执行追踪解决问题时,尤其是在处理 GC(垃圾回收)相关的延迟问题时,它总是能挑战我对运行时行为的假设。
Natalie Pistunovich: 那它是如何融入整个生态系统的?它是 Go 附带的工具之一……你有关于使用频率或其他有趣的信息吗?
Michael Knyszek: 当然。这个工具随每个标准的 Go 发行版一起提供。你可以用 go tool trace
命令分析追踪数据,如果你想获取测试的追踪数据,可以使用 go test -trace
。还有 runtime/trace
包用于生成追踪数据,net/http/pprof
包则可以在 HTTP 服务器中设置端点,允许你按需从生产环境中抓取执行追踪数据。另外,还有一些新实验功能,我稍后可以详细介绍。
Natalie Pistunovich: 让我们保持期待,继续讲下去。
Michael Knyszek: 好的。执行追踪在它能正常工作时确实很棒,但问题是,正如 Felix 之前提到的,它有时很难使用。在那篇博客中,我提到的主要问题是追踪的开销很高。如果你想开始追踪,可能不太实际,原因不仅仅是开销本身。当它首次发布时,开销大概是 450%,这非常高。后来经过一些优化,开销降到了 10% 到 20%。
Natalie Pistunovich: 这大概就是 1.21 版本的情况。
Michael Knyszek: 实际上,在 1.21 版本中,由于 Felix 和 Nick 在 1.21 版本中的工作,追踪的开销已经降到了 1% 到 2%。
Natalie Pistunovich: 原来如此,我以为是 1.22 版本中的改进。
Michael Knyszek: 是的,1.21 版本是引入低开销追踪的版本。1.22 版本中还有很多其他功能,但追踪的开销确实已经大幅降低了。
Natalie Pistunovich: 我们稍后会谈到这些功能。
Michael Knyszek: 追踪的开销现在已经显著降低。这是第一个主要问题:过去大家不太愿意使用它,因为你无法在大规模环境下使用,也无法收集大量的追踪数据。
Natalie Pistunovich: 或者在生产环境中使用。
Michael Knyszek: 是的。你可以在生产环境中使用,但只能用作一次性的工具,持续几秒钟或一分钟左右。
第二个大问题是追踪数据的可扩展性。如果你试图收集一个很长的追踪数据并分析它,go tool trace
可能会崩溃,占用大量内存。因为它需要将整个追踪数据加载到内存中,这对系统来说负担太大。
在 Go 1.22 中,追踪数据的底层格式得到了改进,现在它是可扩展的。虽然目前 go tool trace
还没有完全利用这一点,但我们已经可以修复这个问题了。
第三个大问题是,追踪往往无法捕捉到你想要的数据。通常当一个问题已经发生时,你才开始追踪,但那时你可能已经错过了关键的活动。为此,我们引入了飞行记录(flight recording)的概念
。你可以一直保持追踪开启,但并不写入所有数据,而是只保留最近几秒钟的数据。如果发生问题,你可以立即获取那段时间的追踪数据。
最后一个问题是,解析追踪数据一直是个挑战。现在我们也发布了实验性的 API,允许用户解析追踪数据,并进行自己的分析。这是一个基于流的 API,所以不再需要将所有数据加载到内存中。这基本上就是所有改进的总结。如果你有任何问题或者需要我深入讲解的地方,请告诉我。
Natalie Pistunovich: 这些改动似乎不仅改变了行为模式,甚至可以说从根本上改变了使用方式。以前可能有一些最佳实践,现在随着这些改进,是否有了一些新的最佳实践?
Michael Knyszek: 我认为以前甚至没有所谓的最佳实践,因为很多人其实并不了解如何使用它。
Natalie Pistunovich: 那我们可以说曾经有一些反模式。
Michael Knyszek: 是的,确实存在一些反模式。现在情况大不相同。如果有人说他们想调试延迟问题,我会建议他们尝试飞行记录功能,开启它,并在代码中添加一些检测逻辑,看看能否捕捉到问题的发生。
Natalie Pistunovich: 你能给我们一些最佳实践的建议吗?还是说更像是“让我们看看大家如何使用它,然后根据反馈来制定最佳实践”?
Michael Knyszek: 许多新功能仍然是实验性的。飞行记录和解析追踪数据的 API 还处于实验阶段,它们还没有经过提案审查过程,并且不在标准库中,而是在 x/exp
中。我们确实有一些方向性的想法,但也在观察人们如何使用这些功能,并根据他们的反馈进行改进。
Natalie Pistunovich: 也许过几个月我们可以做一期后续节目,分享一些使用心得。
Michael Knyszek: 希望如此。
Felix Geisendörfer: 我也有一些想法。回顾一下第 260 期提到的内容,执行追踪之前的问题主要有两个方面:一是用户体验和数据可视化,二是数据收集的最佳实践。现在你可以连续使用它,这对于获取你真正想要的数据来说是一个巨大的改变。
至于用户体验,go tool trace
的默认界面显示的内容可能让很多用户感到困惑,因为它展示的是 Go 调度器的视角,而不是 Go 开发者的视角。而 Go Trace UI 则试图通过更直观的方式展示 goroutine 的行为,这让用户更容易理解。
Natalie Pistunovich: Michael抱着头...
Michael Knyszek: 差不多了...
Felix Geisendörfer: 是的,我们一会儿可以深入讨论细节,但我觉得第一次看到这个时,你应该简单地认为每条“车道”代表一个CPU核,而你看到的每个小矩形或时间段都是调度器将一个goroutine分配到CPU核上执行的时刻。这个可能会让很多人感到惊讶,因为他们认为goroutine的存在是连续的,认为它的生命周期包括在CPU上和不在CPU上的时间。但我认为大多数用户并没有真正意识到goroutine何时切换。很多人也没有意识到的是,在goroutine切换的过程中,时间尺度可以非常夸张。我们经常在开发Web服务器时思考的是几十毫秒甚至几百毫秒的时间,但在运行时的调度中,我们讨论的是微秒,有时甚至是纳秒……有些事情可能真的很短暂。
此外,goroutines也可以在不同的CPU核之间迁移,或者[听不清00:20:27.26]在运行时上进行操作。所以这并不是你想象中的那样。你可能期望的是每个goroutine都有一条独立的“车道”,并看到它随着时间的表现,包括它等待某件事的时间……实际上,goroutine大部分时间都在等待,比如等待网络、等待通道操作、等待定时器,或者等待互斥锁的争用---
希望不是这样,但正是这种问题你可以发现。
因此,我认为用户体验的一个重要变化是,正如Michael提到的,Go Trace UI作为一个新的第三方工具,正在尝试改变这种用户体验。它仍然展示了传统的视图,这个视图确实有用---
我们稍后可以讨论为什么这个视图实际上非常好---
但它还提供了一种新的视图,你可以在时间线上看到每个goroutine的行为,甚至当你在执行追踪的同时记录CPU样本时,你可以按下键盘上的S键……我希望它仍然是这个快捷键;我有一阵子没试过了。这个快捷键会显示堆栈追踪。所以它基本上变成了一个火焰图,每个goroutine的“车道”都会随着时间的变化显示堆栈追踪,你可以真正看到一个goroutine在CPU上和不在CPU上时的行为。这是一种真正理解实际情况的非常好的方式。
最终,Go Trace UI可能是在[听不清00:21:42.25]和现在可用的解析器之前就已经开始了……但这种用户体验,我认为是2022年底真正使执行追踪更易于人们使用的关键所在,这样他们可能还会回到传统的[听不清00:22:00.08]视图,并用它做一些有用的事情,但它可能不是介绍那些不太了解运行时内部工作的人最佳的方式。
Natalie Pistunovich: 是的,是的。
Michael Knyszek: 我完全同意……作为Go团队的成员,我不得不说,我对Go Trace UI的使用经验并不多。但我听到的关于它的反馈都是一致的正面评价。我完全同意Dominik的看法,Go Trace工具确实有点过时了,确实可以做得更好。它里面有很多有用的信息,但我必须说,它并不是最用户友好的界面。
不久前,Go团队有人在那个小小的网页UI中添加了一大堆文档,这使它变得更加易于接近……但它基本上只是一个非常简短的指南,在你开始查看之前弹出来,显然它并没有涵盖所有的功能,但它确实是一个改进。所以我非常感激Dominic在这个方向上的努力。我们确实希望改进默认体验,但这并不是目前的优先事项。
Natalie Pistunovich: 你能谈谈一些常见的使用场景或场合吗?在这些场合中,追踪工具是你真正需要的,并且非常有用?也许你看到的有趣的事情,或者你觉得值得分享的案例。
Michael Knyszek: 我觉得Felix可能有最有趣的故事……我花了很多时间在Go运行时和GC方面工作……所以我通常在追踪中寻找的东西可能和普通Go用户的需求不同,但我可以说,我们的团队长期以来一直使用追踪工具来验证和推翻关于延迟问题的理论,并了解垃圾收集器---
抱歉,我刚才说的是GC,我指的是Go的垃圾收集器。我们使用追踪工具来了解垃圾收集器的执行情况……因为它确实有它自己的goroutine在做事情,我们也通过它发现了很多问题,有些问题在追踪中一目了然。
如果有人还记得,在Go 1.14中,Go调度器发生了一个很大的变化,那就是goroutines现在可以……我尽量不讲得太技术化,但基本上在Go 1.14之前,goroutine只能在某些特定情况下,比如函数开始时,或者它在通道上阻塞时,才会停止执行,或者让出它的执行时间。这会导致很多应用程序的延迟问题。比如说,如果一个goroutine没有调用函数,而是执行了一些复杂的计算,它可能会在一个紧密的循环中不断做算术运算,没有任何东西能够阻止它。为了让goroutine能够随时停止执行,解决的很多问题都在执行追踪中非常明显。当时Austin是Go编译器和运行时团队的技术负责人,他们使用执行追踪的视图来可视化这个问题:“嘿,这不仅仅是一个延迟问题,这也是一个吞吐量问题。” 你可以看到,一个goroutine在执行,垃圾收集器试图停止所有的goroutine,它停止了所有的goroutine,除了一个。这基本上意味着,如果你的应用程序通常在16个核心上正常运行,那么在某些时间段内,可能只有一个核心在做有用的工作。这不仅仅是一个延迟问题,它增加了几毫秒的延迟,同时也是一个吞吐量问题,因为没有其他东西在执行。执行追踪中的可视化非常有力地展示了这个问题,并推动了问题的解决。
Natalie Pistunovich: 真是非常有趣的发现。
Felix Geisendörfer: 是的,我认为对我来说,我们将其集成到Datadog中的最常见原因是,你已经有了分布式追踪,这现在是人们查看请求延迟的常用方式……但有时候你会遇到分布式追踪中某个叶节点的请求很慢,但你不知道为什么。于是我们决定构建这个工具,主要动机之一就是解决这个问题。我们显示的界面类似于Go Trace UI,我们为每个goroutine显示一条“车道”,但我们过滤掉了所有不相关的goroutine,只显示与请求相关的那些。这是第一个使用场景。
我没想到的第二个场景实际上与吞吐量有关,但实际上很常见。人们经常有一些系统从队列中读取数据,比如Kafka队列,他们发现吞吐量不足,队列中的消息堆积,处理速度跟不上……但当他们查看应用程序的资源利用率时,他们看到CPU利用率实际上很低。所以他们没有在处理工作,但应用程序也没有在努力工作。他们会疑惑:“我付了云提供商这么多钱,为什么它没有做我想要的工作?” 这就是执行追踪可以大显身手的地方,因为你可以查看经典的调度视图,看看所有的空隙。你希望这个视图是满的;你希望每条车道都充满goroutine的执行。如果你看到某种空隙模式,有时这个模式可以告诉你很多东西。有时你会看到所有东西都在忙碌运作,然后突然看到一个大块的空白区域,这意味着所有的东西都可能堆积在同一个地方,现在你可以点击周围的内容,找出到底是什么导致了吞吐量问题。所以这是我经常看到的另一个使用场景。
最后,有时goroutine视图可以揭示你已经通过其他信号看到的东西。例如,如果你的goroutine池配置不正确。比如你有一个goroutine池,它可能正在发出外部网络请求,但它成为了应用程序的瓶颈---
尤其是在我刚才提到的场景中---
你可以查看阻塞分析,但老实说,由于开销问题,这并不是一个推荐的分析类型。这实际上仍然是一个问题,因为我们还没有将执行追踪中更快的堆栈追踪修复移植到阻塞分析器中……但基本上,你想弄清楚哪个goroutine池是瓶颈。如果你有多个goroutine池,瓶颈池的goroutine在发送数据时会被阻塞。所以你会看到很多在通道发送操作上的争用。而在接收端,你会看到接收时的饥饿。这是你可以在阻塞分析中看到的东西,但它很难读懂,因为它通常会被无关的接收等待操作所主导……所以你可能有一个与问题无关的goroutine池,它的goroutine比其他池多,这会淹没你正在尝试调试的分析类型。
所以,当你使用执行追踪查看这些数据时,你可以清晰地看到每个goroutine在做什么,并意识到:“嘿,我这里有一个50个goroutine的池,但它们95%的时间都在等待网络,只有5%的时间在执行。难怪我没有充分利用我的CPU资源。我需要显著增加这个池的大小,这样不仅这个池可以做更多的工作,其他池也能更好地运作。” 我认为这是另一个非常常见的现象。
Natalie Pistunovich: 当你查看结果并尝试解释它们时,你是如何处理的?你是在寻找某种已知的模式,然后寻找异常吗?还是有其他方法?
Felix Geisendörfer: 是的,模式绝对是关键,无论是在goroutine视图中,还是在调度视图中。还有一件我们没有提到但很有趣的事情---
在Go Tool Trace的执行追踪中,至少有两个非常有趣的时间序列。第一个显示的是goroutines的数量和未调度的goroutines数量。所以当你看到未调度的goroutine数量上升时,你知道很多goroutine想要运行,因为它们是可运行的,但它们没有被调度,这时你可以看看发生了什么。通常这意味着你实际上没有足够的CPU资源。问题是为什么。
有时你可以看到另一个模式,比如垃圾回收进来了,占用了goroutines本来要执行的“车道”,突然你就看到了很多未调度的goroutines。另一件事是垃圾回收,你会看到一个显示当前堆使用量和目标堆使用量的有趣“车道”,你可以看到两者接近,垃圾回收周期开始……在某种程度上,你可以通过指标看到这些,但指标通常是在较低的分辨率下捕获的,比如每10秒一次,所以有时你看不到那些剧烈的垃圾回收周期扰乱应用程序的图案。你仍然可以通过每秒[听不清00:31:29.09]来测量和绘制图表,但我不知道,看到它在应用程序中实际发生并造成的混乱对我来说更有冲击力。
Michael Knyszek: 我认为你无法通过指标看到这种情况的部分原因是---
我不认为我们有一个暴露垃圾回收经典锯齿形模式的指标。垃圾回收时你会看到分配上升,然后垃圾回收发生,分配量下降。我不认为有一个指标能够完全捕捉到这一点,所以这可能是原因。但在执行追踪中,这一点是非常明显的。
我想强调一下你之前提到的关于模式的观点:在执行追踪中,寻找“空白”往往比查看实际正在执行的事情更重要。我们投入了大量精力去捕捉各种事件,并将它们展示出来……这些事件非常有用,因为当你查看空白时,你通常想看看空白的边缘,找出为什么会有空白。但最终,空白才是最重要的。如果你有一个Web服务,并且你认为它运行良好,那么在Felix提到的那个经典的调度视图中,你会希望每条“车道”都被填满。这是你知道一切正常的标志。当车道没有被填满时,说明出了问题。所以寻找这些空白可能是追踪工具能够提供的最重要的信息。
Natalie Pistunovich: 还有什么其他你会寻找的东西呢?或者说……
Michael Knyszek: 对我个人来说,我总是查看垃圾回收的指标,因为有时它可能会指出一些意想不到的东西。我相信很多人都知道通过减少堆分配的数量来优化应用程序---
当然不是每个人,但很多人确实知道可以通过减少堆分配的量来优化程序的性能,减少垃圾回收的开销。
垃圾回收对延迟的影响通常很难看到或理解……但在执行追踪中,它非常明显。我不会深入细节,但有些机制会让goroutines在某些时候去执行一些垃圾回收的工作,这在追踪中可以清楚地看到。所以你可以发现“哦,实际上我的延迟问题是由垃圾回收问题引起的。也许我应该花些时间减少我的程序分配的内存量”,或者类似的事情。或者它可能暗示我应该看看go GC,或者[听不清00:34:03.26],看看我能做些什么。或者它暗示我需要调整我的实例大小。正如Felix所说,如果你看到很多goroutines能够运行但没有运行,这表明服务器可能过载了。
Natalie Pistunovich: Felix,你还有什么要补充的吗?还是我们已经涵盖了你常用的所有方法?
Felix Geisendörfer: 我不确定是否已经提到---
系统调用行为是一个非常有趣的点……
Michael Knyszek:哦,没错。
Felix Geisendörfer:首先,系统调用在运行时是以一种有趣的方式实现的,所以这绝对是需要注意的...不过,是的,在执行追踪中,你可以看到所有的系统调用。这在历史上一直是这样,但自从Michael的工作开始后,现在还可以显示系统调用的持续时间。以前,你只能看到一个微弱的小线条,表示系统调用正在发生,但你不知道它是一个慢调用还是一个快调用。而这对运行时来说非常重要,因为如果一个系统调用时间很长,最终会有一个后台goroutine出现,决定这个操作系统线程现在被系统调用阻塞了,运行时应该将其从调度器中分离出来,让调度器创建一个新线程来运行goroutine。如果我说错了,Michael可以纠正我,但---
关于系统调用和运行时发生的事情非常有趣,值得研究,现在新的执行追踪器比以前更好地展示了这些。
Michael Knyszek:是的,我完全同意。作为一个在运行时工作的开发者,能够看到这些信息非常重要,因为---
我不想称其为启发式方法,但我会说,高效处理系统调用并不是一门精确的科学,我们总是在思考如何改进这一点。能够从运行时的角度观察到这些信息非常有用……回到我之前提到的挑战你对程序运行方式的假设,能够看到花费在系统调用上的时间是非常有用的。在CPU性能分析中,你可能会看到一点点这方面的信息,但在这里,你可以从延迟的角度看到系统调用的影响,这非常有帮助。
Felix Geisendörfer:还有一个有趣的点是,运行时将cgo调用视为系统调用,所以你也能看到很多cgo调用的情况,这有点意外,但也非常有趣。因为cgo通常涉及非常有趣的性能问题---
因为从Go中正确使用cgo并不容易。
Michael Knyszek:对,实际上,Dominic已经向我们提交了一个问题,建议不要将cgo调用计为系统调用。因为显然,追踪会带来一些开销,而cgo的性能是人们总是希望能够更快的一个方面。你希望Go与C之间,或者Go与其他语言之间的桥接尽可能快。现在还可以,因为本来它就不是特别快,但你可以想象,如果未来某天它变得更快了,那么追踪实际上可能会成为一个显著的障碍,到时我们可能需要一种不同的方式来在追踪中表示它。但就目前而言---
是的,它非常好用。
Felix Geisendörfer:是的,但不要依赖这个使用场景。[笑声]
Natalie Pistunovich:是的。Felix,你将在6月的GopherCon欧洲大会上进行一场关于这些内容的演讲,你会谈到这些,还有一些关于帧指针展开的内容。你能提前透露点什么吗?
Felix Geisendörfer:哦,是的。这个演讲叫做《How to Win Frames and Influence Pointers[5] (PPT: https://docs.google.com/presentation/d/1rnCYgT41quuSKk-p9o7WOfzI5xoUAgEG4MTlZljjenQ/edit#slide=id.g1e07bf1996f_3_1588),部分内容是我们今天讨论的故事,关于如何合作使执行追踪工具变得更好,但也特别讲到了如何让堆栈追踪更快,也就是所谓的堆栈展开...因为实际上,之前执行追踪器的10%到20%的开销主要是因为每当调度器中有有趣的事件发生时,它都会捕获堆栈追踪。而优化这一点的方式是通过实现帧指针展开,这实际上是最简单的展开方式。它依赖于编译器在每个函数中插入一些指令,以便每次调用函数时,基本上创建一个链表,让你可以遍历堆栈。沿着这个链表,你可以找到返回地址,也就是你堆栈追踪所需的程序计数器...
Go运行时已经支持这一点很长时间了,所以我最初的想法是“哦,这太棒了,这会很简单。我只需要写一个for循环来遍历链表。”实际上,这部分确实很简单,但事实证明,在Intel上有一些微妙的问题,而在ARM上有一些不那么微妙的问题,导致这一点不能正常工作。其中一些问题与当Go运行时需要增长堆栈并调整堆栈上的指针时,指针的影响直接相关。有些指针可以是---
嗯,不是自引用的,但它们可以指回它们所在的堆栈。当你将这些指针复制到一个新堆栈时,它们实际上需要进行调整。因此,这就是演讲标题第二部分所暗示的内容,关于修正这些问题并使帧指针展开在运行时中表现良好的工作。
Natalie Pistunovich:通常来说,对于指针展开,Go中的一切不是都存储在寄存器中吗,比如返回地址?还是说这是与偏移量相关的?
Felix Geisendörfer:现在我们聊到比较深入的技术了...
Natalie Pistunovich:只是一个简短的问题。[笑]
Felix Geisendörfer:好吧,简短的回答是,一般来说,你所称的堆栈追踪的大部分内容都在堆栈上;返回地址也应该大部分在堆栈上。但是你的第一个入口点通常在寄存器中,具体取决于架构。如果是Intel或类似ARM的架构,可能还会有一些内容在其他寄存器中……但这有点依赖于架构。大多数情况下,它们就存储在堆栈上,位于帧的入口处。实际上,我认为对于ARM来说,它可能位于帧的底部,这取决于你怎么看待它。
Natalie Pistunovich:这引发了我脑中的一个交叉问题,因为我们计划在未来的某期节目中讨论Go在跨架构编译方面的优势和便利性。正如你所说的,这取决于架构……我真的很想问关于这个的问题,但我们已经聊了45分钟了。我们肯定会把这个问题留到第二部分,紧接着问你已经识别的模式。
不过还有一个简短的问题,可能是本期节目的最后一个问题,然后我们会进入“冷门观点”环节---
之前的问题是“当你查看追踪结果时,你在做什么?”你提到你在寻找特定的模式、空白、特定的字段和数值...所以我忍不住想问,出于个人兴趣,你有没有尝试将追踪输出的图片扔给AI,比如你最喜欢的视觉模型,问它“你会怎么做不同的地方?问题是什么?”之类的。有过类似的实验或想法吗?
Felix Geisendörfer:[听不清00:40:53.13]
Natalie Pistunovich:"还没有"是个不错的回答。
Michael Knyszek:我会说还没有。我甚至不知道该从哪里开始。我不知道该如何为此构建一个数据集。我觉得我通过追踪解决的每个问题都不太一样……我甚至不知道如何用AI开始处理这些问题---
如何为AI提供训练集,告诉它“这个模式导致了这个问题。这是解决方法。”嗯,也许我刚才已经告诉自己如何做了。[笑]
Natalie Pistunovich:是啊,你不需要准备训练数据。把它扔给现有的LLM,看看它的表现。也许它已经知道一些东西了。
Michael Knyszek:我不知道。Felix,你有什么想法吗?
Natalie Pistunovich:节目结束后的对话肯定会很有趣。是啊,Felix,你有什么要补充的吗?
Felix Geisendörfer:我还没有把执行追踪数据扔给AI,因为通常我对自己非常熟悉的事情,AI的表现让我失望,尤其是在性能方面。不过,我最近确实上传过一些我做的数据分析图片,实际上与性能分析相关。我在测试采样率以及性能分析器能检测到一个函数被调用的频率……这很复杂。但它产生了一个非常有趣的图表;它是一种模拟,产生了一个振荡曲线,而且振荡的幅度逐渐增大,最终趋于下降……我把这个图表上传到ChatGPT,问它“你见过这种模式吗?”它正确描述了我看到的图形;虽然它没能找出之前在哪里看到过这个模式,但……嘿,已经完成了一半的工作;它能够识别出图中的内容。最终,它将能够连接这些点并给出正确的关联。是的,有时候你需要的只是将一个知识领域与另一个联系起来,然后你就豁然开朗了。所以我觉得未来很有趣。
Natalie Pistunovich:这是免费版吗?
Felix Geisendörfer:不不,我付费了,但没得到什么。所以我只是提前看到了未来。我提前看到了未来。
Natalie Pistunovich:嗯,那也说得通。有趣。好的,很酷。也是我们可以在第二部分中讨论的内容,看看自我们今天的对话以来,AI有哪些进展。但在此之前……
Natalie Pistunovich:好的,冷门观点环节是你可以发表你的冷门观点。它不一定与技术有关,可以关于任何事。然后我们会把它做成一个推特投票,或者说X投票,如果很多人不同意,你就能进入我们的名人堂。谁想先来发表他们的冷门观点?
Michael Knyszek:好吧,我先来。我之前提到了Austin的名字,实际上我之前问了Austin……我很难想出一个冷门观点,所以我从他那里偷了一个。我的冷门观点是,我们应该弃用整个运行时包。我不是说垃圾回收器或调度器,我指的是运行时包的公共API。它有太多的瑕疵,太多错误使用的方式。许多API已经在其他包中有了替代品。许多API应该有替代品。我现在有一个列表,可以逐个讲解。也许其中的一些功能不应该再回来。也许这就是这个冷门观点中不受欢迎的部分……有些功能应该永远消失。当然,有些是必要的“恶”,比如runtime.GC
、runtime.LockOSThread
,如果有人熟悉这些,我想你们会同意它们是必要的“恶”。
Natalie Pistunovich:所以这是几个冷门观点。如果我们要发推特,哪个是你选择的?是你最开始提到的,整个运行时包都应该弃用吗?
Michael Knyszek:是的,就用这个吧。
Natalie Pistunovich:好的,已经记录在案了。Felix,现在轮到你了。你有什么冷门观点?
Felix Geisendörfer:嗯,参考之前的节目,Matt Boyle[6]最近分享了他的观点,他认为Twitter仍然是Go开发者聚集的好地方……如果你要在Twitter上发表冷门观点,说Twitter还很棒,那你显然还不够努力。所以我的冷门观点是,虽然这可能在今天仍然是事实,但我认为在未来几年内LinkedIn将会取代技术界的Twitter,一切都会迁移到LinkedIn上。我对此并不感到高兴,但这是我看到的发展趋势。
Natalie Pistunovich:有意思。你为什么这么认为?
Felix Geisendörfer:我注意到Twitter上的互动量一直在下降。我对Twitter目前的所有者不是很满意,而我看到LinkedIn正处于一个稳定的增长轨道。他们在过去十年里增长得非常快,我认为这种趋势还会继续。LinkedIn虽然是个很奇怪的社交平台,但我发现那里有越来越多的对话在发生。我在LinkedIn上的粉丝远远少于Twitter,但当我在LinkedIn上发布内容时,至少能获得与Twitter相似的互动量。所以我只是观察到这种趋势。虽然我对这个趋势并不感到开心,因为我觉得LinkedIn很怪,但……我认为这确实在发生。
Natalie Pistunovich:是啊。我可以给你推荐一份Java工作吗?[笑声] 一些嵌入式的职位……
Felix Geisendörfer:你在LinkedIn上肯定会做得很棒,技术界的Twitter。
Natalie Pistunovich:每周大家在LinkedIn上收到的那些奇怪的消息……有意思。让我们看看这会如何发展。至于我的冷门观点,我已经在推特上发过并进行了测试……所以我认为这很冷门,因为大多数人持相反的意见。当你在家工作,自己一个人时,听音乐应该通过耳机,而不是电脑的扬声器。
Felix Geisendörfer:你为什么觉得通过耳机听音乐更合理?
Natalie Pistunovich:我想是因为我有过很长时间和德国邻居相处的经历……我现在一想到会打扰别人就会觉得很困扰。
Michael Knyszek:你知道吗,我有点同意。我以前几乎一直通过电视或外放设备来听音乐或看节目。但最近,我开始真正享受用耳机玩游戏和看电视,因为这样我能听到立体声效果,而这通常是外放无法提供的。
Natalie Pistunovich:是啊,音质确实……
Michael Knyszek:对,音质最终确实会好很多。所以在这点上,我其实和你站在同一边。
Felix Geisendörfer:是的,我其实也这么做,所以我不确定这个观点最后会有多冷门。
Natalie Pistunovich:我的小样本推特追随者群体说,外放更合理,但我们看看这次的结果吧。好的,非常感谢你们加入我们的讨论,谈论追踪工具。这次的内容非常有趣,所有的更新都很有意思。我希望飞行追踪能够顺利完成并脱离实验阶段,下一次我们可以讨论它如何加入标准库。到那时,祝你们……追踪顺利。希望你们遇到的异常越来越少。感谢你们的参与。
Felix Geisendörfer:谢谢你的主持。
#310 The magic of a trace: https://changelog.com/gotime/310
[2]Natalie Pistunovich: https://github.com/Pisush
[3]Felix Geisendörfer: https://github.com/felixge
[4]Michael Knyszek: https://github.com/mknyszek
[5]《How to Win Frames and Influence Pointers: https://www.youtube.com/watch?v=0f8JnJhC0-0
[6]Matt Boyle: https://github.com/MatthewJamesBoyle