这块的内容比较多,单独开一个帖子。
在这个帖子中 优化windows版本emacs的总结(持续更新 2024-2-6) 提到cmdproxy的存在。
它编译出来后是在类似这样的位置
C:\opt\emacs-2024-2-2\libexec\emacs\30.0.50\x86_64-w64-mingw32\cmdproxy.exe
源码的位置在nt\cmdproxy.c
这个文件到底干啥了呢,打开源码可以看到干的事情还比较多,包括用各种后缀去定位程序,处理编码。
但是单纯的命令执行如果是我自己来做,直接cmd /c
拼接下不就行了,感觉只是自己用的话没必要这么复杂。
于是用procmon监控下emacs到底用cmdproxy干了啥,
在emacs里面执行M-x shell-command
whoami
如下可以看到,它仅仅是执行了cmdproxy -c whoami
再测试下
async-shell-command
,会发现和shell-command执行的命令相同再测试下
shell
,会发现它执行的是cmdproxy -i
那就比较简单了,我自己写个程序,替换掉cmdproxy,执行速度应该会更快把。
写如下的newcmdproxy.cpp
#include <string.h>
#include <string>
int main(int argc, char** argv)
{
if (argc < 2) {
return -1;
}
if (strncmp(argv[1], "-c", 2) == 0) {
std::string cmd = "cmd /c ";
cmd += argv[2];
::system(cmd.c_str());
} else {
::system("cmd");
}
}
编译之后替换掉之前的cmdproxy.exe。
测试如下的elisp函数都能正常执行
shell-command
async-shell-command
shell
注意因为是直接用的cmd /c执行命令,没有处理编码,默认是gbk,需要如下设置下
(modify-coding-system-alist 'process "[cC][mM][dD][pP][rR][oO][xX][yY]" '(chinese-gbk-dos . chinese-gbk-dos))
接下来试试magit。本以为能提升速度,结果和之前没有变化。
再用procmon监控看看,发现magit并不是调用上面的函数来执行命令,貌似是直接走的call-process
这就还得再研究下了。
对magit也做了下分析,抓取进程名是git的,发现有点震惊的事情,只是打开一个git仓库,简单操作 了一下,magit就执行了好几十条命令,这想快也难呐。
就算换成libgit,估计也提升不到哪里去。
另外可以看到,magit还自己尝试用各种步骤去处理路径问题,其实这些完全没必要magit来做,判断是windows之后直接从PATH里面读就行了,为了解决兼容性问题,多启动了这么多次进程,肯定就慢。
"c:\Program Files\Git\cmd\git.exe" -c "alias.X=!x() { which \"$1\" | cygpath -mf -; }; x" X git
C:/Program Files/Git/mingw64/libexec/git-core/git.exe
git.exe -c "alias.X=!x() { which \"$1\" | cygpath -mf -; }; x" X git
C:/Program Files/Git/mingw64/libexec/git-core/git.exe
sh -c "x() { which \"$1\" | cygpath -mf -; }; x \"$@\"" "x() { which \"$1\" | cygpath -mf -; }; x" git
C:/Program Files/Git/cmd/git.exe
sh -c "x() { which \"$1\" | cygpath -mf -; }; x \"$@\"" "x() { which \"$1\" | cygpath -mf -; }; x" git
C:/Program Files/Git/cmd/git.exe
"C:\Program Files\Git\usr\bin\which.exe" git
/cmd/git
"C:\Program Files\Git\usr\bin\cygpath.exe" -mf -
从标准输入读路径进行转换
"c:\Program Files\Git\cmd\git.exe" -c "alias.P=!cygpath -wp \"$PATH\"" P
把PATH中的内容打做路径转换
git.exe -c "alias.P=!cygpath -wp \"$PATH\"" P
"c:\Program Files\Git\mingw64\libexec\git-core\git.exe" --exec-path
c:/Program Files/Git/mingw64/libexec/git-core
"c:\Program Files\Git\mingw64\libexec\git-core\git.exe" -c alias.echo=!echo echo x{0}
"c:\Program Files\Git\mingw64\libexec\git-core\git.exe" config --get-all credential.helper
"c:\Program Files\Git\mingw64\libexec\git-core\git.exe" version
"c:\Program Files\Git\mingw64\libexec\git-core\git.exe" --no-pager --literal-pathspecs -c core.preloadindex=true -c log.showSignature=false -c color.ui=false -c color.diff=false -c i18n.logOutputEncoding=UTF-8 rev-parse --show-toplevel
"c:\Program Files\Git\mingw64\libexec\git-core\git.exe" --no-pager --literal-pathspecs -c core.preloadindex=true -c log.showSignature=false -c color.ui=false -c color.diff=false -c i18n.logOutputEncoding=UTF-8 --no-pager --literal-pathspecs -c core.preloadindex=true -c log.showSignature=false -c color.ui=false -c color.diff=false -c i18n.logOutputEncoding=UTF-8 config --list -z
"c:\Program Files\Git\mingw64\libexec\git-core\git.exe" --no-pager --literal-pathspecs -c core.preloadindex=true -c log.showSignature=false -c color.ui=false -c color.diff=false -c i18n.logOutputEncoding=UTF-8 update-index --refresh
"c:\Program Files\Git\mingw64\libexec\git-core\git.exe" --no-pager --literal-pathspecs -c core.preloadindex=true -c log.showSignature=false -c color.ui=false -c color.diff=false -c i18n.logOutputEncoding=UTF-8 rev-parse --verify HEAD
"c:\Program Files\Git\mingw64\libexec\git-core\git.exe" --no-pager --literal-pathspecs -c core.preloadindex=true -c log.showSignature=false -c color.ui=false -c color.diff=false -c i18n.logOutputEncoding=UTF-8 rev-parse --git-dir
"c:\Program Files\Git\mingw64\libexec\git-core\git.exe" --no-pager --literal-pathspecs -c core.preloadindex=true -c log.showSignature=false -c color.ui=false -c color.diff=false -c i18n.logOutputEncoding=UTF-8 --no-pager --literal-pathspecs -c core.preloadindex=true -c log.showSignature=false -c color.ui=false -c color.diff=false -c i18n.logOutputEncoding=UTF-8 status -z --porcelain --
"c:\Program Files\Git\mingw64\libexec\git-core\git.exe" --no-pager -c core.preloadindex=true -c log.showSignature=false -c color.ui=false -c color.diff=false -c i18n.logOutputEncoding=UTF-8 --no-pager -c core.preloadindex=true -c log.showSignature=false -c color.ui=false -c color.diff=false -c i18n.logOutputEncoding=UTF-8 diff --ita-visible-in-index --no-ext-diff --no-prefix --
"c:\Program Files\Git\mingw64\libexec\git-core\git.exe" --no-pager --literal-pathspecs -c core.preloadindex=true -c log.showSignature=false -c color.ui=false -c color.diff=false -c i18n.logOutputEncoding=UTF-8 rev-parse --is-bare-repository
"c:\Program Files\Git\mingw64\libexec\git-core\git.exe" --no-pager -c core.preloadindex=true -c log.showSignature=false -c color.ui=false -c color.diff=false -c i18n.logOutputEncoding=UTF-8 --no-pager -c core.preloadindex=true -c log.showSignature=false -c color.ui=false -c color.diff=false -c i18n.logOutputEncoding=UTF-8 diff --ita-visible-in-index --cached --no-ext-diff --no-prefix --
打出了diff的输出
"c:\Program Files\Git\mingw64\libexec\git-core\git.exe" --no-pager --literal-pathspecs -c core.preloadindex=true -c log.showSignature=false -c color.ui=false -c color.diff=false -c i18n.logOutputEncoding=UTF-8 rev-parse --verify refs/stash
"c:\Program Files\Git\mingw64\libexec\git-core\git.exe" --no-pager --literal-pathspecs -c core.preloadindex=true -c log.showSignature=false -c color.ui=false -c color.diff=false -c i18n.logOutputEncoding=UTF-8 --no-pager --literal-pathspecs -c core.preloadindex=true -c log.showSignature=false -c color.ui=false -c color.diff=false -c i18n.logOutputEncoding=UTF-8 symbolic-ref --short HEAD
"c:\Program Files\Git\mingw64\libexec\git-core\git.exe" --no-pager --literal-pathspecs -c core.preloadindex=true -c log.showSignature=false -c color.ui=false -c color.diff=false -c i18n.logOutputEncoding=UTF-8 --no-pager --literal-pathspecs -c core.preloadindex=true -c log.showSignature=false -c color.ui=false -c color.diff=false -c i18n.logOutputEncoding=UTF-8 rev-parse --verify --abbrev-ref master@{upstream}
"c:\Program Files\Git\mingw64\libexec\git-core\git.exe" --no-pager --literal-pathspecs -c core.preloadindex=true -c log.showSignature=false -c color.ui=false -c color.diff=false -c i18n.logOutputEncoding=UTF-8 rev-parse --verify HEAD~10
"c:\Program Files\Git\mingw64\libexec\git-core\git.exe" --no-pager -c core.preloadindex=true -c log.showSignature=false -c color.ui=false -c color.diff=false -c i18n.logOutputEncoding=UTF-8 --no-pager -c core.preloadindex=true -c log.showSignature=false -c color.ui=false -c color.diff=false -c i18n.logOutputEncoding=UTF-8 log --format=%h%x0c%D%x0c%x0c%aN%x0c%at%x0c%s --decorate=full -n10 --use-mailmap --no-prefix --
这里不禁在想,如果我手动指定了git的路径,是不是就能减少前面执行的命令,试了下还真可以,用如下的命令设置之后就跳过了前面获取git程序路径,解析PATH的过程。
(setq magit-git-executable "C:\\Program Files\\Git\\mingw64\\bin\\git.exe")
不过这里有个坑,我开始是把路径设置为 C:\\Program Files\\Git\\cmd\\git.exe
,就会同时执行C:\\Program Files\\Git\\mingw64\\bin\\git.exe
这两个git.exe。
只有按上面那样设置才能生效,最大化减少执行的命令
总结:
1 用上面代码编译出来的程序直接替换cmdproxy.exe。对于使用上面提到的函数执行命令的插件,应该有执行速度的提升。但是对magit没用。
2 手动设置 (setq magit-git-executable "C:\\Program Files\\Git\\mingw64\\bin\\git.exe")
之后对magit的启动速度有提升