paredit 很容易造成不平衡的情况,比如复制内容里面不平衡。
但这不是重点,我想知道我写的这个代码怎么能有更好的性能。而不是 paredit 和 parinfer 的关系、区别、优劣和必要性之类的。
paredit 很容易造成不平衡的情况,比如复制内容里面不平衡。
但这不是重点,我想知道我写的这个代码怎么能有更好的性能。而不是 paredit 和 parinfer 的关系、区别、优劣和必要性之类的。
setq 1+不写成incf吗?
代码看起来很low-level了,不知道还有什么优化空间。
buffer有必要从头parse到尾?
函数调用的开销?改写成inline?
谢谢,我不知道有incf这个函数。
从头 parse 到尾是为了解决没有关闭的字符串之类的问题。就算取一段来处理,我也觉得要保证在 1000 行这个数量上性能比较好才行。
说一个比较琐碎的,pcase
和 cl-case
比较数字用的是eql
,这个函数没有自己的byte op,性能不是最快的。可以用 =
(确定lhs和rhs都是数字)或者eq
(确定要比较一个long int)来做数字比较
(defmacro my/number-case (form &rest clauses)
(declare (indent 1) (debug cl-case))
(macroexp-let2 macroexp-copyable-p form form
`(cond ,@(mapcar (lambda (c)
(pcase-exhaustive c
(`(,head . ,handlers)
(if (memq head '(t otherwise))
`(t ,@handlers)
`((= ,head ,form)
,@handlers)))))
clauses))))
(my/number-case 1
(1 (+ 1 2))
(3 4))
谢谢,但是不知道为什么换了这个方式之后反而更慢了。差30%左右的样子。
性能和 cond 里面都用 = 是一样,不知道为什么 cl-case 反而还快。
但我明白了确实一些细微的地方就可以有比较大的影响。
那可以试试用eq,eq是比=快的。是我疏忽了,因为=要分别判断 浮点,整数,大整这几个不同的数字类型。eq就是比较指针。另外一方面,你有没有把macro编译之后再做bench?macro展开也是需要时间的。
(defmacro my/number-case (form &rest clauses)
(declare (indent 1) (debug cl-case))
(macroexp-let2 macroexp-copyable-p form form
`(cond ,@(mapcar (lambda (c)
(pcase-exhaustive c
(`(,head . ,handlers)
(if (memq head '(t otherwise))
`(t ,@handlers)
`((eq ,head ,form)
,@handlers)))))
clauses))))
(my/number-case 1
(1 (+ 1 2))
(3 4))
这样的话似乎和 cl-case 的性能是一样的,可能这个地方的影响不是很大。 虽然代码里面有个很长的 case。
你是什么环境?我在Archlinux Emacs 27.0.90里,用原编译后的代码(无更改)在 flycheck.el
直接开parinfer-bench
,耗时3.288438ms
flycheck 共11521行代码
ArchLinux Emacs 27.0.90 是不是你的电脑性能太好了?
你试的 flycheck.el?
有可能是中间遇到了对于 parinfer 来说的错误,停止 parse 了。 我试下
是的,有 parse 中的错误。在我这大约 10ms
原来一大片下划线是parinfer标错啊,我还以为flycheck坏了……不过就算标错我也只用了2ms,这样我觉得可能是你GC阈值太低影响性能了。
(setq gc-cons-threshold (* 20 1024 1024))
是的,遇到错误就会停下来了。我决定先放一放,等有想法了再继续。
也许能够获得一个范围是最好的办法,不过这个范围很难算。
这个函数如果用的太多是很慢的,因为他的原理是跳到point-min一行行数……不过既然你profile了没有发现,应该不是大头。
GC很多会不会是你overlay太多了?太多的话可以不用以后立即删掉overlay,而不是等着GC。
是的,初始化的时候一共只用一次。
我感觉是push和pop太频繁了,初步的一个想法就是不要一个字符一个字符的处理,看在哪些情况下可以跳过一些内容。我改了 whitespace 的逻辑,在缩进那段区域直接用 back-to-indentation 跳过去,但是好像效果不是很好。
在考虑遇到 symbol 的时候直接跳过去。
代码上好像没啥问题,再优化估计要靠算法了。
感觉 elisp 在性能上还是和 js 有比较大的差距。如果按照 js 的库一比一的 port 的话,性能惨不忍睹。js 在字符串处理上性能相当好。
elisp 里面尽量利用 buffer 里面的内容也还是差一大截。
elisp性能和Python(CPython implementation)大概同一级
按理说字符串上大家都是用utf-8,应该差不多。主要还是运行速度的差距。
Emacs 26.1 以及之前的 pcase
用的是 eq
,不知道后来怎么就改成 eql
了,好像 NEWS 也没有解释
~ $ emacs-26.1 -Q --batch --eval "(print (macroexpand '(pcase 1 (1 t))))"
(if (eq 1 1) (progn t) nil)
~ $ emacs -Q --batch --eval "(print (macroexpand '(pcase 1 (1 t))))"
(if (eql 1 1) (progn t) nil)
parinfer 原本的逻辑是一个大字符串来处理的,在 emacs 里面如果把 buffer 里的字符串拿出来,好像做什么性能都不是太好。也可能我写的不好,但是那样比我现在这样的方式要差至少几十倍。
在学Elisp, 想请大神帮忙解释一下 (declare (indent 1) (debug cl-case))
里面(debug cl-case)
怎么解释, 有什么用处? 看文档在第18章完全不懂 。