<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Search on Tech Explorer</title><link>https://byronhe.com/categories/search/</link><description>C++、Linux、算法、数据结构与密码学的学习笔记与实践记录。</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><copyright>© 2026 BH</copyright><lastBuildDate>Sun, 14 Jun 2020 00:00:00 +0000</lastBuildDate><atom:link href="https://byronhe.com/categories/search/index.xml" rel="self" type="application/rss+xml"/><item><title>倒排索引求交算法相关资料调研</title><link>https://byronhe.com/post/2020/06/14/posting-list-intersection-algorithms/</link><pubDate>Sun, 14 Jun 2020 00:00:00 +0000</pubDate><guid>https://byronhe.com/post/2020/06/14/posting-list-intersection-algorithms/</guid><description>&lt;h2 class="relative group"&gt;1. IR-book 经典教材中的思路&#10; &lt;div id="1-ir-book-经典教材中的思路" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#1-ir-book-%e7%bb%8f%e5%85%b8%e6%95%99%e6%9d%90%e4%b8%ad%e7%9a%84%e6%80%9d%e8%b7%af" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;Faster postings list intersection via skip pointers&#10;通过跳指针，实现更快的 posting list 求交&#10;&lt;a href="https://nlp.stanford.edu/IR-book/html/htmledition/faster-postings-list-intersection-via-skip-pointers-1.html" target="_blank" rel="noreferrer"&gt;https://nlp.stanford.edu/IR-book/html/htmledition/faster-postings-list-intersection-via-skip-pointers-1.html&lt;/a&gt;&lt;/p&gt;</description><content:encoded>&#10;&lt;h2 class="relative group"&gt;1. IR-book 经典教材中的思路&#10; &lt;div id="1-ir-book-经典教材中的思路" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#1-ir-book-%e7%bb%8f%e5%85%b8%e6%95%99%e6%9d%90%e4%b8%ad%e7%9a%84%e6%80%9d%e8%b7%af" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;Faster postings list intersection via skip pointers&#10;通过跳指针，实现更快的 posting list 求交&#10;&lt;a href="https://nlp.stanford.edu/IR-book/html/htmledition/faster-postings-list-intersection-via-skip-pointers-1.html" target="_blank" rel="noreferrer"&gt;https://nlp.stanford.edu/IR-book/html/htmledition/faster-postings-list-intersection-via-skip-pointers-1.html&lt;/a&gt;&lt;/p&gt;&#10;&lt;figure&gt;&#10; &lt;img class="my-0 rounded-md" loading="lazy" decoding="async" alt="" src="https://nlp.stanford.edu/IR-book/html/htmledition/img106.png"&gt;&lt;/figure&gt;&#10;&lt;p&gt;首先有个最 trival 的算法，两个 posting list，各一个指针，谁不匹配，就把谁右移一个，&lt;/p&gt;&#10;&lt;p&gt;跳表方法，思路类似 skip list，是要在建索引的时候，额外构建 skip list，这样就不是右移一个，而是按照跳表来右移。&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;2. lemire 教授的 SIMDCompressionAndIntersection 库中有多种算法&#10; &lt;div id="2-lemire-教授的-simdcompressionandintersection-库中有多种算法" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#2-lemire-%e6%95%99%e6%8e%88%e7%9a%84-simdcompressionandintersection-%e5%ba%93%e4%b8%ad%e6%9c%89%e5%a4%9a%e7%a7%8d%e7%ae%97%e6%b3%95" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;&lt;a href="https://github.com/lemire/SIMDCompressionAndIntersection/blob/master/src/intersection.cpp" target="_blank" rel="noreferrer"&gt;https://github.com/lemire/SIMDCompressionAndIntersection/blob/master/src/intersection.cpp&lt;/a&gt;&lt;/p&gt;&#10;&lt;p&gt;可以看到实现了这些求交算法，并且还有个 benchmark 程序 benchintersection 可以做对比&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&amp;ldquo;simd&amp;rdquo;&lt;/li&gt;&#10;&lt;li&gt;&amp;ldquo;galloping&amp;rdquo;&lt;/li&gt;&#10;&lt;li&gt;&amp;ldquo;scalar&amp;rdquo;&lt;/li&gt;&#10;&lt;li&gt;&amp;ldquo;v1&amp;rdquo;&lt;/li&gt;&#10;&lt;li&gt;&amp;ldquo;v3&amp;rdquo;&lt;/li&gt;&#10;&lt;li&gt;&amp;ldquo;simdgalloping&amp;rdquo;&lt;/li&gt;&#10;&lt;li&gt;&amp;ldquo;simd_avx2&amp;rdquo;&lt;/li&gt;&#10;&lt;li&gt;&amp;ldquo;v1_avx2&amp;rdquo;&lt;/li&gt;&#10;&lt;li&gt;&amp;ldquo;v3_avx2&amp;rdquo;&lt;/li&gt;&#10;&lt;li&gt;&amp;ldquo;simdgalloping_avx2&amp;rdquo;&lt;/li&gt;&#10;&lt;li&gt;&amp;ldquo;highlyscalable_intersect_SIMD&amp;rdquo;&lt;/li&gt;&#10;&lt;li&gt;&amp;ldquo;lemire_highlyscalable_intersect_SIMD&amp;rdquo;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;其中根据输入数据的规模，会选用不同的算法，并且还有 avx2 指令集的优化算法。&lt;/p&gt;&#10;&lt;p&gt;其中提到的 sse 优化的 fast-intersection-sorted-lists-sse&#10;&lt;a href="https://highlyscalable.wordpress.com/2012/06/05/fast-intersection-sorted-lists-sse/" target="_blank" rel="noreferrer"&gt;https://highlyscalable.wordpress.com/2012/06/05/fast-intersection-sorted-lists-sse/&lt;/a&gt;&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;3. &amp;ldquo;The magic of Algorithms! &amp;quot; 中的算法&#10; &lt;div id="3--the-magic-of-algorithms--中的算法" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#3--the-magic-of-algorithms--%e4%b8%ad%e7%9a%84%e7%ae%97%e6%b3%95" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;Chap. 6, Algorithm 6.1 Intersection based on Mutual Partitioning&lt;/p&gt;&#10;&lt;p&gt;&lt;a href="http://didawiki.di.unipi.it/doku.php/magistraleinformaticanetworking/ae/ae2019/start#books_notes_etc" target="_blank" rel="noreferrer"&gt;http://didawiki.di.unipi.it/doku.php/magistraleinformaticanetworking/ae/ae2019/start#books_notes_etc&lt;/a&gt;&lt;/p&gt;&#10;&lt;p&gt;pdf 是 &lt;a href="https://www.dropbox.com/s/dqttqigvuf4ydkd/main.pdf?dl=0" target="_blank" rel="noreferrer"&gt;https://www.dropbox.com/s/dqttqigvuf4ydkd/main.pdf?dl=0&lt;/a&gt;&lt;/p&gt;&#10;&lt;p&gt;思路很简单，就是嵌套的 binary search，实测效果不错，而且实现简单，可以直接递归实现。&lt;/p&gt;&#10;&lt;p&gt;我实测效果还不错，提了个 issue&#10;&lt;a href="https://github.com/lemire/SIMDCompressionAndIntersection/pull/29" target="_blank" rel="noreferrer"&gt;https://github.com/lemire/SIMDCompressionAndIntersection/pull/29&lt;/a&gt;&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;4. List Intersection for Web Search: Algorithms, Cost Models, and Optimizations&#10; &lt;div id="4--list-intersection-for-web-search-algorithms-cost-models-and-optimizations" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#4--list-intersection-for-web-search-algorithms-cost-models-and-optimizations" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;&lt;a href="http://www.vldb.org/pvldb/vol12/p1-kim.pdf" target="_blank" rel="noreferrer"&gt;http://www.vldb.org/pvldb/vol12/p1-kim.pdf&lt;/a&gt;&#10;微软的文章，搞了个 cost model， 来评估各种 list 求交集算法的性能，但是不开源。&lt;/p&gt;&#10;&lt;blockquote&gt;&lt;p&gt;本文提出一种基于代价的模型，来估算各种算法组合的代价，并使用代价模型来搜索代价最低的算法，得到的计划通常是 2-way 算法的一个组合，&#10;并且比传统的 2-way 和 k-way 算法性能都好。&lt;/p&gt;&#10;&lt;/blockquote&gt;&lt;p&gt;并列举了常见的 List Intersection 算法：&#10;2-way:&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;2-Gallop&lt;/li&gt;&#10;&lt;li&gt;2-Merge&lt;/li&gt;&#10;&lt;li&gt;2-SIMD&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;k-way:&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;k-Gallop&lt;/li&gt;&#10;&lt;li&gt;k-Merge&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;2-way vs. k-way&#10;本文断言，对 k-list 求交，通过 k-1 次重复 2-way 求交算法，比通过单次 k-way 求交算法，更快。&lt;/p&gt;&#10;&lt;p&gt;结论，通过本文 cost model optimizer 规划出来的 2-way 求交计算，通常比 k-way 求交更快。&lt;/p&gt;&#10;&lt;p&gt;&lt;a href="https://arxiv.org/pdf/1401.6399.pdf" target="_blank" rel="noreferrer"&gt;https://arxiv.org/pdf/1401.6399.pdf&lt;/a&gt;&lt;/p&gt;&#10;&lt;blockquote&gt;&lt;p&gt;We demonstrated that combining unpacking and differential coding resulted in faster decompression&#10;speeds, which were approximately 30 % better than the best speeds reported previously [3]. To match&#10;the performance of these fast compression schemes, we additionally vectorized and optimized the&#10;intersection of posting lists. To this end, we introduced a family of algorithms exploiting commonly available SIMD instructions (V1, V3 and SIMD GALLOPING). They are often twice as fast as the&#10;best non-SIMD algorithms. Then, we used our fast SIMD routines for decompression and posting&lt;/p&gt;&#10;&lt;/blockquote&gt;&lt;p&gt;Efficient set intersection for inverted indexing&lt;/p&gt;&#10;&lt;p&gt;&lt;a href="http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.415.2636&amp;amp;rep=rep1&amp;amp;type=pdf" target="_blank" rel="noreferrer"&gt;http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.415.2636&amp;rep=rep1&amp;type=pdf&lt;/a&gt;&lt;/p&gt;&#10;&lt;p&gt;Intersection in integer inverted indices&#10;&lt;a href="https://pdfs.semanticscholar.org/db30/9935b512ef4d0c8941c96e987ad70400ebde.pdf" target="_blank" rel="noreferrer"&gt;https://pdfs.semanticscholar.org/db30/9935b512ef4d0c8941c96e987ad70400ebde.pdf&lt;/a&gt;&lt;/p&gt;&#10;&lt;p&gt;lookup 算法最快，非常简单，两层存储，按位分成两层。&#10;首层直接定位，二层直接遍历。 二层可以压缩。&lt;/p&gt;&#10;</content:encoded></item><item><title>基于 ElasticSearch 开发垂直搜索系统</title><link>https://byronhe.com/post/elasticsearch-dev-arch/</link><pubDate>Sun, 29 Mar 2020 00:00:00 +0000</pubDate><guid>https://byronhe.com/post/elasticsearch-dev-arch/</guid><description>&lt;h2 class="relative group"&gt;一，背景介绍&#10; &lt;div id="一背景介绍" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e4%b8%80%e8%83%8c%e6%99%af%e4%bb%8b%e7%bb%8d" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;ElasticSearch 是由 Lucene 包装上分布式复制一致性算法等附加功能，构成的开源搜索引擎系统。&lt;/p&gt;&#10;&lt;p&gt;近两年在业界热度大增，主要有 3 种应用场景：&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;全文搜索引擎&lt;/li&gt;&#10;&lt;li&gt;NOSQL 数据库&lt;/li&gt;&#10;&lt;li&gt;日志分析数据库 ELK&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;很多垂直领域搜索需求，都可以基于 ElasticSearch 来设计架构。&lt;/p&gt;&#10;&lt;p&gt;ElasticSearch 能大幅度提升相关业务的迭代开发速度，实现类似 sql 数据库增删改查一样的快速开发。&#10;并在相对高 qps 的在线业务中，保证毫秒级的延迟，提供极高的可用性和稳定性。&lt;/p&gt;&#10;&lt;p&gt;经过持续的研读官方文档，调研业界经验，并在实践中应用反思后，总结出一套架构方案。供参考，欢迎意见建议。&lt;/p&gt;</description><content:encoded>&lt;h2 class="relative group"&gt;一，背景介绍&#10; &lt;div id="一背景介绍" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e4%b8%80%e8%83%8c%e6%99%af%e4%bb%8b%e7%bb%8d" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;ElasticSearch 是由 Lucene 包装上分布式复制一致性算法等附加功能，构成的开源搜索引擎系统。&lt;/p&gt;&#10;&lt;p&gt;近两年在业界热度大增，主要有 3 种应用场景：&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;全文搜索引擎&lt;/li&gt;&#10;&lt;li&gt;NOSQL 数据库&lt;/li&gt;&#10;&lt;li&gt;日志分析数据库 ELK&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;很多垂直领域搜索需求，都可以基于 ElasticSearch 来设计架构。&lt;/p&gt;&#10;&lt;p&gt;ElasticSearch 能大幅度提升相关业务的迭代开发速度，实现类似 sql 数据库增删改查一样的快速开发。&#10;并在相对高 qps 的在线业务中，保证毫秒级的延迟，提供极高的可用性和稳定性。&lt;/p&gt;&#10;&lt;p&gt;经过持续的研读官方文档，调研业界经验，并在实践中应用反思后，总结出一套架构方案。供参考，欢迎意见建议。&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;二，架构方案&#10; &lt;div id="二架构方案" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e4%ba%8c%e6%9e%b6%e6%9e%84%e6%96%b9%e6%a1%88" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;一个 ElasticSearch 集群 cluster ，配套：&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;转发代理 proxy&lt;/li&gt;&#10;&lt;li&gt;队列&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&#10;&lt;h3 class="relative group"&gt;1. 转发代理 proxy&#10; &lt;div id="1--转发代理-proxy" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#1--%e8%bd%ac%e5%8f%91%e4%bb%a3%e7%90%86-proxy" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h3&gt;&#10;&lt;p&gt;proxy 的功能是：&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;协议转换 ，做 rpc 协议如 protobuf 和 ElasticSearch REST json 之间的协议转换，&#10;对文档更新等请求，定义了 rpc 协议，便于队列做各种处理。&lt;/li&gt;&#10;&lt;li&gt;引入 RPC 框架成熟的负载均衡，容灾，故障屏蔽等功能，到对 ES 的 RPC 中，&#10;比如如果单机 ES 进程挂了，通过返回码让调用方自动换机器重试。&lt;/li&gt;&#10;&lt;li&gt;统一监控告警系统，监控各种请求失败，延迟分布等，并监控 ElasticSearch java 进程状态，集群状态&lt;/li&gt;&#10;&lt;li&gt;转发文档更新请求给本机的队列 。用队列做削峰填谷，自动合并批量，做限流。&lt;/li&gt;&#10;&lt;li&gt;提供双写能力，便于索引升级切换&lt;/li&gt;&#10;&lt;li&gt;proxy 到本机 ES 做了 http 连接池，避免频繁的 HTTP tcp 建连接。&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&#10;&lt;h3 class="relative group"&gt;2. 队列&#10; &lt;div id="2-队列" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#2-%e9%98%9f%e5%88%97" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h3&gt;&#10;&lt;p&gt;队列 实现了 出队限流，请求合并，削峰填谷 3个功能。&lt;/p&gt;&#10;&lt;p&gt;在实际业务中，常常会定期做文档全量更新，会出现短时间内写请求高峰，&lt;/p&gt;&#10;&lt;p&gt;如果直接写 ES，请求高峰时，经常出现 ES write 线程池占满，导致部分写请求失败。&lt;/p&gt;&#10;&lt;p&gt;另外部分业务每次请求只更新1个文档，导致 ES cpu 高，影响 ES 的写性能，不符合官方推荐做法。&lt;/p&gt;&#10;&lt;p&gt;为此，引入队列:&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;配置限制了出队的 QPS ，确保集中高峰被抹平，以匀速稳定地写入 ES，彻底消除了更新失败。&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;并用配置自动把多个请求合并成批量 （比如 5000个文档一个批量），优化了 ES 的写入性能。&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;请求高峰中超出配置 QPS 的请求，队列自动暂存在文件中，随后处理，保证了 ES 服务平稳。&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&#10;&lt;h3 class="relative group"&gt;3. 其他工具&#10; &lt;div id="3-其他工具" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#3-%e5%85%b6%e4%bb%96%e5%b7%a5%e5%85%b7" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h3&gt;&#10;&lt;p&gt;另外，繁荣的 ES 开源生态中，周边工具非常丰富便捷，&#10;我们常用的两种周边工具：kinana 和 bin/elasticsearch-sql-cli，极其方便快捷，大幅度提升了开发效率。&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;三，搜索应用开发优化指南&#10; &lt;div id="三搜索应用开发优化指南" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e4%b8%89%e6%90%9c%e7%b4%a2%e5%ba%94%e7%94%a8%e5%bc%80%e5%8f%91%e4%bc%98%e5%8c%96%e6%8c%87%e5%8d%97" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;垂直搜索系统的在线检索部分，一般流程如下&lt;/p&gt;&#10;&lt;figure&gt;&lt;img&#10; class="my-0 rounded-md"&#10; loading="lazy"&#10; decoding="async"&#10; alt=""&#10; width="501"&#10; height="482"&#10; src="https://byronhe.com/images/custom_search_arch_hu_83d0c7bc1192a104.webp"&#10; srcset="https://byronhe.com/images/custom_search_arch_hu_a71f4582c0e1c5d3.webp 480w, https://byronhe.com/images/custom_search_arch_hu_83d0c7bc1192a104.webp 501w"&#10; sizes="(min-width: 768px) 50vw, 92vw"&#10; data-zoom-src="https://byronhe.com/images/custom_search_arch.png"&gt;&lt;/figure&gt;&#10;&lt;p&gt;ES 用来实现 召回和粗排环节 ，和部分自动补全环节。&lt;/p&gt;&#10;&lt;p&gt;基于 ES 开发的优点：&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;ES/Lucene 的 Query DSL 极其强大全面灵活，业务逻辑代码大幅度简化，开发简单便捷，业务迭代开发速度大大提高。&lt;/li&gt;&#10;&lt;li&gt;有商业公司维护的高质量官方文档， 网上也有海量资料，新人几天就可以上手，快速形成生产力，提升团队效率。&lt;/li&gt;&#10;&lt;li&gt;成熟稳定，就目前经验看没有遇到过 bug&lt;/li&gt;&#10;&lt;li&gt;业务如果扩展，后续伸缩性，扩展性，分 shard ，多副本等，都有比较成熟方案。&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&#10;&lt;h3 class="relative group"&gt;1. query DSL 语法&#10; &lt;div id="1-query-dsl-语法" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#1-query-dsl-%e8%af%ad%e6%b3%95" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h3&gt;&#10;&lt;p&gt;基于 ES 的开发，首先需要学习常见的几种 query，&lt;/p&gt;&#10;&lt;p&gt;ES 的 query 简单分成 4 类:&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;term query，对单个词的 query，包括 term/terms/range/exists/missing/ids/regexp 等&lt;/li&gt;&#10;&lt;li&gt;full text query ，全文检索query，对多个词（即句子）的query，包括&#10;match/multi_match/common 等&lt;/li&gt;&#10;&lt;li&gt;compound query 复合 query，包括 bool/dis_max/function_score 等&lt;/li&gt;&#10;&lt;li&gt;match_all ，简单匹配所有文档&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;建议先学习 term/match/range/bool ，就可以实现大部分业务逻辑。&lt;/p&gt;&#10;&lt;p&gt;网上资料较多，就不转述了。&lt;/p&gt;&#10;&lt;p&gt;可以先看看这些中文资料，在 test 环境的 kibana 做做实验，快速上手：&lt;/p&gt;&#10;&lt;p&gt;&lt;a href="https://www.elastic.co/guide/cn/elasticsearch/guide/current/search-in-depth.html" target="_blank" rel="noreferrer"&gt;https://www.elastic.co/guide/cn/elasticsearch/guide/current/search-in-depth.html&lt;/a&gt;&lt;/p&gt;&#10;&lt;p&gt;&lt;a href="https://my.oschina.net/yumg/blog/637409" target="_blank" rel="noreferrer"&gt;https://my.oschina.net/yumg/blog/637409&lt;/a&gt;&lt;/p&gt;&#10;&lt;p&gt;&lt;a href="https://www.cnblogs.com/yjf512/p/4897294.html" target="_blank" rel="noreferrer"&gt;https://www.cnblogs.com/yjf512/p/4897294.html&lt;/a&gt;&lt;/p&gt;&#10;&lt;p&gt;当然最好的还是官方英文文档：&lt;/p&gt;&#10;&lt;p&gt;&lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl.html" target="_blank" rel="noreferrer"&gt;https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl.html&lt;/a&gt;&lt;/p&gt;&#10;&#10;&lt;h3 class="relative group"&gt;2. 分词&#10; &lt;div id="2-分词" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#2-%e5%88%86%e8%af%8d" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h3&gt;&#10;&lt;p&gt;中文搜索的一个核心议题，就是分词。&lt;/p&gt;&#10;&lt;p&gt;ElasticSearch 常用的中文分词是 ik analyzer。ik 是开箱即用，便于小型业务快速开发的。&lt;/p&gt;&#10;&lt;p&gt;但是作为对分词可定制性要求较高的业务，我们实际测试，发现 ik analyzer :&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;不支持本地自定义词典文件的热加载；&lt;/li&gt;&#10;&lt;li&gt;无法针对不同 index 配置不同自定义词典；&lt;/li&gt;&#10;&lt;li&gt;另外对一些分词的 bad case ，比如没有正确切分的词，没法简单 fix。&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;因此推荐不用 ik ，而是在更新文档和搜索的时候，在外部做分词，然后用空格拼起来，传给 ES 做索引/搜索。这种方案中，在 ES mapping 中配置成 whitespace 分词器。&lt;/p&gt;&#10;&lt;p&gt;外部分词可以用 cppjieba 等，索引分词还可以合并多种分词算法结果提高召回率。&lt;/p&gt;&#10;&lt;p&gt;对 cppjieba ，我之前做过内存优化，将内存优化到了 1/100。&lt;/p&gt;&#10;&lt;p&gt;另外，索引之前，也有必要做 UTF8 的 normalize，全角转半角，英文大小写统一，和英文的词干提取， mapping 中常用&lt;/p&gt;&#10;&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&amp;#34;cjk_width&amp;#34;, &amp;#34;lowercase&amp;#34;, &amp;#34;porter_stem&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&#10;&lt;p&gt;这些filter&lt;/p&gt;&#10;&lt;p&gt;具体可以参考已有业务代码。&lt;/p&gt;&#10;&#10;&lt;h3 class="relative group"&gt;3.关系型搜索&#10; &lt;div id="3关系型搜索" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#3%e5%85%b3%e7%b3%bb%e5%9e%8b%e6%90%9c%e7%b4%a2" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h3&gt;&#10;&lt;p&gt;实际开发遇到典型的 one-many 关系型数据上的 query，&lt;/p&gt;&#10;&lt;p&gt;比如在某业务中，就遇到这种逻辑，经过调研发现常见有 4 种方案：&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;分开2 个 index : one + many ，分开2次串行 search，&#10;问题: 需要2次延迟大&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;反范式，完全展开，one 的数据追加到每一个 many 文档中，&#10;问题：数据量变大，更新 one 需要用 _update_by_query&#10;如果 one 数据更新频繁，可能导致大量写操作&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;nested ，比如 one 嵌套 many 子文档&#10;问题是：nested 嵌套文档更新需要更新整个 root 文档，即要把整个 one 文档 和含有的 many 文档 select 出来，修改，再写回。&#10;对热门 one 文档， 更新会操作大量数据，并发写还可能 data race。&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;join, has_parent, has_child&#10;把 one 和 many 的所有字段合并到一个 index 中， one 和 many 分别独立更新。&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;经过实际数据测试 join field 方案， 发现当 one:many = 1:1000万 时， 延迟在 5ms 可以接受，因此目前采用了这种方案。&lt;/p&gt;&#10;&lt;p&gt;当然，官方文档指出 join 性能是会慢的，后续也有待实践检验。&lt;/p&gt;&#10;&#10;&lt;h3 class="relative group"&gt;4. 粗排相关性打分&#10; &lt;div id="4-粗排相关性打分" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#4-%e7%b2%97%e6%8e%92%e7%9b%b8%e5%85%b3%e6%80%a7%e6%89%93%e5%88%86" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h3&gt;&#10;&lt;p&gt;Lucene 从 2016年的 6.0 版本开始，默认的相关性算法切换成了 bm25 ，&#10;bm25 是一种调整过的 tf idf 算法。&lt;/p&gt;&#10;&lt;p&gt;这里可以做一简单举例介绍，更深入的介绍可以参见下面文章，以及官方文档：&lt;/p&gt;&#10;&lt;p&gt;&lt;a href="https://farer.org/2018/09/10/practical-bm25-part-1-how-shards-affect-relevance-scoring-in-elasticsearch/" target="_blank" rel="noreferrer"&gt;https://farer.org/2018/09/10/practical-bm25-part-1-how-shards-affect-relevance-scoring-in-elasticsearch/&lt;/a&gt;&lt;/p&gt;&#10;&lt;p&gt;&lt;a href="https://www.cnblogs.com/richaaaard/p/5254988.html" target="_blank" rel="noreferrer"&gt;https://www.cnblogs.com/richaaaard/p/5254988.html&lt;/a&gt;&lt;/p&gt;&#10;&lt;p&gt;ES 的 explain 对 bm25 算分的过程有详尽的解释，推荐自行实验。&lt;/p&gt;&#10;&lt;p&gt;&lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/search-explain.html" target="_blank" rel="noreferrer"&gt;https://www.elastic.co/guide/en/elasticsearch/reference/current/search-explain.html&lt;/a&gt;&lt;/p&gt;&#10;&#10;&lt;h4 class="relative group"&gt;4.1. BM25 例解&#10; &lt;div id="41-bm25-例解" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#41-bm25-%e4%be%8b%e8%a7%a3" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h4&gt;&#10;&lt;p&gt;比如某业务的真实数据中，我们在所有文档的 title 这个 field 搜索 “牛奶 ” 这个词，&lt;/p&gt;&#10;&lt;p&gt;explain 可以看到，这个 bm25 分数的是这样得来的：&lt;/p&gt;&#10;&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sum( weight(title:牛奶 in 77341) [PerFieldSimilarity] ) ，&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;weight(title:牛奶 in 77341) [PerFieldSimilarity] = idf * tfNorm&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&#10;&lt;p&gt;首先，如果某 field 被多个 term 命中，分别算每个 term 的分数 (PerFieldSimilarity)，然后求和，本例子只有1个 term “牛奶”。&lt;/p&gt;&#10;&lt;p&gt;每个 term 的分数 PerFieldSimilarity&lt;/p&gt;&#10;&lt;p&gt;&lt;code&gt;PerFieldSimilarity = idf * tfNorm&lt;/code&gt;&lt;/p&gt;&#10;&lt;p&gt;而 idf 表征词的重要程度，与具体文档无关。&lt;/p&gt;&#10;&lt;p&gt;&lt;code&gt;idf = log(1 + (docCount - docFreq + 0.5) / (docFreq + 0.5))&lt;/code&gt;&lt;/p&gt;&#10;&lt;p&gt;其中&#10;docFreq 就是本shard 中，有多少个文档含有 “牛奶”，&#10;docCount 就是本shard 一共有多少个文档。&lt;/p&gt;&#10;&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;tfNorm = (freq * (k1 + 1)) / (freq + k1 * (1 - b + b * fieldLength / avgFieldLength))&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;termFreq=1.0&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;k1=1.2&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;b=0.75&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;avgFieldLength=14.456173&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;fieldLength=2 //比如如果 title 是 &amp;#34;牛奶 醪糟&amp;#34; ，那就是 2个 term&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&#10;&lt;p&gt;freq 即该 field 中，“牛奶”这个词出现了几次&#10;k1 和 b 都是固定常数。&#10;fieldLength 是当前文档的当前field ，一共有多少个 term。&#10;avgFieldLength 即本 shard 中的所有文档的本 field 的 fieldLength 的平均值&lt;/p&gt;&#10;&#10;&lt;h4 class="relative group"&gt;4.2. BM25 shard 调整&#10; &lt;div id="42-bm25-shard-调整" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#42-bm25-shard-%e8%b0%83%e6%95%b4" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h4&gt;&#10;&lt;p&gt;实际业务发现，当 index 内文档太少(比如 10w 量级就算少) 时 ， 有的词在多个 shard 内，词频分布会出现严重不均匀，可能会导致 bm25 分数产生较大偏差，&lt;/p&gt;&#10;&lt;p&gt;实践中的解决办法：&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Search 的 url_parameters 参数填 “&amp;amp;search_type=dfs_query_then_fetch”&lt;/li&gt;&#10;&lt;li&gt;减少 shard 数&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&#10;&lt;h4 class="relative group"&gt;4.3. BM25 similarity 参数调整&#10; &lt;div id="43-bm25-similarity-参数调整" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#43-bm25-similarity-%e5%8f%82%e6%95%b0%e8%b0%83%e6%95%b4" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h4&gt;&#10;&lt;p&gt;如上计算过程可见，bm25 中的 b 参数，是用来给短文档做加权的，即 b 越大，越倾向于给短文档更高的 score，&#10;实际中，和算法同学一起分析后，发现针对我们的某业务，不应该对短文本有太高偏向，所以我们把 b 调整成了 0.3 ，&#10;实测发现解决了一批 bad case，用户体验有明显改善。&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;四，性能优化&#10; &lt;div id="四性能优化" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%9b%9b%e6%80%a7%e8%83%bd%e4%bc%98%e5%8c%96" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;计算机程序的性能取决于数据结构和算法，&#10;ES/Lucene 中主要有几种数据结构:&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;FST&lt;/li&gt;&#10;&lt;li&gt;Posting List ，著名的倒排索引，PForDelta 压缩，支持 SkipList 方式跳跃&lt;/li&gt;&#10;&lt;li&gt;BKD Tree，用来实现 int 和 geo 查询&lt;/li&gt;&#10;&lt;li&gt;DocValues , 以 DocID 为 Key 的列存储&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;&lt;a href="https://zhuanlan.zhihu.com/p/47951652" target="_blank" rel="noreferrer"&gt;https://zhuanlan.zhihu.com/p/47951652&lt;/a&gt;&lt;/p&gt;&#10;&lt;p&gt;&lt;a href="https://www.elastic.co/blog/elasticsearch-query-execution-order" target="_blank" rel="noreferrer"&gt;https://www.elastic.co/blog/elasticsearch-query-execution-order&lt;/a&gt;&lt;/p&gt;&#10;&lt;p&gt;更深入的理解，我目前也在探索中。&lt;/p&gt;&#10;&lt;p&gt;在垂直搜索引擎业务中，用户对延迟非常敏感，一般业界经验认为，良好的用户体验应该是在 ** 200毫秒 ** 内返回搜索结果，&#10;这就意味着 ES 延迟最好控制在 100毫秒之内。&lt;/p&gt;&#10;&lt;p&gt;经过我们实际业务发现，决定 ES 延迟的因素主要有：&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;内存是否足够， page cache 是否 cache 了检索过程用到的文件数据&lt;/li&gt;&#10;&lt;li&gt;具体 query 的优化，类比 mysql query 优化&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&#10;&lt;h3 class="relative group"&gt;1. page cache 内存优化&#10; &lt;div id="1-page-cache-内存优化" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#1-page-cache-%e5%86%85%e5%ad%98%e4%bc%98%e5%8c%96" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h3&gt;&#10;&lt;p&gt;page cache 是决定 ES 延迟的首要因素，用作在线检索服务的 ES ，&#10;实际中在线检索的代码路径不能有硬盘 io 访问 (实践证明， SSD也不行)&lt;/p&gt;&#10;&lt;p&gt;当 ES 用作在线垂直搜索引擎时，&lt;/p&gt;&#10;&lt;p&gt;《查询亿级数据毫秒级返回！牛逼哄哄的ElasticSearch是如何做到的？》&#10;&lt;a href="https://zhuanlan.zhihu.com/p/68706615" target="_blank" rel="noreferrer"&gt;https://zhuanlan.zhihu.com/p/68706615&lt;/a&gt;&lt;/p&gt;&#10;&lt;p&gt;《ElasticSearch在数十亿级别数据下，如何提高查询效率？》&#10;&lt;a href="https://zhuanlan.zhihu.com/p/60458049" target="_blank" rel="noreferrer"&gt;https://zhuanlan.zhihu.com/p/60458049&lt;/a&gt;&lt;/p&gt;&#10;&lt;p&gt;实践中，某 index 发现延迟非常高，达到了 1-2秒，用户体验很差。&#10;调查发现，iostat 看下 io util 很高，经常到 80% 90%，单机索引数据文件是 page cache 可用内存的 4倍，&#10;于是降低了副本数，单机数据量减少到 page cache 可用内存2倍后， 硬盘 io 降到了 0 ，延迟一下降低到了 150ms 。&lt;/p&gt;&#10;&#10;&lt;h3 class="relative group"&gt;2. int 字段查询优化&#10; &lt;div id="2--int-字段查询优化" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#2--int-%e5%ad%97%e6%ae%b5%e6%9f%a5%e8%af%a2%e4%bc%98%e5%8c%96" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h3&gt;&#10;&lt;p&gt;业务中常会有一些 int 型的字段，存一些枚举性质的值。&#10;在 10亿以上文档的情况下，实际发现有的会出性能问题。&lt;/p&gt;&#10;&lt;p&gt;比如 比如前述业务有1个 int 类型的 filter 字段，实际只有 {0,1} 2种取值，&lt;/p&gt;&#10;&lt;p&gt;借助 ES 的 profile ，我们发现搜索 query 93% 的耗时在 filter 字段的 PointInSetQuery 中，&lt;/p&gt;&#10;&lt;p&gt;随后发现，针对该业务，只需要返回 filter 为 0 的文档，于是我们在更新文档时，发现 filter 非0 的文档，直接把所有字段都清空，并随后在 query 中去掉了 filter 字段的过滤。&lt;/p&gt;&#10;&lt;p&gt;之后发现耗时从 150ms 降到了 20ms。&lt;/p&gt;&#10;&#10;&lt;h3 class="relative group"&gt;3. 副本数 replicas num&#10; &lt;div id="3-副本数-replicas-num" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#3-%e5%89%af%e6%9c%ac%e6%95%b0-replicas-num" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h3&gt;&#10;&lt;p&gt;确定副本数的思路：&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;副本数越小越好。越小，单机数据越少，文件被 cache 的比例越高，性能越好。&lt;/li&gt;&#10;&lt;li&gt;副本太少会影响可用性。因此必须大于最大能容忍故障单机个数 max_failures 。&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;综合起来就是：&lt;/p&gt;&#10;&lt;p&gt;&lt;code&gt;max(max_failures, ceil(num_nodes / num_primaries) - 1).&lt;/code&gt;&lt;/p&gt;&#10;&lt;p&gt;num_primaries 是 primary shards 的数量，就是一个 index 有多少个 shards，一般都 &amp;gt; num_nodes&lt;/p&gt;&#10;&lt;p&gt;&lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/master/tune-for-search-speed.html#_replicas_might_help_with_throughput_but_not_always" target="_blank" rel="noreferrer"&gt;replicas_might_help_with_throughput_but_not_always&lt;/a&gt;&lt;/p&gt;&#10;&lt;p&gt;对数据量特别少的 index，可以每台机都存一个副本&#10;&amp;ldquo;auto_expand_replicas&amp;rdquo;: &amp;ldquo;0-all&amp;rdquo;,&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;4. 多 SSD&#10; &lt;div id="4--多-ssd" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#4--%e5%a4%9a-ssd" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;在 elasticsearc.yml 的 path.data 配置多个路径，ES 会自动把 shard 均分到多个路径上，如果有多个硬盘，可以充分利用多设备的 io 带宽，当然对在线业务意义不大。&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;5. 内存配置&#10; &lt;div id="5-内存配置" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#5-%e5%86%85%e5%ad%98%e9%85%8d%e7%bd%ae" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;最开始我们使用 16G 内存机型，&#10;后来发现出现大量 Elasticsearch Data too large Error 错误，随后发现，解决办法就是换到 64G 内存机型，&lt;/p&gt;&#10;&lt;p&gt;改 jvm.options 加大 jvm 的 heap 解决，从 10G 加大到 30G 解决&#10;-Xms30g&#10;-Xmx30g&lt;/p&gt;&#10;&lt;p&gt;需要注意的是，不建议大于 32G，避免 jvm 的指针压缩优化失效。&#10;可以看 ES 的启动 log 确定&lt;/p&gt;&#10;&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[2019-05-22T12:29:16,961][INFO ][o.e.e.NodeEnvironment ] [node_xxx] heap size [29.7gb], compressed ordinary object pointers [true]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&#10;&lt;p&gt;&lt;a href="https://www.elastic.co/cn/blog/a-heap-of-trouble" target="_blank" rel="noreferrer"&gt;https://www.elastic.co/cn/blog/a-heap-of-trouble&lt;/a&gt;&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;6. refresh_interval&#10; &lt;div id="6-refresh_interval" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#6-refresh_interval" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;如网上众多文章所说， refresh_interval 一般都设成了 30秒。&lt;/p&gt;&#10;&lt;p&gt;一些参考资料：&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;滴滴 Elasticsearch 多集群架构实践 &lt;a href="https://cloud.tencent.com/developer/article/1405404" target="_blank" rel="noreferrer"&gt;https://cloud.tencent.com/developer/article/1405404&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;京东到家订单中心 Elasticsearch 演进历程 &lt;a href="https://cloud.tencent.com/developer/article/1377194" target="_blank" rel="noreferrer"&gt;https://cloud.tencent.com/developer/article/1377194&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;从平台到中台：Elaticsearch在蚂蚁金服的实践经验 &lt;a href="https://new.qq.com/omn/20190117/20190117A09ADY.html" target="_blank" rel="noreferrer"&gt;https://new.qq.com/omn/20190117/20190117A09ADY.html&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;美团点评旅游搜索召回策略的演进 &lt;a href="https://tech.meituan.com/2017/06/16/travel-search-strategy.html" target="_blank" rel="noreferrer"&gt;https://tech.meituan.com/2017/06/16/travel-search-strategy.html&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;eBay 的 Elasticsearch 性能调优实践 &lt;a href="https://www.infoq.cn/article/elasticsearch-performance-tuning-practice-at-ebay" target="_blank" rel="noreferrer"&gt;https://www.infoq.cn/article/elasticsearch-performance-tuning-practice-at-ebay&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;有赞搜索系统的架构演进 &lt;a href="https://tech.youzan.com/search-tech-1/" target="_blank" rel="noreferrer"&gt;https://tech.youzan.com/search-tech-1/&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;有赞搜索引擎实践(工程篇) &lt;a href="https://tech.youzan.com/search-engine1/" target="_blank" rel="noreferrer"&gt;https://tech.youzan.com/search-engine1/&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;全面理解搜索Query：当你在搜索引擎中敲下回车后，发生了什么？ &lt;a href="https://zhuanlan.zhihu.com/p/112719984" target="_blank" rel="noreferrer"&gt;https://zhuanlan.zhihu.com/p/112719984&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;elasticsearch初学终极教程: 从零到一 &lt;a href="https://kalasearch.cn/blog/elasticsearch-tutorial/" target="_blank" rel="noreferrer"&gt;https://kalasearch.cn/blog/elasticsearch-tutorial/&lt;/a&gt;&lt;/li&gt;&#10;&lt;/ul&gt;</content:encoded></item><item><title>Python 自动翻译成 C++ ，彻底保证离线在线特征一致</title><link>https://byronhe.com/post/2019/11/29/pthran-py-to-cpp-translater-in-feature-engineering/</link><pubDate>Fri, 29 Nov 2019 00:00:00 +0000</pubDate><guid>https://byronhe.com/post/2019/11/29/pthran-py-to-cpp-translater-in-feature-engineering/</guid><description>&lt;h2 class="relative group"&gt;一，问题背景&#10; &lt;div id="一问题背景" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e4%b8%80%e9%97%ae%e9%a2%98%e8%83%8c%e6%99%af" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;随着深度学习的广泛应用，在搜索引擎/推荐系统/机器视觉等业务系统中，越来越多的深度学习模型部署到线上服务。&lt;/p&gt;&#10;&lt;p&gt;机器学习模型在离线训练时，一般要将输入的数据做特征工程预处理，再输入模型在 TensorFlow PyTorch 等框架上做训练。&lt;/p&gt;&#10;&#10;&lt;h4 class="relative group"&gt;1.常见的特征工程逻辑&#10; &lt;div id="1常见的特征工程逻辑" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#1%e5%b8%b8%e8%a7%81%e7%9a%84%e7%89%b9%e5%be%81%e5%b7%a5%e7%a8%8b%e9%80%bb%e8%be%91" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h4&gt;&#10;&lt;p&gt;常见的特征工程逻辑有：&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;分箱/分桶 离散化&lt;/li&gt;&#10;&lt;li&gt;log/exp 对数/幂等 math numpy 常见数学运算&lt;/li&gt;&#10;&lt;li&gt;特征缩放/归一化/截断&lt;/li&gt;&#10;&lt;li&gt;交叉特征生成&lt;/li&gt;&#10;&lt;li&gt;分词匹配程度计算&lt;/li&gt;&#10;&lt;li&gt;字符串分隔匹配判断tong&lt;/li&gt;&#10;&lt;li&gt;缺省值填充等&lt;/li&gt;&#10;&lt;li&gt;数据平滑&lt;/li&gt;&#10;&lt;li&gt;onehot 编码，hash 编码等&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;这些特征工程代码，当然一般使用深度学习最主要的语言 &lt;strong&gt;python&lt;/strong&gt; 实现。&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;二，业务痛点&#10; &lt;div id="二业务痛点" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e4%ba%8c%e4%b8%9a%e5%8a%a1%e7%97%9b%e7%82%b9" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;离线训练完成，模型上线部署后，同样要&lt;strong&gt;用 C++ 重新实现&lt;/strong&gt; 这些 python 的特征工程逻辑代码。&lt;/p&gt;&#10;&lt;p&gt;我们发现，&lt;strong&gt;“用 C++ 重新实现”&lt;/strong&gt; 这个步骤，给实际业务带来了大量的问题：&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;繁琐，费时费力，极容易出现 python 和 C++ 代码&lt;strong&gt;不一致&lt;/strong&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;不一致&lt;/strong&gt;会直接影响模型在线上的效果，导致大盘业务指标不如预期，产生各种 bad case&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;不一致&lt;/strong&gt;难以发现，无法测试，无法监控，经常要靠用户投诉反馈，甚至大盘数据异常才能发现&lt;/li&gt;&#10;&lt;/ol&gt;</description><content:encoded>&lt;h2 class="relative group"&gt;一，问题背景&#10; &lt;div id="一问题背景" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e4%b8%80%e9%97%ae%e9%a2%98%e8%83%8c%e6%99%af" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;随着深度学习的广泛应用，在搜索引擎/推荐系统/机器视觉等业务系统中，越来越多的深度学习模型部署到线上服务。&lt;/p&gt;&#10;&lt;p&gt;机器学习模型在离线训练时，一般要将输入的数据做特征工程预处理，再输入模型在 TensorFlow PyTorch 等框架上做训练。&lt;/p&gt;&#10;&#10;&lt;h4 class="relative group"&gt;1.常见的特征工程逻辑&#10; &lt;div id="1常见的特征工程逻辑" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#1%e5%b8%b8%e8%a7%81%e7%9a%84%e7%89%b9%e5%be%81%e5%b7%a5%e7%a8%8b%e9%80%bb%e8%be%91" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h4&gt;&#10;&lt;p&gt;常见的特征工程逻辑有：&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;分箱/分桶 离散化&lt;/li&gt;&#10;&lt;li&gt;log/exp 对数/幂等 math numpy 常见数学运算&lt;/li&gt;&#10;&lt;li&gt;特征缩放/归一化/截断&lt;/li&gt;&#10;&lt;li&gt;交叉特征生成&lt;/li&gt;&#10;&lt;li&gt;分词匹配程度计算&lt;/li&gt;&#10;&lt;li&gt;字符串分隔匹配判断tong&lt;/li&gt;&#10;&lt;li&gt;缺省值填充等&lt;/li&gt;&#10;&lt;li&gt;数据平滑&lt;/li&gt;&#10;&lt;li&gt;onehot 编码，hash 编码等&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;这些特征工程代码，当然一般使用深度学习最主要的语言 &lt;strong&gt;python&lt;/strong&gt; 实现。&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;二，业务痛点&#10; &lt;div id="二业务痛点" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e4%ba%8c%e4%b8%9a%e5%8a%a1%e7%97%9b%e7%82%b9" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;离线训练完成，模型上线部署后，同样要&lt;strong&gt;用 C++ 重新实现&lt;/strong&gt; 这些 python 的特征工程逻辑代码。&lt;/p&gt;&#10;&lt;p&gt;我们发现，&lt;strong&gt;“用 C++ 重新实现”&lt;/strong&gt; 这个步骤，给实际业务带来了大量的问题：&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;繁琐，费时费力，极容易出现 python 和 C++ 代码&lt;strong&gt;不一致&lt;/strong&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;不一致&lt;/strong&gt;会直接影响模型在线上的效果，导致大盘业务指标不如预期，产生各种 bad case&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;不一致&lt;/strong&gt;难以发现，无法测试，无法监控，经常要靠用户投诉反馈，甚至大盘数据异常才能发现&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&#10;&lt;h4 class="relative group"&gt;1. 业界方案&#10; &lt;div id="1-业界方案" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#1-%e4%b8%9a%e7%95%8c%e6%96%b9%e6%a1%88" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h4&gt;&#10;&lt;p&gt;针对这些问题，我调研了这些业界方案：&lt;/p&gt;&#10;&lt;p&gt;《推荐系统中模型训练及使用流程的标准化》&#10;&lt;a href="https://www.infoq.cn/article/2E6LCqb1GeqFRAjkkjX3" target="_blank" rel="noreferrer"&gt;https://www.infoq.cn/article/2E6LCqb1GeqFRAjkkjX3&lt;/a&gt;&lt;/p&gt;&#10;&lt;p&gt;《自主研发、不断总结经验，美团搜索推荐机器学习平台》&#10;&lt;a href="https://cloud.tencent.com/developer/article/1357309" target="_blank" rel="noreferrer"&gt;https://cloud.tencent.com/developer/article/1357309&lt;/a&gt;&lt;/p&gt;&#10;&lt;p&gt;《京东电商推荐系统实践》&#10;&lt;a href="https://www.infoq.cn/article/1OkKmb_gEYNR3YqC9RcW" target="_blank" rel="noreferrer"&gt;https://www.infoq.cn/article/1OkKmb_gEYNR3YqC9RcW&lt;/a&gt;&lt;/p&gt;&#10;&lt;blockquote&gt;&lt;p&gt;“模型线上线下一致性问题对于模型效果非常重要，我们使用特征日志来实时记录特征，保证特征的一致性。这样离线处理的时候会把实时的用户反馈，和特征日志做一个结合生成训练样本，然后更新到模型训练平台上，平台更新之后在推送到线上，这样整个排序形成了一个闭环。”&lt;/p&gt;&#10;&lt;/blockquote&gt;&lt;p&gt;总结起来，有几种思路：&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;在线特征存储起来给离线用&lt;/li&gt;&#10;&lt;li&gt;在线 C++ 代码编译成 so 导出给离线用&lt;/li&gt;&#10;&lt;li&gt;根据一份配置生成离线和在线代码&lt;/li&gt;&#10;&lt;li&gt;提取公共代码，加强代码复用，等软件工程手段，减少不一致&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&#10;&lt;h4 class="relative group"&gt;2. 自动翻译方案&#10; &lt;div id="2-自动翻译方案" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#2-%e8%87%aa%e5%8a%a8%e7%bf%bb%e8%af%91%e6%96%b9%e6%a1%88" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h4&gt;&#10;&#10;&lt;h5 class="relative group"&gt;(1) .已有方案的缺点&#10; &lt;div id="1-已有方案的缺点" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#1-%e5%b7%b2%e6%9c%89%e6%96%b9%e6%a1%88%e7%9a%84%e7%bc%ba%e7%82%b9" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h5&gt;&#10;&lt;p&gt;但这些思路都有各种缺点：&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;所有在线请求的所有特征，这个存储量数据量很大&lt;/li&gt;&#10;&lt;li&gt;算法改代码需要等待后台开发，降低了算法同学的工作效率&lt;/li&gt;&#10;&lt;li&gt;特征处理代码的复杂度转移到配置文件中，不一定能充分表达，而且配置格式增加学习成本&lt;/li&gt;&#10;&lt;li&gt;就这边真实离线特征处理代码来看，大部分代码都无法抽取出公共代码做复用。&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&#10;&lt;h5 class="relative group"&gt;(2). 翻译器&#10; &lt;div id="2-翻译器" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#2-%e7%bf%bb%e8%af%91%e5%99%a8" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h5&gt;&#10;&lt;p&gt;回到问题出发点考虑，显而易见，这个问题归根结底就是需要一个 “ python 到 c++ 的翻译器 ” 。&lt;/p&gt;&#10;&lt;p&gt;那其实 “翻译器 Transpiler ” ，和编译器解释器类似，也是个古老的热门话题了，比如 &lt;a href="https://webassembly.org/" target="_blank" rel="noreferrer"&gt;WebAssembly&lt;/a&gt;, &lt;a href="https://coffeescript.org/" target="_blank" rel="noreferrer"&gt;CoffeeScript &lt;/a&gt;，&lt;a href="https://www.babeljs.cn/docs/" target="_blank" rel="noreferrer"&gt;Babel&lt;/a&gt; ,&#10;&lt;a href="https://github.com/google/closure-compiler" target="_blank" rel="noreferrer"&gt;Google Closure Compiler&lt;/a&gt;，&lt;a href="https://www.netlib.org/f2c/f2c.1" target="_blank" rel="noreferrer"&gt;f2c&lt;/a&gt;&lt;/p&gt;&#10;&lt;p&gt;于是一番搜索，发现 python 到 C++ 的翻译器也不少，其中 &lt;a href="https://github.com/serge-sans-paille/pythran" target="_blank" rel="noreferrer"&gt;Pythran&lt;/a&gt; 是新兴比较热门的开源项目。&lt;/p&gt;&#10;&lt;p&gt;于是一番尝试后，借助 pythran，我们实现了：&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;一条命令 &lt;strong&gt;全自动把 Python 翻译成等价 C++&lt;/strong&gt;&lt;/li&gt;&#10;&lt;li&gt;严格等价保证改写，彻底消除不一致&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;完全去掉重新实现&lt;/strong&gt; 这块工作量，后台开发成本降到 0 ，彻底解放生产力&lt;/li&gt;&#10;&lt;li&gt;算法同学继续使用纯 python，开发效率无影响，** 无学习成本 **&lt;/li&gt;&#10;&lt;li&gt;并能推广到其他需要 &lt;strong&gt;python 改写成后台 C++ 代码&lt;/strong&gt; 的业务场景，解放生产力&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&#10;&lt;h2 class="relative group"&gt;三，pythran 的使用流程&#10; &lt;div id="三pythran-的使用流程" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e4%b8%89pythran-%e7%9a%84%e4%bd%bf%e7%94%a8%e6%b5%81%e7%a8%8b" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&#10;&lt;h4 class="relative group"&gt;(1). 安装&#10; &lt;div id="1-安装" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#1-%e5%ae%89%e8%a3%85" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h4&gt;&#10;&lt;p&gt;一条命令安装：&lt;/p&gt;&#10;&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pip3 install pythran&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&#10;&#10;&lt;h4 class="relative group"&gt;(2). 写 Python 代码&#10; &lt;div id="2-写-python-代码" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#2-%e5%86%99-python-%e4%bb%a3%e7%a0%81" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h4&gt;&#10;&lt;p&gt;下面这个 python demo，是 pythran 官方 demo&lt;/p&gt;&#10;&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;math&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;numpy&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nn"&gt;np&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;zero&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;#pythran export matrix_multiply(float list list, float list list)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;matrix_multiply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;m1&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;new_matrix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;zero&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m1&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m0&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m1&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])):&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m1&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;new_matrix&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;m0&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;m1&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;new_matrix&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;#pythran export arc_distance(float[], float[], float[], float[])&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;arc_distance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;theta_1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;phi_1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;theta_2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;phi_2&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; Calculates the pairwise arc distance&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; between all points in vector a and b.&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;temp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sin&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;theta_2&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;theta_1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;theta_1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;theta_2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sin&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;phi_2&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;phi_1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;distance_matrix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arctan2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;temp&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;temp&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;distance_matrix&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;#pythran export dprod(int list, int list)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;dprod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;l0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;l1&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;WoW, generator expression, zip and sum.&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;l0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;l1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;#pythran export get_age(int )&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_age&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;age_x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;0_20&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;age_x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;21_25&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;age_x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;26_30&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;35&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;age_x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;31_35&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;age_x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;36_40&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;age_x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;41_45&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;age_x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;46_50&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;age_x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;50+&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;age_x&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&#10;&#10;&lt;h4 class="relative group"&gt;(3). Python 转成 C++&#10; &lt;div id="3-python-转成-c" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#3-python-%e8%bd%ac%e6%88%90-c" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h4&gt;&#10;&lt;p&gt;一条命令完成翻译&lt;/p&gt;&#10;&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pythran -e demo.py -o demo.hpp&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&#10;&#10;&lt;h4 class="relative group"&gt;(4). 写 C++ 代码调用&#10; &lt;div id="4-写-c-代码调用" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#4-%e5%86%99-c-%e4%bb%a3%e7%a0%81%e8%b0%83%e7%94%a8" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h4&gt;&#10;&lt;p&gt;pythran/pythonic/ 目录下是 python 标准库的 C++ 等价实现，翻译出来的C++ 代码需要 include 这些头文件&lt;/p&gt;&#10;&lt;p&gt;写个 C++ 代码调用&lt;/p&gt;&#10;&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;#34;demo.hpp&amp;#34;&lt;/span&gt;&lt;span class="cp"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;#34;pythonic/numpy/random/rand.hpp&amp;#34;&lt;/span&gt;&lt;span class="cp"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;iostream&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cout&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;endl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;pythonic&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;pythonic&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;m0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;3.0&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mf"&gt;4.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;5.0&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;m1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mf"&gt;3.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;4.0&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;cout&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;m0&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;*&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;m1&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;=&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;__pythran_demo&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;matrix_multiply&lt;/span&gt;&lt;span class="p"&gt;()(&lt;/span&gt;&lt;span class="n"&gt;m0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;m1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;endl&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;endl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;theta_1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pythonic&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;numpy&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;phi_1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pythonic&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;numpy&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;theta_2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pythonic&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;numpy&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;phi_2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pythonic&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;numpy&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;cout&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;arc_distance &amp;#34;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;theta_1&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;,&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;phi_1&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;,&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;theta_2&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;,&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;phi_2&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;=&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;__pythran_demo&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;arc_distance&lt;/span&gt;&lt;span class="p"&gt;()(&lt;/span&gt;&lt;span class="n"&gt;theta_1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;phi_1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;theta_2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;phi_2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;endl&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;endl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;pythonic&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;l0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;l1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;cout&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;dprod &amp;#34;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;l0&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;,&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;l1&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;=&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;__pythran_demo&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;dprod&lt;/span&gt;&lt;span class="p"&gt;()(&lt;/span&gt;&lt;span class="n"&gt;l0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;l1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;endl&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;endl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;cout&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;get_age 30 = &amp;#34;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;__pythran_demo&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;get_age&lt;/span&gt;&lt;span class="p"&gt;()(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;endl&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;endl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&#10;&#10;&lt;h4 class="relative group"&gt;(5). 编译运行&#10; &lt;div id="5-编译运行" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#5-%e7%bc%96%e8%af%91%e8%bf%90%e8%a1%8c" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h4&gt;&#10;&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;g++ -g -std&lt;span class="o"&gt;=&lt;/span&gt;c++11 main.cpp -fopenmp -march&lt;span class="o"&gt;=&lt;/span&gt;native -DUSE_XSIMD -I /usr/local/lib/python3.6/site-packages/pythran/ -o pythran_demo &#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;./pythran_demo&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&#10;&#10;&lt;h2 class="relative group"&gt;四，pythran 的功能与特性&#10; &lt;div id="四pythran-的功能与特性" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%9b%9bpythran-%e7%9a%84%e5%8a%9f%e8%83%bd%e4%b8%8e%e7%89%b9%e6%80%a7" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&#10;&lt;h4 class="relative group"&gt;(1). 介绍&#10; &lt;div id="1-介绍" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#1-%e4%bb%8b%e7%bb%8d" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h4&gt;&#10;&lt;p&gt;按官方定义，Pythran 是一个 AOT (Ahead-Of-Time - 预先编译) 编译器。 给科学计算的 python 加注解后，pythran 可以把 python 代码变成接口相同的原生 python 模块，大幅度提升性能。&lt;/p&gt;&#10;&lt;p&gt;并且 pythran 也可以利用 OpenMP 多核和 SIMD 指令集。&lt;/p&gt;&#10;&lt;p&gt;支持 python 3 和 Python 2.7 。&lt;/p&gt;&#10;&lt;p&gt;pythran 的 manual 挺详细：&#10;&lt;a href="https://pythran.readthedocs.io/en/latest/MANUAL.html" target="_blank" rel="noreferrer"&gt;https://pythran.readthedocs.io/en/latest/MANUAL.html&lt;/a&gt;&lt;/p&gt;&#10;&#10;&lt;h4 class="relative group"&gt;(2). 功能&#10; &lt;div id="2-功能" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#2-%e5%8a%9f%e8%83%bd" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h4&gt;&#10;&lt;p&gt;pythran 并不支持完整的python， 只支持 python 语言特性的一个子集:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;polymorphic functions 多态函数(翻译成 C++ 的泛型模板函数)&lt;/li&gt;&#10;&lt;li&gt;lambda&lt;/li&gt;&#10;&lt;li&gt;list comprehension 列表推导式&lt;/li&gt;&#10;&lt;li&gt;map, reduce 等函数&lt;/li&gt;&#10;&lt;li&gt;dictionary, set, list 等数据结构&lt;/li&gt;&#10;&lt;li&gt;exceptions 异常&lt;/li&gt;&#10;&lt;li&gt;file handling 文件处理&lt;/li&gt;&#10;&lt;li&gt;部分 numpy&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;不支持的功能：&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;classes 类&lt;/li&gt;&#10;&lt;li&gt;polymorphic variables 可变类型变量&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&#10;&lt;h4 class="relative group"&gt;(3). 支持的数据类型和函数&#10; &lt;div id="3-支持的数据类型和函数" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#3-%e6%94%af%e6%8c%81%e7%9a%84%e6%95%b0%e6%8d%ae%e7%b1%bb%e5%9e%8b%e5%92%8c%e5%87%bd%e6%95%b0" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h4&gt;&#10;&lt;p&gt;pythran export 可以导出函数和全局变量。&#10;支持导出的数据类型，BNF 定义是：&lt;/p&gt;&#10;&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-antlr" data-lang="antlr"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;argument_type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;basic_type&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;(argument_type+)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;this&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;tuple&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;argument_type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;list&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;this&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;list&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;argument_type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;this&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;set&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;argument_type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="err"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;this&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;ndarray&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;C&lt;/span&gt;&lt;span class="err"&gt;-style&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;argument_type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="x"&gt;::&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;this&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;strided&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;ndarray&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;argument_type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="x"&gt;:,...,:&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;this&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;ndarray&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;Cython&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;style&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;argument_type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="x"&gt;:,...,3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;this&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;ndarray&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;some&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;dimension&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;fixed&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;argument_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;argument_type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;dict&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;this&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;dictionary&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;basic_type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;bool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;byte&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;str&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;None&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;slice&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;uint8&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;uint16&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;uint32&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;uint64&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;uintp&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;int8&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;int16&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;int32&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;int64&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;intp&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;float32&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;float64&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;float128&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;complex64&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;complex128&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;complex256&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&#10;&lt;p&gt;可以看到基础类型相当全面，支持各种 整数，浮点数，字符串，复数&lt;/p&gt;&#10;&lt;p&gt;复合类型支持 tuple, list, set, dict, numpy.ndarray 等，&lt;/p&gt;&#10;&lt;p&gt;对应 C++ 代码的类型实现在 pythran/pythonic/include/types/ 下面，可以看到比如 dict 实际就是封装了一下 std::unordered_map&#10;&lt;a href="https://pythran.readthedocs.io/en/latest/SUPPORT.html" target="_blank" rel="noreferrer"&gt;https://pythran.readthedocs.io/en/latest/SUPPORT.html&lt;/a&gt;&#10;可以看到支持的 python 基础库，其中常用于机器学习的 numpy 支持算比较完善。&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;五，pythran 的基本原理&#10; &lt;div id="五pythran-的基本原理" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e4%ba%94pythran-%e7%9a%84%e5%9f%ba%e6%9c%ac%e5%8e%9f%e7%90%86" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;和常见的编译器/解释器类似， pythran 的架构是分成 3 层：&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;python 代码解析成抽象语法树 AST 。用 python 标准库自带的的 ast 模块实现&lt;/li&gt;&#10;&lt;li&gt;代码优化。&#10;在 AST 上做优化，有多种 transformation pass，比如 deadcode_elimination 死代码消除，loop_full_unrolling 循环展开 等。还有 Function/Module/Node 级别的 Analysis，用来遍历 AST 供 transformation 利用。&lt;/li&gt;&#10;&lt;li&gt;后端，实现代码生成。目前有2个后端，Cxx / Python， Cxx 后端可以把 AST 转成 C++ 代码（ Python 后端用来调试）。&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;目前看起来 ，pythran 还欠缺的：&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;字符串处理能力欠缺，缺少 str.encode()/str.decode() 对 utf8 的支持&lt;/li&gt;&#10;&lt;li&gt;缺少正则表达式 regex 支持&lt;/li&gt;&#10;&lt;li&gt;缺少 json 支持&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;看文档要自己加也不麻烦，看业务需要可以加。&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;六，相关文章&#10; &lt;div id="六相关文章" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%85%ad%e7%9b%b8%e5%85%b3%e6%96%87%e7%ab%a0" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;《京东电商推荐系统实践》&#10;&lt;a href="https://www.infoq.cn/article/1OkKmb_gEYNR3YqC9RcW" target="_blank" rel="noreferrer"&gt;https://www.infoq.cn/article/1OkKmb_gEYNR3YqC9RcW&lt;/a&gt;&lt;/p&gt;&#10;&lt;p&gt;《自主研发、不断总结经验，美团搜索推荐机器学习平台》&#10;&lt;a href="https://cloud.tencent.com/developer/article/1357309" target="_blank" rel="noreferrer"&gt;https://cloud.tencent.com/developer/article/1357309&lt;/a&gt;&lt;/p&gt;&#10;&lt;p&gt;《推荐系统中模型训练及使用流程的标准化》&#10;&lt;a href="https://www.infoq.cn/article/2E6LCqb1GeqFRAjkkjX3" target="_blank" rel="noreferrer"&gt;https://www.infoq.cn/article/2E6LCqb1GeqFRAjkkjX3&lt;/a&gt;&lt;/p&gt;&#10;&lt;p&gt;numba&#10;&lt;a href="http://numba.pydata.org" target="_blank" rel="noreferrer"&gt;http://numba.pydata.org&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title>用 DAT 重实现 CppJieba 中文分词算法，降低 99% 内存消耗</title><link>https://byronhe.com/post/2019/11/25/cppjieba-darts-DAT-memory_optimize/</link><pubDate>Mon, 25 Nov 2019 00:00:00 +0000</pubDate><guid>https://byronhe.com/post/2019/11/25/cppjieba-darts-DAT-memory_optimize/</guid><description>&lt;h2 class="relative group"&gt;一，问题背景&#10; &lt;div id="一问题背景" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e4%b8%80%e9%97%ae%e9%a2%98%e8%83%8c%e6%99%af" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;中文分词应用比较广泛的开源算法，是 &lt;a href="https://github.com/fxsjy/jieba" target="_blank" rel="noreferrer"&gt;jieba 结巴分词&lt;/a&gt;，结巴分词较高性能的实现是 C++ 版本的 CppJieba :&#10;&lt;a href="https://github.com/yanyiwu/cppjieba" target="_blank" rel="noreferrer"&gt;https://github.com/yanyiwu/cppjieba&lt;/a&gt;&lt;/p&gt;&#10;&lt;p&gt;在实际使用 CppJieba 的过程中，我们发现 CppJieba 的内存占用比较高。&lt;/p&gt;&#10;&lt;p&gt;比如对一个 76W 词 大小 11MB 的词典 ，加载 2份 （比如为了支持平滑改动用户词典）就需要耗费 505MB内存。&lt;/p&gt;&#10;&lt;p&gt;这对一些多进程的后台服务，浪费大量内存，难以接受，因此这里希望削减内存耗费。&lt;/p&gt;&#10;&lt;p&gt;经过初步调查，确定改进方法，然后动手改造，&lt;strong&gt;最终把 505MB 缩减到了 4.7MB ，实现了 99% 内存降低&lt;/strong&gt;。&lt;/p&gt;&#10;&lt;p&gt;此处也有 issue 讨论 &lt;a href="https://github.com/yanyiwu/cppjieba/issues/3" target="_blank" rel="noreferrer"&gt;https://github.com/yanyiwu/cppjieba/issues/3&lt;/a&gt;&lt;/p&gt;&#10;&lt;p&gt;代码在 &lt;a href="https://github.com/byronhe/cppjieba" target="_blank" rel="noreferrer"&gt;https://github.com/byronhe/cppjieba&lt;/a&gt; 。&lt;/p&gt;</description><content:encoded>&lt;h2 class="relative group"&gt;一，问题背景&#10; &lt;div id="一问题背景" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e4%b8%80%e9%97%ae%e9%a2%98%e8%83%8c%e6%99%af" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;中文分词应用比较广泛的开源算法，是 &lt;a href="https://github.com/fxsjy/jieba" target="_blank" rel="noreferrer"&gt;jieba 结巴分词&lt;/a&gt;，结巴分词较高性能的实现是 C++ 版本的 CppJieba :&#10;&lt;a href="https://github.com/yanyiwu/cppjieba" target="_blank" rel="noreferrer"&gt;https://github.com/yanyiwu/cppjieba&lt;/a&gt;&lt;/p&gt;&#10;&lt;p&gt;在实际使用 CppJieba 的过程中，我们发现 CppJieba 的内存占用比较高。&lt;/p&gt;&#10;&lt;p&gt;比如对一个 76W 词 大小 11MB 的词典 ，加载 2份 （比如为了支持平滑改动用户词典）就需要耗费 505MB内存。&lt;/p&gt;&#10;&lt;p&gt;这对一些多进程的后台服务，浪费大量内存，难以接受，因此这里希望削减内存耗费。&lt;/p&gt;&#10;&lt;p&gt;经过初步调查，确定改进方法，然后动手改造，&lt;strong&gt;最终把 505MB 缩减到了 4.7MB ，实现了 99% 内存降低&lt;/strong&gt;。&lt;/p&gt;&#10;&lt;p&gt;此处也有 issue 讨论 &lt;a href="https://github.com/yanyiwu/cppjieba/issues/3" target="_blank" rel="noreferrer"&gt;https://github.com/yanyiwu/cppjieba/issues/3&lt;/a&gt;&lt;/p&gt;&#10;&lt;p&gt;代码在 &lt;a href="https://github.com/byronhe/cppjieba" target="_blank" rel="noreferrer"&gt;https://github.com/byronhe/cppjieba&lt;/a&gt; 。&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;二，实现过程&#10; &lt;div id="二实现过程" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e4%ba%8c%e5%ae%9e%e7%8e%b0%e8%bf%87%e7%a8%8b" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&#10;&lt;h3 class="relative group"&gt;二.1 查内存分布&#10; &lt;div id="二1--查内存分布" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e4%ba%8c1--%e6%9f%a5%e5%86%85%e5%ad%98%e5%88%86%e5%b8%83" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h3&gt;&#10;&lt;p&gt;第一步先用 jemalloc 的 memory profiler 工具查看内存耗费在哪里，&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;改一下 CppJieba 的 test/demo.cpp， 链接 jemalloc，编译成二进制 jieba_test&lt;/li&gt;&#10;&lt;li&gt;然后设置环境变量&#10;&lt;code&gt;export MALLOC_CONF=&amp;quot;prof:true,prof_prefix:mem_prof/mem_profile_je.out,lg_prof_interval:20,lg_prof_sample:20&amp;quot;&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;然后 mkdir mem_prof， 并运行测试程序&lt;/li&gt;&#10;&lt;li&gt;jeprof &amp;ndash;pdf ./jieba_test mem_prof/mem_profile_je.out.xxx.heap &amp;gt; mem_profile.pdf&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;打开 mem_profile.pdf ，就可以看到内存分布了&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;二.2 优化方案&#10; &lt;div id="二2-优化方案" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e4%ba%8c2-%e4%bc%98%e5%8c%96%e6%96%b9%e6%a1%88" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;显而易见，内存主要耗费在:&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Trie.hpp 中的 Trie 树构建&lt;/li&gt;&#10;&lt;li&gt;KeywordExtractor.hpp 加载 idf 词典文件。&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;因此方案:&lt;/p&gt;&#10;&#10;&lt;h3 class="relative group"&gt;1. Double Array Trie 代替 cppjieba::Trie&#10; &lt;div id="1-double-array-trie-代替--cppjiebatrie" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#1-double-array-trie-%e4%bb%a3%e6%9b%bf--cppjiebatrie" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h3&gt;&#10;&lt;p&gt;引入 Double Array Trie (简称 DAT ,https://github.com/s-yata/darts-clone) , 代替 Trie.hpp 中的简单内存 Trie，并把 darts 生成的 DAT 保存到文件中，在启动时，如果已经有和词典对应的 DAT ，直接 mmap() attach 上去，即可启动。&lt;/p&gt;&#10;&lt;p&gt;经过实测发现，75万词词典，dart-clone 生成的 DAT 文件，大小只有 24MB，而且可以 mmap 挂载，多进程共享。&lt;/p&gt;&#10;&#10;&lt;h3 class="relative group"&gt;2. KeywordExtractor&#10; &lt;div id="2-keywordextractor" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#2-keywordextractor" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h3&gt;&#10;&lt;p&gt;KeywordExtractor 是个不常用功能，直接改成支持传入空的 idfPath 和 stopWordPath, 此时不加载数据即可。&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;二.3 其他问题&#10; &lt;div id="二3-其他问题" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e4%ba%8c3-%e5%85%b6%e4%bb%96%e9%97%ae%e9%a2%98" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&#10;&lt;h3 class="relative group"&gt;1. 支持热更新，保证词典和DAT一致&#10; &lt;div id="1-支持热更新保证词典和dat一致" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#1-%e6%94%af%e6%8c%81%e7%83%ad%e6%9b%b4%e6%96%b0%e4%bf%9d%e8%af%81%e8%af%8d%e5%85%b8%e5%92%8cdat%e4%b8%80%e8%87%b4" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h3&gt;&#10;&lt;p&gt;这里一个问题是，词典可能热更新，那怎么知道 DAT 文件和当前词典的内容对应？&lt;/p&gt;&#10;&lt;p&gt;我的做法是，对 默认词典文件+自定义词典文件，用文件内容算 MD5，写入 DAT 文件头部，这样打开 DAT 文件发现 MD5 不一致，就知道 DAT文件过时了，即可重建 DAT 。&lt;/p&gt;&#10;&lt;p&gt;实测发现算 MD5 还是很快的，启动时间都在 1秒 左右。&lt;/p&gt;&#10;&#10;&lt;h3 class="relative group"&gt;2. 代码清理&#10; &lt;div id="2-代码清理" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#2-%e4%bb%a3%e7%a0%81%e6%b8%85%e7%90%86" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h3&gt;&#10;&lt;p&gt;另外，清理了一下代码，删掉了 Unicode.hpp 中的无用代码。&#10;清理了 FullSegment.hpp HMMSegment.hpp MixSegment.hpp MPSegment.hpp QuerySegment.hpp 等中的重复代码。&lt;/p&gt;&#10;&#10;&lt;h3 class="relative group"&gt;3. 不兼容改动&#10; &lt;div id="3-不兼容改动" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#3-%e4%b8%8d%e5%85%bc%e5%ae%b9%e6%94%b9%e5%8a%a8" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h3&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;由于 Double Array Trie 无法支持动态插入词，删除 InsertUserWord() 方法&lt;/li&gt;&#10;&lt;li&gt;FullSegment.hpp 中 maxId 的计算有 bug，做了 fix。&lt;/li&gt;&#10;&lt;li&gt;为了节省内存，改成允许传入空的 idfPath 和 stopWordPath 。&lt;/li&gt;&#10;&lt;li&gt;会生成 Double Array Trie 临时文件，临时文件名默认会自动生成，也可以传 &lt;code&gt;dict_cache_path&lt;/code&gt; 指定&lt;/li&gt;&#10;&lt;li&gt;改成自定义词典中重复的词，保留权重最大的。&lt;/li&gt;&#10;&lt;li&gt;删除 Unicode.hpp 中的无用代码&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;整体改造后，代码量比原来减少 100 多行。&lt;/p&gt;&#10;&lt;p&gt;上线后效果显著。&lt;/p&gt;&#10;&lt;p&gt;当内存降低到 2-3MB 的水平后，这意味着 75W 词这种规模的大词典，可以用在手机环境。&lt;/p&gt;&#10;&lt;p&gt;比如可以在 ios 或者 Android 上做 中文/英文的切词，&#10;这意味着可能在客户端实现体验相当良好的搜索引擎。&lt;/p&gt;&#10;&lt;p&gt;ios 上也有可用于中文的分词器 &lt;a href="https://developer.apple.com/documentation/corefoundation/cfstringtokenizer-rf8" target="_blank" rel="noreferrer"&gt;CFStringTokenizer&lt;/a&gt; ，但貌似不开源。&lt;/p&gt;</content:encoded></item><item><title>GB 规模语料上的高性能新词发现算法</title><link>https://byronhe.com/post/2019/09/18/newwords_discovery/</link><pubDate>Wed, 18 Sep 2019 00:00:00 +0000</pubDate><guid>https://byronhe.com/post/2019/09/18/newwords_discovery/</guid><description>&lt;p&gt;分词是中文搜索的重要环节，目前分词算法已经比较成熟，分词错误的主要是由于未登录词。&lt;/p&gt;&#10;&lt;p&gt;因此发现业务领域语料库中的新词，减少未登录词，对改善搜索引擎的用户体验有重要意义。&lt;/p&gt;&#10;&lt;p&gt;新词发现的一种常用算法，是 matrix67 大神 2012 年提出的 《互联网时代的社会语言学：基于SNS的文本数据挖掘》&#10;&lt;a href="https://www.matrix67.com/blog/archives/5044" target="_blank" rel="noreferrer"&gt;https://www.matrix67.com/blog/archives/5044&lt;/a&gt;&lt;/p&gt;&#10;&lt;p&gt;其主要思路，是统计语料中出现的所有 ngram 子字符串的凝固度，自由度，信息熵。&lt;/p&gt;&#10;&lt;p&gt;算法中需要统计所有 ngram 子字符串的 左熵右熵，实现该算法时，一般以子字符串为 key，用哈希表来存。&lt;/p&gt;&#10;&lt;p&gt;但随着语料库变大时，内存消耗变大，&lt;/p&gt;&#10;&lt;p&gt;比如之前的 python 版本实现，对 200MB 的语料，就需要约 30G 内存来存哈希表，&lt;/p&gt;&#10;&lt;p&gt;导致单机内存不足无法运行，而且对这样规模的语料库，算法需要跑一两天才能出结果。&lt;/p&gt;&#10;&lt;p&gt;这里我应用一些工程实现方面的技巧，&#10;把用哈希表统计左熵右熵的计算，拆分成多个子哈希表，分批计算，并利用多核并行，大幅度优化了算法的性能。&lt;/p&gt;</description><content:encoded>&lt;p&gt;分词是中文搜索的重要环节，目前分词算法已经比较成熟，分词错误的主要是由于未登录词。&lt;/p&gt;&#10;&lt;p&gt;因此发现业务领域语料库中的新词，减少未登录词，对改善搜索引擎的用户体验有重要意义。&lt;/p&gt;&#10;&lt;p&gt;新词发现的一种常用算法，是 matrix67 大神 2012 年提出的 《互联网时代的社会语言学：基于SNS的文本数据挖掘》&#10;&lt;a href="https://www.matrix67.com/blog/archives/5044" target="_blank" rel="noreferrer"&gt;https://www.matrix67.com/blog/archives/5044&lt;/a&gt;&lt;/p&gt;&#10;&lt;p&gt;其主要思路，是统计语料中出现的所有 ngram 子字符串的凝固度，自由度，信息熵。&lt;/p&gt;&#10;&lt;p&gt;算法中需要统计所有 ngram 子字符串的 左熵右熵，实现该算法时，一般以子字符串为 key，用哈希表来存。&lt;/p&gt;&#10;&lt;p&gt;但随着语料库变大时，内存消耗变大，&lt;/p&gt;&#10;&lt;p&gt;比如之前的 python 版本实现，对 200MB 的语料，就需要约 30G 内存来存哈希表，&lt;/p&gt;&#10;&lt;p&gt;导致单机内存不足无法运行，而且对这样规模的语料库，算法需要跑一两天才能出结果。&lt;/p&gt;&#10;&lt;p&gt;这里我应用一些工程实现方面的技巧，&#10;把用哈希表统计左熵右熵的计算，拆分成多个子哈希表，分批计算，并利用多核并行，大幅度优化了算法的性能。&lt;/p&gt;&#10;&lt;p&gt;最终实现了 GB 大小语料上的新词发现，并把运行时间缩减到了 30 分钟左右 。&lt;/p&gt;&#10;&lt;p&gt;&lt;a href="https://github.com/hankcs/HanLP/wiki/%E6%96%B0%E8%AF%8D%E8%AF%86%E5%88%AB" target="_blank" rel="noreferrer"&gt;https://github.com/hankcs/HanLP/wiki/%E6%96%B0%E8%AF%8D%E8%AF%86%E5%88%AB&lt;/a&gt;&lt;/p&gt;</content:encoded></item></channel></rss>