<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title></title>
    <description>Lin Ju&apos;s Blog</description>
    <link>http://www.soasme.com/</link>
    <atom:link href="http://www.soasme.com/feed/index.xml" rel="self" type="application/rss+xml" />
    
      <item>
        <title>多出来的睾丸</title>
        <description>&lt;p&gt;安妮拉着被子，转头看向正在面色煞白的丈夫。刚刚的一番云雨，似乎让丈夫感到精疲力尽，不，更确切地说像是晕乎乎的。她关切地问道，“本，你没事吧？”&lt;/p&gt;

&lt;p&gt;“本，我们需要谈谈。” 自从一个月前，本的身体开始异变，他辞掉了稳定的工作，还出了一个月的远门，说是去度假。&lt;/p&gt;

&lt;p&gt;他不可能有外遇。他只是心里多了些事情。然而，安妮不确切那是什么。&lt;/p&gt;

&lt;p&gt;本摇了摇头，抬了抬额头，似乎在让自己更清醒一些。&lt;/p&gt;

&lt;p&gt;“安妮，对的，对的。我们是需要谈谈。我知道你想问什么。我有点困，你让我冷静一分钟，好吗？” 本的眼神突然又变得十分温柔。&lt;/p&gt;

&lt;p&gt;本点了一根事后烟。&lt;/p&gt;

&lt;p&gt;安妮突然觉得很安心，他觉得本没事。&lt;/p&gt;

&lt;p&gt;“安妮，我想你要做好心理准备。”&lt;/p&gt;

&lt;p&gt;“什么准备，你说。”&lt;/p&gt;

&lt;p&gt;“今晚可能将是我们的最后一个晚上。”&lt;/p&gt;

&lt;p&gt;空气仿佛被壁炉里的火也烧的噼里啪啦。安妮突然觉得口渴，一阵耳鸣从左耳贯穿到右耳。&lt;/p&gt;

&lt;p&gt;“等等，你说的是什么意思。什么叫最后一个晚上，什么叫可能将是。”&lt;/p&gt;

&lt;p&gt;“但是，安妮，我想让你知道，我爱你，我爱你，我爱你“， 本的眼睛开始湿润，“你是我一生唯一的挚爱。”&lt;/p&gt;

&lt;p&gt;尽管困惑，安妮回应道，“我也是，如果可以，我想和你白头偕老，共度一生，就像我们的结婚誓言说的。”&lt;/p&gt;

&lt;p&gt;“我爱你，我很幸运，过了今晚，我们就真的共度一生了，虽然没有白头偕老。”&lt;/p&gt;

&lt;p&gt;两人拥抱良久。&lt;/p&gt;

&lt;p&gt;本松开了安妮的肩膀，开始了他的述说。&lt;/p&gt;

&lt;p&gt;“让我们从我的睾丸说起。”&lt;/p&gt;

&lt;p&gt;“对，你的那颗睾丸。”&lt;/p&gt;

&lt;p&gt;“你知道的，一个月前，我长出了第三颗睾丸，不偏不倚，就在阴囊的正中间。与此同时，我发现，我的记忆开始涌现无数其他人的生活细节。”&lt;/p&gt;

&lt;p&gt;“这也是你一个人出去散心的原因？”&lt;/p&gt;

&lt;p&gt;“是的，我在汽车旅店里呆了一整个月，不吃不喝，尝试搞清楚是怎么回事。这些人包括了我只在照片里见过的父母。”&lt;/p&gt;

&lt;p&gt;“天呐，这怎么可能？”&lt;/p&gt;

&lt;p&gt;“它就是发生了，这期间我从没有感受到饥饿。不仅如此，那些无数的记忆碎片慢慢的连了起来，我就这么安静地就躺在床上，像看肥皂剧一样看完了所有的情节，然后，我就睡着了。”&lt;/p&gt;

&lt;p&gt;“睡着了？”&lt;/p&gt;

&lt;p&gt;“对，我的直观感觉就像是睡着了。更确切地说，是我的人格沉睡了。在这之后，我的躯体应该是被另外的东西驱策着回家，直到刚才射精完成的一瞬间。”&lt;/p&gt;

&lt;p&gt;安妮惊讶地捂着嘴巴。“精神分裂？”&lt;/p&gt;

&lt;p&gt;“不是的。我的解释可能让你觉得匪夷所思。我想给你讲个故事，好帮你理解。”&lt;/p&gt;

&lt;p&gt;“阿鲁，是个无所事事的原始人。他的主要工作就是出去打猎，找吃的。有一天，他发现天上有一个火球，拖曳着细长的尾巴，直直的砸到地面。地震让他惊恐，也让他好奇。他忘记了饥肠辘辘，直奔那个火球掉落的地方。”&lt;/p&gt;

&lt;p&gt;“也就是陨石咯？”&lt;/p&gt;

&lt;p&gt;“对，也就是陨石。阿鲁等到周围冷却，凑近一看，陨石就像裂开的豆荚，里面藏着一颗豌豆大的小坚果。”&lt;/p&gt;

