Snails超级快的模糊搜索框架

像vscode

@manateelazycat Hello, 我受到 snails 的启发,造了一个类似 snails 的 vim 插件 GitHub - liuchengxu/vim-clap: Modern performant fuzzy picker for Vim and NeoVim 。但是有一个问题,即使使用异步搜索,但是当遇到耗 CPU 的任务时,输入窗口的响应还是会有延迟。比如使用 fd 在 home 目录下面搜索文件时,有超过上百万个文件,在搜索的几秒内,CPU 爆满,此时再输入就会有延迟。有什么能够在CPU密集时依然可以快速响应的建议吗?fd, rg 默认都是使用所有线程。

1 个赞

第一多进程,第二输入字符改变后要杀掉来的进程

具体细节可以看我写的文章:Snails架构设计

这两点的话,我应该都有做到。每个耗时任务都用异步作业建一个新的进程进行。每次要新建一个作业时,如果当前还在进行中的异步作业,先杀死然后再开始新的一个。渲染的话,也是延迟渲染,只先渲染3倍的窗口大小的数据,其他数据全部 cache,渲染的数据量应该很小,毕竟窗口一般就就是几十行数据,用户向下滚动到最后一行时再从cache中加载。设计上应该没有什么大的缺陷,问题就是如果当前CPU爆满,即使从编辑器中发送杀死作业的命令,也不会有即时的响应。感觉这个是个坑…, 限制 fd 和 rg 的多线程数量,可能是个workaround, 但是我觉得并不好…

调命令行参数?

这个一来是有同样的问题,就是CPU占用接近100%的时候,此时发送任何指令其实都不会有即时的相应。二来如果自己调用命令行参数会出现平台兼容性的问题,window下面肯定跟 Linux,macOS 不一样,如果依靠编辑器提供的api,开发维护都会轻松很多,没有这种兼容性的问题,由编辑器 api 统一去 kill job.

snails也会搜索很大的文件目录,会等一段时间出结果,但不会卡顿

我不用vim,帮不理你,不好意思

没关系,十分感谢大佬的 snails,设计思路上 vim-clap 初始的 idea 就是来自 snails :(。

说句题外话,其实本来是打算切换到 emacs 的,emacs 的 GUI 表现确实好得多,后来太忙了,而且日常以终端为主,最终切换失败 :sweat_smile: ,偶尔还是来 emacs 偷师哈哈

新的进程要延时500ms启动,因为立即启动就会导致手指太快,创建了非常多无用的子进程

这个有的,检测到输入字符有变化时,会等待一个延迟时间才会真正调用异步作业。回头我再 profile 看下瓶颈吧,感谢大佬回复.

snails还有个优化就是,如果搜索出很多文件,只显示前100个,因为很多时候卡顿不在于后台搜索进程。

而是一下子搜索太多结果,渲染上千条匹配会卡顿

个人感觉,这个是系统资源分配策略的问题。使用多线程或者调高优先级并不会解决问题,反而是效果相反?

问题关键是应用意识到自己占据了绝大多数的系统资源了吗?要么应用自己控制点,少占资源慢慢吐数据;要么更高层的 api 介入,通知它减少占用或者不听话直接干掉。


感觉 fd,rg 没有提供选项的话,可以把候选项弄个池子,分段一口一口喂给它们,省得一秒膨胀成炸弹,把房子炸掉。简单说,就是事务里面再分子事务,每完成一个子事务休息一会。而不是像以前一样,把几百万个项目一次性甩给它们,干不完不准吃饭睡觉。

@manateelazycat 这个也有 :sweat_smile: , 现在默认只会展示屏幕大小两倍的数据,剩余返回回来的结果缓存起来,当用户滚动到最后一行时,再从 cache 中每次加载50条数据。vim 的大文件高亮渲染是个问题,所以我一早就刻意避免了这种情况。我回头试一下直接丢弃数据,只统计匹配的个数。因为其实也用不到那么多数据,如果返回几千几万条结果,通常情况下我们只会看前面一两屏的数据,后面的只是想知道一个匹配的结果数而已。

@ashfinal fd, rg 倒是有限制线程数的选项,只是我觉得这个不是最优解。比如 fzf 在用户目录下搜索时,依旧响应迅速,可能我还 “未得法”,目前还没什么思路,设计上觉得问题不大,可能是工程上哪里还有啥问题要去发现。

1 个赞

搜索的太多剩下的直接抛弃掉吧, 不用做什么缓存和用户翻屏获取,对用户来说翻屏太麻烦了。

Snails是Emacs一个进程,每个搜索一个后端一个进程,Snails对过期的进程直接用 kill-process 强制杀掉的,你看一下你杀进程的信号是什么?不管线程数多少,操作系统都可以直接强制杀掉子进程的。

限制目录递归深度,防止跑 home 目录下搜索。也是简单粗暴的解决方案。

这个补丁用fzf作为EAF浏览器的搜索后端,fzf 配合 -e 选项,搜索算法最贴近Chrome的体验,fuz.el太模糊了,导致有很多干扰结果,不太适合浏览器历史搜索。

同时使用的是 fzf 子进程,不管多久的浏览器历史记录,都不会卡住Emacs.

1 个赞

%E6%B7%B1%E5%BA%A6%E5%BD%95%E5%B1%8F_emacs_20200202160325

浏览器历史搜索效果动画

模糊搜索算法 fzy 算是最好的,我从别人那里抄了一个 fzy 的 Python 实现 https://github.com/liuchengxu/vim-clap/blob/master/pythonx/clap/scorer.py ,eaf 刚好也是 Python,可以考虑内置进去, 这样就实现无依赖的模糊搜索了。

好像串场了,是 snails 不是 eaf, 仅供参考吧… 不过 eaf 可以调 Python,snails应该也行…

fzf -e 本质就是把字符串按空格分割以后直接匹配。

1 个赞

其实你可以考虑把 fzy 的 Rust 实现包装一下给 emacs 用,fzy 是模糊算法目前最好的选择,参考 https://github.com/liuchengxu/vim-clap/blob/master/pythonx/clap/extracted_fzy/src/lib.rs