<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
	<channel>
		<title>Performance on NicoLabs</title>
		<link>http://blog.hellonico.info/zh/tags/performance/</link>
		<description>Recent content in Performance on NicoLabs</description>
		<generator>Hugo</generator>
		<language>zh</language>
		
		
		
		
			<lastBuildDate>Sat, 27 Jun 2026 00:00:00 +0000</lastBuildDate>
		
			<atom:link href="http://blog.hellonico.info/zh/tags/performance/index.xml" rel="self" type="application/rss+xml" />
			<item>
				<title>在Coni中构建原生并行下载</title>
				<link>http://blog.hellonico.info/zh/posts/coni/parallel-downloads/</link>
				<pubDate>Sat, 27 Jun 2026 00:00:00 +0000</pubDate>
				<guid>http://blog.hellonico.info/zh/posts/coni/parallel-downloads/</guid>
				<description>&lt;p&gt;构建自己的语言和工具的绝佳之处之一，是能够重新思考操作的执行方式。在最近的提交中，我们决定解决构建过程中的一个主要瓶颈：下载Maven依赖项。&lt;/p&gt;&#xA;&lt;h2 id=&#34;特定平台脚本存在的问题&#34;&gt;特定平台脚本存在的问题&lt;/h2&gt;&#xA;&lt;p&gt;以前，Coni中的 &lt;code&gt;download-url-to-file&lt;/code&gt; 函数依赖于调用特定平台的外部工具：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;在Linux/macOS上，它会生成一个 &lt;code&gt;curl&lt;/code&gt; 进程。&lt;/li&gt;&#xA;&lt;li&gt;在Windows上，它会调用一个使用 &lt;code&gt;System.Net.WebClient&lt;/code&gt; 的庞大 &lt;code&gt;powershell&lt;/code&gt; 命令。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;虽然这种方法可行，但它有几个缺点。首先，调用外部进程既缓慢又消耗资源。其次，依赖外部工具意味着 &lt;code&gt;curl&lt;/code&gt; 版本的微妙差异或Windows安全协议可能会导致意想不到的失败。最重要的是，使用这些shell命令顺序下载工件意味着解析大型Maven项目会花费太长时间。&lt;/p&gt;&#xA;&lt;h2 id=&#34;原生解决方案&#34;&gt;原生解决方案&lt;/h2&gt;&#xA;&lt;p&gt;在提交 &lt;code&gt;9ac76d12&lt;/code&gt; 中，我们引入了 &lt;code&gt;sys-http-download&lt;/code&gt;，这是一个直接在Go评估器中实现的原生内置函数。这个新的内置函数使用了Go标准的 &lt;code&gt;net/http&lt;/code&gt; 客户端。&lt;/p&gt;&#xA;&lt;p&gt;因为它原生内置在评估器中，所以我们完全避免了进程创建的开销。我们甚至在Go实现中直接加入了一些健壮的重试逻辑，以处理来自Maven Central的速率限制（如果您在没有可识别的User-Agent的情况下请求过快，它经常会抛出429 Too Many Requests错误）。&lt;/p&gt;&#xA;&lt;h2 id=&#34;使用goroutine进行超级加速&#34;&gt;使用Goroutine进行超级加速&lt;/h2&gt;&#xA;&lt;p&gt;在原生内置函数准备就绪后，真正的魔力发生在我们应用Coni的并发原语时。Coni使用 &lt;code&gt;go&lt;/code&gt;、&lt;code&gt;chan&lt;/code&gt;、&lt;code&gt;&amp;lt;!&lt;/code&gt;（从通道读取）和 &lt;code&gt;&amp;gt;!&lt;/code&gt;（写入通道）原生支持Go风格的并发。&lt;/p&gt;&#xA;&lt;p&gt;我们没有逐个下载工件，而是直接在标准库中设置了一个工作池 (worker pool)：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-clojure&#34; data-lang=&#34;clojure&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;let &lt;/span&gt;[total-count (count missing)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      result-ch (&lt;span style=&#34;color:#a6e22e&#34;&gt;chan&lt;/span&gt; total-count)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      task-ch (&lt;span style=&#34;color:#a6e22e&#34;&gt;chan&lt;/span&gt; total-count)]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;;; 将所有任务推送到任务通道&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  (&lt;span style=&#34;color:#66d9ef&#34;&gt;loop &lt;/span&gt;[rem missing]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    (&lt;span style=&#34;color:#66d9ef&#34;&gt;if &lt;/span&gt;(not (&lt;span style=&#34;color:#a6e22e&#34;&gt;empty?&lt;/span&gt; rem))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      (&lt;span style=&#34;color:#66d9ef&#34;&gt;do &lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (&lt;span style=&#34;color:#a6e22e&#34;&gt;&amp;gt;!&lt;/span&gt; task-ch (first rem))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (&lt;span style=&#34;color:#a6e22e&#34;&gt;recur&lt;/span&gt; (rest rem)))))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  (&lt;span style=&#34;color:#a6e22e&#34;&gt;close!&lt;/span&gt; task-ch)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;;; 生成8个工作goroutine并发处理任务&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  (&lt;span style=&#34;color:#66d9ef&#34;&gt;loop &lt;/span&gt;[i &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    (&lt;span style=&#34;color:#66d9ef&#34;&gt;if &lt;/span&gt;(&amp;lt; i &lt;span style=&#34;color:#ae81ff&#34;&gt;8&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      (&lt;span style=&#34;color:#a6e22e&#34;&gt;do&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (&lt;span style=&#34;color:#a6e22e&#34;&gt;go&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          (&lt;span style=&#34;color:#66d9ef&#34;&gt;loop &lt;/span&gt;[]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            (&lt;span style=&#34;color:#66d9ef&#34;&gt;let &lt;/span&gt;[item (&lt;span style=&#34;color:#a6e22e&#34;&gt;&amp;lt;!&lt;/span&gt; task-ch)]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              (&lt;span style=&#34;color:#66d9ef&#34;&gt;if &lt;/span&gt;(not (nil? item))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                (&lt;span style=&#34;color:#a6e22e&#34;&gt;do&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  (&lt;span style=&#34;color:#a6e22e&#34;&gt;&amp;gt;!&lt;/span&gt; result-ch (&lt;span style=&#34;color:#a6e22e&#34;&gt;download-file-single&lt;/span&gt; item repos))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  (&lt;span style=&#34;color:#a6e22e&#34;&gt;recur&lt;/span&gt;))))))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (&lt;span style=&#34;color:#a6e22e&#34;&gt;recur&lt;/span&gt; (+ i &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;)))))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;;; 等待结果并显示进度&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ...)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;我们将工作池的上限限制为8个goroutine，以防止网络不堪重负或触发激进的速率限制。&lt;/p&gt;</description>
			</item>
	</channel>
</rss>