&lt;p&gt;“阿鲁的肚子适时的叫了，他啃了啃，发现能吃，便不假思索地吞了下去。就在陨坑里，不消一刻钟，他也经历了像我一样的情况，长出了第三颗睾丸。”&lt;/p&gt;

&lt;p&gt;“那你？”&lt;/p&gt;

&lt;p&gt;“我的第三颗睾丸当然不是这么长出来的啦。对于我，它就自己长出来了。回到阿鲁的故事，他确切地就在吃完那颗小坚果后长了出来，就像被蚊子叮了后起包那么快。而且不像我，他头脑简单，花了两分钟便完成了人格的切换。”&lt;/p&gt;

&lt;p&gt;“当他回到部落的时候，部落的人很快就发现了他的神奇之处。他有了第三颗睾丸，还可以不吃不喝。第二天晚上，众人便在篝火旁跳舞，拥立他为部落首领。”&lt;/p&gt;

&lt;p&gt;“阿霞是他最喜欢的姑娘。那天晚上，做为部落首领的他，行使了他的权利，将阿霞带到洞穴深处。”&lt;/p&gt;

&lt;p&gt;“然而，第二天，阿鲁便死了。他死在洞穴深处。”&lt;/p&gt;

&lt;p&gt;“故事的主角切换到阿霞。阿霞怀孕了，而且开始不与人说话。在诞下一个男孩后，阿霞便去世了。阿霞将她和阿鲁的儿子起名也叫阿鲁。让我们姑且叫他阿鲁二。阿鲁二在他长到大约有他父亲那么大的时候，也长出了第三颗睾丸。”&lt;/p&gt;

&lt;p&gt;安妮张开嘴巴，想说什么，但没有说。&lt;/p&gt;

&lt;p&gt;本点点头，继续说道，“对，你没有猜错，我就是阿鲁和阿霞的后代。确切地说，我是阿鲁和阿霞的大约五百多代后裔。具体多少代，我没有去数。这五百多代人，每一代都是男性。每位男性都在长出第三个睾丸后，沉睡了人格，并在之后的一次性交中，将自己的基因，连同自己的记忆传承给了下一代。性交过后，这位男性便会死去。与他性交的女性会怀孕，也会在性交后失去人格，接着生下男孩，然后死去。”&lt;/p&gt;

&lt;p&gt;“这，太不科学了！这怎么可能？”&lt;/p&gt;

&lt;p&gt;“这是可能的，安妮。我是个遗传学家，相信我。在汽车旅馆里的一个月，除了看这跨度一万年的肥皂剧，我还在研究这些东西，在我完全失去对身体的控制之前。我出门时带了个手提箱，里面有个快速基因测序器。我检测过我的基因序列。第三个睾丸会出现，是因为我的基因序列如此表达。在步入中年后不久，第三个睾丸被表达出来。”&lt;/p&gt;

&lt;p&gt;“另外，平常人有 95% 的基因序列是没有任何功能的‘垃圾’，不表达任何东西，然而我的不是。我的基因序列似乎编码着历代男性和女性大脑的关键信息。这些信息会在长出睾丸后慢慢加载进海马体。我不知道阿鲁当时吞下的到底是基督的圣水还是太上老君的金丹，这远远超出了我们的理解范畴。但我可以肯定的是，我的精子现在已经在你的体内，潜伏着，这数以亿计的精子比平常的精子都更长寿，能耗上一个月，然后伺机钻入卵子，完成受精。”&lt;/p&gt;

&lt;p&gt;“那我们为什么会死？”&lt;/p&gt;

&lt;p&gt;“我可能完成了繁衍的使命吧。人类会繁衍后代，然后老去。我们只是离死亡更近一些。其实在储备精液的过程中，我的端粒就已经在每个细胞中剥落了。”&lt;/p&gt;

&lt;p&gt;“虽然一直都想有个小孩，但没有想到是以这样的方式”，安妮望着天花板，壁炉火光摇摇曳曳，“我害怕。”&lt;/p&gt;

&lt;p&gt;本握住安妮的手，“我也是，安妮。”&lt;/p&gt;

&lt;p&gt;回忆与讨论交织，直到第一缕阳光晒到窗帘上。&lt;/p&gt;

&lt;p&gt;壁炉的灰烬还残留最后一点余温。阿鲁的后代回应慢慢弱了下去，最后没了声音。&lt;/p&gt;

&lt;p&gt;安妮松开与阿鲁相扣的手，起身拉开窗帘。&lt;/p&gt;

&lt;p&gt;她极目远眺，朝霞中间，有一颗巨大的陨石正在掉落。&lt;/p&gt;
</description>
        <pubDate>Fri, 18 Jun 2021 00:00:00 +0000</pubDate>
        <link>http://www.soasme.com/2021/06/18/the-extra-testicle</link>
        <guid isPermaLink="true">http://www.soasme.com/2021/06/18/the-extra-testicle</guid>
      </item>
    
      <item>
        <title>钗头凤</title>
        <description>&lt;p&gt;楼飞已经垂垂老矣，奄奄一息。&lt;/p&gt;

&lt;p&gt;春江水涨，浅草马蹄。&lt;/p&gt;

&lt;p&gt;仆从撩开帘子告知楼飞前方便是双江城地界，还望见有个凉亭，可下车休憩，再过十多里便能进城。&lt;/p&gt;

&lt;p&gt;楼飞示意知道了。何尝只是知道？&lt;/p&gt;

&lt;p&gt;下了车，步入凉亭，楼飞抚着石桌，看着远山。&lt;/p&gt;

&lt;p&gt;那年，相约之期已至，心仪的姑娘并没有出现。雪夜漫漫，这凉亭无人问津。一把凤钗孤零零地横在凉亭里的石桌上。&lt;/p&gt;

&lt;p&gt;楼飞知晓了姑娘的心意，既不肯托付终生，亦不愿当面直说，见簪知意。谁人，何时，放的这信物自是不重要的。&lt;/p&gt;

&lt;p&gt;他带走了那只凤钗，小心翼翼地放入包袱，在凉亭生了一堆火，过了一夜。次日便离开了。&lt;/p&gt;

&lt;p&gt;这一去，便是五十余年。庙堂高远，官场沉浮。他曾高居尚书，也曾远谪琼岛。然而，他一直都孤身一人，未曾婚娶。来来去去，伴他的箱里，总有待在最角落的那只凤钗。&lt;/p&gt;

&lt;p&gt;祖宗，后嗣，名声，世俗，党争，都与他无关。&lt;/p&gt;

&lt;p&gt;离开后的数年，他多次托人询问姑娘的下落，方知这姑娘被族人沉塘，未能赴约。&lt;/p&gt;

&lt;p&gt;那年，他走的决绝，却一生没有放下。&lt;/p&gt;

&lt;p&gt;仆从搀扶着楼飞，步入凉亭中。台阶总共三级，他却似走了一辈子。&lt;/p&gt;

&lt;p&gt;楼飞瘫坐在凉亭石椅上，原本想要落叶归根。他想着，这里，也是一个不错的离开人世的地方。&lt;/p&gt;

&lt;p&gt;他从胸口衣领中取出凤钗，出奇地望着。&lt;/p&gt;

&lt;p&gt;他吐纳着最后一口气，好似他的魂魄已经要迫不及待地离开他的躯体。&lt;/p&gt;

&lt;p&gt;他惊讶地发现，凤钗闪着幽光。&lt;/p&gt;

&lt;p&gt;一个十六七岁姑娘的鬼形现出。抱着楼飞。没过一会儿，她便越发变的透明，最后消失了。&lt;/p&gt;

&lt;p&gt;在消失的一瞬间，他笑着断了气。&lt;/p&gt;

&lt;p&gt;原来，姑娘的亡魂附在这钗上，与他共度了一生。&lt;/p&gt;
</description>
        <pubDate>Mon, 31 May 2021 00:00:00 +0000</pubDate>
        <link>http://www.soasme.com/2021/05/31/hairpin</link>
        <guid isPermaLink="true">http://www.soasme.com/2021/05/31/hairpin</guid>
      </item>
    
      <item>
        <title>Python PEG Grammar</title>
        <description>&lt;p&gt;The Python programming language has its own PEG parser since 3.10, which was derived from its EBNF notation in former versions. Here are some significant characteristics of its PEG parser:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Python PEG Parser is only used internally. The parser has no public C API.&lt;/li&gt;
  &lt;li&gt;The input grammar&lt;a href=&quot;https://github.com/python/cpython/blob/master/Grammar/python.gram&quot;&gt;0&lt;/a&gt; needs to be compiled to file &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;parser.c&lt;/code&gt;&lt;a href=&quot;https://github.com/python/cpython/blob/master/Parser/parser.c&quot;&gt;1&lt;/a&gt; first.&lt;/li&gt;
  &lt;li&gt;Python PEG grammar allows left-recursion. So &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sum = sum &apos;+&apos; term | sum &apos;-&apos; term | term&lt;/code&gt; is allowed and won’t end up with stack exhausted.&lt;/li&gt;
  &lt;li&gt;Python PEG grammar don’t have CST to AST conversion. The grammar rules are compiled from source code to AST directly given an “action” block followed by the definitions. The action blocks are in essence raw C code that will be copied into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;parser.c&lt;/code&gt;&lt;a href=&quot;https://github.com/python/cpython/blob/master/Parser/parser.c&quot;&gt;1&lt;/a&gt; in verbatim.
For example, if there is an exact keyword match for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pass&lt;/code&gt;, an AST node for pass will be created.
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;simple_stmt:
  ... (truncated)
  | &apos;pass&apos; { _PyAST_Pass(EXTRA) }
  ... (truncated)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;Python provides ASDL description to aid the AST generation. ASDL file (Python.asdl&lt;a href=&quot;https://github.com/python/cpython/blob/master/Parser/Python.asdl&quot;&gt;2&lt;/a&gt;) is parsed to a “meta-AST” by asdl_c.py, then emit C statements into a “Include/internal/pycore_ast.h”&lt;a href=&quot;https://github.com/python/cpython/blob/master/Include/internal/pycore_ast.h&quot;&gt;3&lt;/a&gt; file and “Python/Python-ast.c”&lt;a href=&quot;https://github.com/python/cpython/blob/master/Python/Python-ast.c&quot;&gt;4&lt;/a&gt;. The functions in “Python/Python-ast.c”&lt;a href=&quot;https://github.com/python/cpython/blob/master/Python/Python-ast.c&quot;&gt;4&lt;/a&gt; happens to be those action code in PEG grammar.&lt;/li&gt;
&lt;/ol&gt;

</description>
        <pubDate>Mon, 19 Apr 2021 00:00:00 +0000</pubDate>
        <link>http://www.soasme.com/2021/04/19/python-peg-grammar</link>
        <guid isPermaLink="true">http://www.soasme.com/2021/04/19/python-peg-grammar</guid>
      </item>
    
      <item>
        <title>Nim-markdown 性能优化</title>
        <description>&lt;p&gt;2020 年的最后一天，发现来的不是 2021 年，而是一个 GitHub Issue &lt;a href=&quot;https://github.com/soasme/nim-markdown/issues/48&quot;&gt;https://github.com/soasme/nim-markdown/issues/48&lt;/a&gt;
一个 Nim-Markdown 的用户报告性能问题，100kb 的文档，库解析要 4 秒。&lt;/p&gt;

&lt;p&gt;在此之前，我从来没有考虑过性能优化，能把 markdown 几百个 spec 跑通已经是件很恶心的事情了。
但作为维护者，我还是承诺在 2021 年会给出一个修复。
此后两月，每每想要解决，总是改一行挂几十个 case，真的是怕了怕了！
我都撸完 Peppa PEG 一整个项目了，这个 Issue 就一直挂在那里。&lt;/p&gt;

&lt;p&gt;眼看三月就要入秋了，一个小伙子发来了一个 PR &lt;a href=&quot;https://github.com/soasme/nim-markdown/pull/50&quot;&gt;https://github.com/soasme/nim-markdown/pull/50&lt;/a&gt; 带来了 25% 的性能提升。
哎，心想，该来的还是要来。
所以，最近别的项目都停更了，死磕这个性能问题。&lt;/p&gt;

&lt;p&gt;这里稍微聊 Nimlang 里面做性能优化一定要用的标准库 nimprof，如下&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;import nimprof
enableProfiling()
discard markdown(stdin.readAll)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;编译时，带上 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--profiler:on&lt;/code&gt; 选项即可。运行时，程序会产生一个 profile_results.txt 文件，里面的内容会从高到低排出最多被运行的指令或函数调用。&lt;/p&gt;

&lt;p&gt;再稍微聊聊为什么会有性能问题。排查下来，最大的问题是字符串的 GC 开销：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;let a = &quot;long long long string ….&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;做一次 slice: a[i .. j] 其实底下 malloc/free 都很吃时间的。Nim 中字符串 slicing 可能还涉及一些别的操作，如果仅仅使用 substr(a, i, j) 都会比 a[i, j] 快。事实上，如果你去看 nim 标准库的源码，strutils, parseutils 这些实现也都偏向使用 substr(a, i, j) 来做 slicing。&lt;/p&gt;

&lt;p&gt;另外 a[i, j].matchLen(pattern) 替换成 a.matchLen(pattern, i, j) 也有立竿见影的效果 - 不需要额外的 malloc/free 开销。&lt;/p&gt;

&lt;p&gt;同理，a.splitLines 在大文档的场景下也非常吃亏，不如去拿到行首和行尾的 index，有需要再去做 substr。&lt;/p&gt;

&lt;p&gt;就这么花了半个月，每晚就这么 0.1s 0.1s 的优化，终于从 4秒优化到大约 100+ms 了。有点体会到钓鱼的人的乐趣。。。&lt;/p&gt;

&lt;p&gt;哎，就是也没人给我 bounty，&lt;a href=&quot;https://nee.lv/2021/02/28/How-I-cut-GTA-Online-loading-times-by-70/&quot;&gt;https://nee.lv/2021/02/28/How-I-cut-GTA-Online-loading-times-by-70/&lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Fri, 19 Mar 2021 00:00:00 +0000</pubDate>
        <link>http://www.soasme.com/2021/03/19/markdown-performance-optimization</link>
        <guid isPermaLink="true">http://www.soasme.com/2021/03/19/markdown-performance-optimization</guid>
      </item>
    
      <item>
        <title>Callgrind</title>
        <description>&lt;p&gt;使用 callgrind 报告输出函数指令的调用占比，可以快速定位和修复 C 程序性能瓶颈。以下是使用 callgrind 的一点不重要的小笔记。&lt;/p&gt;

&lt;p&gt;我发现，使用 Peppa PEG 编写的 JSON 解析器运行非常的慢。构造的测试用例是深度嵌套的 JSON Array 数据结构：&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[[[[[…]]]]]&lt;/code&gt;, 左边一千个 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[&lt;/code&gt;, 右边也一千个 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;]&lt;/code&gt;。写好一个程序后，使用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;valgrind —tool=callgrind&lt;/code&gt; 生成一个注解文件 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;callgrind.out.$PID&lt;/code&gt;。再使用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;callgrind_annotate&lt;/code&gt; 即可将注解文件合并生成最终报告。示例：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ gcc -g  peppapeg.c examples/json.c &amp;amp;&amp;amp; valgrind --tool=callgrind ./a.out
$ callgrind_annotate callgrind.out.63642
1,265,470,759 (100.0%)  PROGRAM TOTALS
--------------------------------------------------------------------------------
Ir                    file:function
--------------------------------------------------------------------------------
706,187,168 (55.80%)  peppapeg.c:P4_NeedLoosen [/app/a.out]
320,620,450 (25.34%)  peppapeg.c:P4_IsTight [/app/a.out]
170,320,160 (13.46%)  peppapeg.c:P4_IsScoped [/app/a.out]
 25,020,000 ( 1.98%)  peppapeg.c:P4_NeedSquash [/app/a.out]
 10,000,000 ( 0.79%)  peppapeg.c:P4_IsSquashed [/app/a.out]
  4,617,160 ( 0.36%)  ???:_int_free [/usr/lib64/libc-2.28.so]
  3,382,226 ( 0.27%)  ???:malloc [/usr/lib64/ld-2.28.so]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这个报告把问题报告的非常清楚，程序整整有 94% 时间浪费在这几个 P4_ 的调用上。如果可以优化掉它们，就能带来 10 倍的性能提升。&lt;/p&gt;

&lt;p&gt;具体修复过程就不赘述，无外乎空间换时间和减少调用开销。下面的 Issue 关联了 三 个 PRs 用于改善性能。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/soasme/PeppaPEG/issues/15&quot;&gt;https://github.com/soasme/PeppaPEG/issues/15&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;经过修复后，调用量直降了两个数量级。&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;33,294,688 (100.0%)  PROGRAM TOTALS
--------------------------------------------------------------------------------
Ir                  file:function
--------------------------------------------------------------------------------
5,259,163 (15.80%)  ???:_int_free [/usr/lib64/libc-2.28.so]
3,846,555 (11.55%)  ???:malloc [/usr/lib64/ld-2.28.so]
2,595,108 ( 7.79%)  peppapeg.c:P4_Match&apos;2 [/app/a.out]
2,078,669 ( 6.24%)  ???:free [/usr/lib64/ld-2.28.so]
1,841,351 ( 5.53%)  peppapeg.c:P4_MatchLiteral [/app/a.out]
1,805,004 ( 5.42%)  ???:__strlen_avx2 [/usr/lib64/libc-2.28.so]
1,705,988 ( 5.12%)  peppapeg.c:P4_MatchChoice&apos;2 [/app/a.out]
1,518,598 ( 4.56%)  peppapeg.c:P4_Expression_dispatch&apos;2 [/app/a.out]
1,260,936 ( 3.79%)  ???:strdup [/usr/lib64/ld-2.28.so]
1,174,097 ( 3.53%)  peppapeg.c:P4_MatchRepeat [/app/a.out]
1,092,247 ( 3.28%)  peppapeg.c:P4_RaiseError [/app/a.out]
1,003,802 ( 3.01%)  ???:__memcpy_avx_unaligned_erms [/usr/lib64/libc-2.28.so]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在修复后重新输出的报告中，所有的耗时调用均被消除，程序最大头的消耗落在了 malloc/free，这完全符合身为解析器的最大要务：malloc/free 语法树 Token。事实上，这个千层饼 JSON 的运行时间也从 0.15 秒降到了 0.012 秒，我们也确实感知到了 10 倍的性能提升。&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ time ./a.out
real	0m0.012s
user	0m0.009s
sys	0m0.002s
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;看了下其他 JSON 解析器的使用，Python 的也不多说，没改限制的情况下，调用栈被打爆了。Nim 首次运行大概需要 0.9 秒，JIT 后一直稳定在 0.018 左右。&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ cat testjson.nim
import json, strutils
let s = &quot;[&quot;.repeat(1000) &amp;amp; &quot;]&quot;.repeat(1000)
discard parseJson(s)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;当然啦，跟 cJSON，RapidJSON 等专为 JSON 优化的解析器相比，自然是比不过的（大概在 0.04s 左右）。我觉得性能调优到这里已经可以算有阶段性成果了。&lt;/p&gt;

&lt;p&gt;下一篇，我打算看看 valgrind massif 的用法，它看起来可以报告程序对 Heap 的使用。&lt;/p&gt;

&lt;p&gt;最后，欢迎关注 我的新项目 Peppa PEG，an untra lightweight PEG parser in ANSI C.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/soasme/PeppaPEG&quot;&gt;https://github.com/soasme/PeppaPEG&lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Thu, 18 Feb 2021 00:00:00 +0000</pubDate>
        <link>http://www.soasme.com/2021/02/18/callgrind</link>
        <guid isPermaLink="true">http://www.soasme.com/2021/02/18/callgrind</guid>
      </item>
    
      <item>
        <title>Valgrind</title>
        <description>&lt;p&gt;很早以前就听过 Valgrind，在看各种库的测试代码的时候总能看到这个词，但一直没有机会使用到。今天在为 Peppa PEG 写增强测试用例的时候，感受到了它的便捷。&lt;/p&gt;

&lt;p&gt;Valgrind 可以检测内存使用异常，最常见的就是内存泄漏啦（malloc 没有对应的 free）。&lt;/p&gt;

&lt;p&gt;它的使用非常简单，编出可执行文件后，&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;valgrind ./program
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在整合进 Peppa PEG 的测试用例时，我使用了如下这些选项：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;valgrind --trace-children=yes --leak-check=full —-error-exitcode=1 -s ./test_xxx
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这套选项可以在所有测试用例功能测试 pass 的情况下，额外检查是否有内存泄漏，果不其然，是有两三个小地方的( &lt;a href=&quot;https://github.com/soasme/PeppaPEG/commit/104f356fc1cb04a4df34621ba9f4563a98fd3665&quot;&gt;104f356&lt;/a&gt; &lt;a href=&quot;https://github.com/soasme/PeppaPEG/commit/3e751ebf5f687998e80509d6e44ace207a659731&quot;&gt;3e751ebf&lt;/a&gt; )。它甚至查到了一个变量未初始化 &lt;a href=&quot;https://github.com/soasme/PeppaPEG/commit/e75024b2c8c30326806e3935c54920c69c60dc13&quot;&gt;e75024b&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;CMake ctest 在混合使用 valgrind 的时候，真是程序美话不多，没事儿啥也不报，测试结果报告干干净净，有错的时候各种信息弹出来帮助分析定位内存异常。&lt;/p&gt;

&lt;p&gt;它的错误报告非常贴心，在 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gcc -g&lt;/code&gt; 开启的情况下，valgrind 的错误报告也包含了足够多的信息用于帮助修复。具体不多说，我看的是这篇小短文：&lt;a href=&quot;https://epitech-2022-technical-documentation.readthedocs.io/en/latest/valgrind.html&quot;&gt;https://epitech-2022-technical-documentation.readthedocs.io/en/latest/valgrind.html&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;嗯嗯，还在学习中，看到 &lt;a href=&quot;https://github.com/json-c/json-c/blob/json-c-0.15/bench/jc-bench.sh#L234&quot;&gt;json-c&lt;/a&gt; 还使用 valgrind 做 benchmark。给 Peppa PEG 做完 benchmark 后再发开箱体验第二弹。&lt;/p&gt;

</description>
        <pubDate>Tue, 16 Feb 2021 00:00:00 +0000</pubDate>
        <link>http://www.soasme.com/2021/02/16/valgrind</link>
        <guid isPermaLink="true">http://www.soasme.com/2021/02/16/valgrind</guid>
      </item>
    
      <item>
        <title>Peppa PEG 发布啦!</title>
        <description>&lt;p&gt;大嘎猴啊！在磨皮大半年，奋战三天三夜，燃烧自己的青春，照亮人类的黑暗后，我的 Peppa PEG 项目终于发布了。这么肉乎乎的名字，听了真是顺口啊！比以前那个没&lt;a href=&quot;https://www.soasme.com/2020/06/28/peg-i&quot;&gt;脸&lt;/a&gt;没皮的名字好听多了。&lt;a href=&quot;https://github.com/soasme/PeppaPEG&quot;&gt;https://github.com/soasme/PeppaPEG&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;昨晚稍微写了个 INI Parser，觉得它真的是太不好用了呢。因为错误报告还没搞完，程序崩溃的时候我也选择了崩溃。但是，又但是，它毕竟是个好东西啊，愣是天王老子，那也不能随随便便花半个小时手撸一个 Parser 的，对伐？昨晚，崩溃后，我选择了冷静一下，就把它写完了。嘿，也就半小时。&lt;/p&gt;

&lt;p&gt;Peppa PEG 在语义上做到了 Pest 项目的 80% 吧，我觉得是个不错的开始。接下来一阵子，我会好好做人，继续更新，&lt;del&gt;以儆效尤&lt;/del&gt; 以饷各位！&lt;/p&gt;
</description>
        <pubDate>Wed, 10 Feb 2021 00:00:00 +0000</pubDate>
        <link>http://www.soasme.com/2021/02/10/peppa-peg-is-released</link>
        <guid isPermaLink="true">http://www.soasme.com/2021/02/10/peppa-peg-is-released</guid>
      </item>
    
      <item>
        <title>Mt Cook</title>
        <description>&lt;p&gt;新年伊始，选择了休几天去南岛玩。这次的目的地挑了很久，就定在 Mt Cook。&lt;/p&gt;

&lt;p&gt;在惠灵顿机场发现不慎将眼镜弄丢了，可真是吓尿了，没眼镜怎么开车呀！一秒冷静，觉得会在刚去过的卫生间里，百米冲刺过去，果然发现了遗失的眼镜。&lt;/p&gt;

&lt;p&gt;从 Wellington 出发，坐飞机到 Christchurch，屁股没捂热就到了；再开几个小时车去 White Horse Hill 的旅店。途经 Fairlie Bakehouse，买了远近驰名的 Pie，果然是一级棒，是我吃过的最好吃的 Pie。&lt;/p&gt;

&lt;p&gt;Motel 的家伙什配的非常齐全，可以煎煮，可以冷藏食物，拉开窗帘就是雪山。第一天的时候，由于大雾，一度怀疑是不是来对地方了，雪山在哪里？！接下来几天舒舒服服地走了步道和爬山，换不同的角度看冰川湖。这次的装备挑了很久，硬底鞋，防风防雨 速干贴身T恤内裤+Merino 秋衣，完全不怕风吹雨打地进山徒步。&lt;/p&gt;

&lt;p&gt;好几个晚上里面，只有一个晚上是适合观星的，虽然那个晚上也有云挡着一些星星，但不妨碍整个银河的呈现。这次带上了双筒望远镜，往上一看，霍，好家伙，发现了好多没发现的星星，就算是在猎户座腰带三星那么小的范围里也能看到上百颗星星，完全不需要照相机长时间曝光也能看到的感觉真是太棒了。正说着，“不知道能不能看到流”，刷，一个流星飞过，我大叫一声，流星！过了一会儿，“遗憾，要是两个人都能一起看到就好了”，刷，一个流星又飞过，这次两人都看到啦！&lt;/p&gt;

&lt;p&gt;下次想从 Picton 坐轮渡去南岛，走西线，从 Arthur’s Pass 圈回 Kaikoura。期待。&lt;/p&gt;
</description>
        <pubDate>Sun, 10 Jan 2021 00:00:00 +0000</pubDate>
        <link>http://www.soasme.com/2021/01/10/mt-cook</link>
        <guid isPermaLink="true">http://www.soasme.com/2021/01/10/mt-cook</guid>
      </item>
    
      <item>
        <title>2020 年终总结</title>
        <description>&lt;p&gt;很难将 2020 年与 Covid-19 脱钩，所以先说说这方面的影响。对于生活而言，简直不要太好了，天天跟鹅腻在家里，一整年的生活就如同 2016 年准备移民前的那两个月。全年只去过办公室不到半个月时间。一月份后就去过办公室取个 HHKB 回家而已。Auckland 有两次 Lockdown，但其实没 Lockdown 也没怎么出去，除了饭后散步，去超市，徒步。NZ 的疫情控制应该算全球顶级了，运气真好。&lt;/p&gt;

&lt;p&gt;2020 年占去最多时间的事情是买房。NZ 的房价在过去的几年可以说是涨疯了，我们有买房的意愿，如果再不买就很难跟上了。经过调研，我们发现 Auckland 的房价已经超出了预算，所以打算搬去 Wellington，一个我们曾经生活过，觉得很好，房价也可以接受的地方。Dunedin 也超喜欢，但是太干燥了。 一月份，Boss 批准了四月份后可以搬去 Wellington 居家办公。不巧的是，四月份碰上了 Auckland 第一波 Lockdown，直到七月整个房市都几乎处于冻结的状态。我们在七月底横穿 Wellington ，才算第一次真正意义上现场看了几套房，不过如预期般的，不可能看一两套房子就入。不过回 Auckland 后，马上就出现了我们觉得可以接受的房子，就马上把开了 Offer。卖家隔天中午就接了。手续过得很快，律师也都在 Zoom 上干活帮忙把合同什么的一起搞了。八月底就入住了。回顾过程，买房是个非常耗精力的事情，我们的主要精力开销是在网络上收集地理，治安，交通，朝向，户型等等信息，毕竟异地买房+旅行感染风险都使现场看房难度加大，不过也因为这个原因，我觉得看房基本是很高效的，十几分钟就能评估新出的房源是否合适我们。九月之后，就是不停地跑商场，置办家具，到现在算勉强布置地差不多了。&lt;/p&gt;

&lt;p&gt;2020 年还有两件不大不小的事情 - 永居签证和驾照到手。两年前的 PR 到期终于在七月份换成了无限制的 Permanent Resident Visa。另外，由于一直使用国内驾照+翻译件的方式开车上路，今年终于不行了 - 由于 Lockdown，不能出境刷新使用一年的限制。在十月份在 Wellington NZTA 考了两趟就过了（第一趟没把 Hazardous 说全）。可以说在 NZ 生活的证件很齐备啦。&lt;/p&gt;

&lt;p&gt;今年没怎么学习，本来也没设什么目标。本来还打算用 OKR 的方式来管理，后来发现真是扯淡，上班已经够了，下班只想当头咸鱼。&lt;/p&gt;

&lt;p&gt;娱乐：买了没怎么玩的健身环，吃灰中。动森我玩了一段时间，到后来鹅玩了很久，再后来就没有后来了，太吃时间了。今年就看完了一本书，海伯利安，无比精彩！&lt;/p&gt;

&lt;p&gt;2021 年想做的事情：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;希望能把手头的解析器项目 (jupeg) 正式发布。&lt;/li&gt;
  &lt;li&gt;学画画&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 30 Dec 2020 00:00:00 +0000</pubDate>
        <link>http://www.soasme.com/2020/12/30/covid</link>
        <guid isPermaLink="true">http://www.soasme.com/2020/12/30/covid</guid>
      </item>
    
      <item>
        <title>写在搬家两月后</title>
        <description>&lt;p&gt;在 Auckland 的出租屋里的东西不是卖掉就是扔掉。如果放在草坪上，大约一个小时后东西就回神奇的消失掉啦～ 在出租屋里最后一天因为桌子卖了，所以要在箱子上办公，腰酸背痛，花了整整一个月才恢复。零零碎碎整理到半夜，以为早晨可以大清早就能出发，结果五点起来，愣是还整理到了八点才出发。&lt;/p&gt;

&lt;p&gt;Auckland Lockdown 一再延期，所以出城的时候要带上 Sales and Purchase Agreement。不过流程非常快，车大概就排了几分钟，就轮到我们。一个兵哥哥看了下我们的材料很快就放行了。从 Auckland 到 Wellington，车程是 10 小时，算上休息的时间。看房时候花两天开的路，这一天开完，其实也还好。一路上感受到了 Hanmilton 的雾气，Taupo 的湖景，躲在荒漠腹地的 Police，山野，平原，森林，田地，牧场，总而言之，非常的 New Zealand。&lt;/p&gt;

&lt;p&gt;因为之前看房的时候把这一片都转了一转，所以开进来时候的熟悉感还没消失。唯一新鲜的是房子本身。喜欢这儿的安静。这个 Suburb 算是非常新的了，房子们都像是刚盖的，跟以前住的地方很不一样。山头上还在不停的攒房子。&lt;/p&gt;

&lt;p&gt;此后两月，慢慢购置了很多东西，比如办公系列，厨房系列，客厅被排到了最后到现在也还没搞起来。忙活着忙活着，俩月要过去了。新家落户到现在其实还是空荡荡的。这次还是很感谢 Trello，所有任务拆解，完成，看得清清楚楚。&lt;/p&gt;

&lt;p&gt;这中间还由于疫情不敢出国了，导致我的海外驾照无法使用了。其实已经过期一年了，因为 Exemption，可以延期到十月份。这次实在不能再拖了，便转了香港驾照，寄回来，再转 NZ 驾照。又因为不满持有两年，所以路考了一下。第一次路考因为 Harzard Response 没说挂了，第二次再考就简单多了。把 &lt;a href=&quot;https://www.nzta.govt.nz/assets/Driver-Licences/docs/full-test-guide.pdf&quot;&gt;这个&lt;/a&gt; 看了好好练习下就能过了。&lt;/p&gt;

&lt;p&gt;结尾附一些看房时候的笔记，权当看个意思。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.waternz.org.nz/Attachment?Action=Download&amp;amp;Attachment_id=779&quot;&gt;Overland Flow Path&lt;/a&gt; 是下雨天雨水的流动方向。我们因为有一间房子的 LIM report 上提到这点，放弃了那间房子。一旦雨水侵入屋子，会比较麻烦。有些房子的 Land 会有约束，不可以改变这条 Path，会更麻烦。&lt;/li&gt;
  &lt;li&gt;追踪了半年房子，看过的房子都记着，过一阵子看他们的售价，感受市场的热度。要不是因为疫情把房价上涨停一停，可能真的买不到想要的房子了。&lt;/li&gt;
  &lt;li&gt;所有 Plaster 的房子都不考虑。看了几间，Plaster 或者砖都是直接 Mount 在 Wood Frame 上，没有 Cavity System。&lt;/li&gt;
  &lt;li&gt;因为异地买房，研究过 Buyers Agent ，有点贵。最后选择远程看房，反正视频看跟现场看，我自己觉得看不太出来什么。&lt;/li&gt;
  &lt;li&gt;在 NZ，南向的房子不要。太阴了。特别还是在山头上的。&lt;/li&gt;
  &lt;li&gt;任何买房有关的细节，看 &lt;a href=&quot;https://www.settled.govt.nz/&quot;&gt;settled.co.nz&lt;/a&gt;, 还不够的话问朋友或者中介。&lt;/li&gt;
  &lt;li&gt;关注房子的销售历史，有的房子每次交易非常快，一看就是有坑的。结合 Google Search，看老照片，有的房子隔年买了翻新就拿来卖。&lt;/li&gt;
  &lt;li&gt;我们在美国的资产，是通过 xe.com 转到 NZ 来的。&lt;/li&gt;
  &lt;li&gt;同样是 Greater Wellington 地区，Wellington 的地税远低于 Porirua。&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 30 Oct 2020 00:00:00 +0000</pubDate>
        <link>http://www.soasme.com/2020/10/30/after-movein</link>
        <guid isPermaLink="true">http://www.soasme.com/2020/10/30/after-movein</guid>
      </item>
    
  </channel>
</rss>
