GC加速猜想,抛砖引玉

emacs慢一大部分是gc,gc慢主要是分析大量对象是否可回收慢,其实释放不用对象空间不耗费性能。

单线程花太多时间分析gc,就会卡emacs。

有没有一种可能,在emacs外部分析emacs的内存特征和重建gc对象扫描,这样没扫完就持续扫,一旦扫完同时emacs空闲,就让emacs直接释放内存。

这样做会增加外部进程内存占用,但绝对不卡emacs,性能提升会让所有elisp插件受益。

现在流行的操作系统安全策略 (ASLR) 都不让这么搞了吧

4 个赞

现在scratch/igc分支使用感受快了很多,不过没有了解实现方法

感觉上在 emacs 内部开些线程做并发 GC 就行? 外部进程做 GC 暂时没想到什么特别的优势.

瞎猜现在 igc 分支上用的 GC 应该有这种设计, 一般 mark stack/reg 之类的地方需要停一下, 大部分 mark 和 sweep 就可以放在其他线程了, 然后 compaction 阶段也会停一次. 整个 GC 还是会有一些 pause, 不过比在主线程 GC 要好挺多.

出于安全考虑外部扫描基本不可行,内部专门开一个线程做这事儿?

还好把,
1 pe文件自己的重定位可以直接改pe结构的字段来关闭
2 堆内存也可以指定分配地址,如果是外部进程,也不会冲突

1 个赞

刚去看了下igc分支的实现 emacs/src/alloc.c at 0756b1f2f5452d715396f66d887c137776e360ca · emacs-mirror/emacs · GitHub

和原来的几个区别
1 所有的标记操作不需要在这里做了

#ifndef HAVE_MPS
  struct gc_root_visitor visitor = { .visit = mark_object_root_visitor };
  visit_static_gc_roots (visitor);

  mark_pinned_objects ();
  mark_pinned_symbols ();
  mark_lread ();
  mark_terminals ();
  mark_kboards ();
  mark_threads ();
  mark_charset ();
  mark_composite ();
  mark_profiler ();
#ifdef HAVE_PGTK
  mark_pgtkterm ();
#endif
#ifdef USE_GTK
  xg_mark_data ();
#endif

#ifdef HAVE_HAIKU
  mark_haiku_display ();
#endif

#ifdef HAVE_WINDOW_SYSTEM
  mark_fringe_data ();
#endif

#ifdef HAVE_X_WINDOWS
  mark_xterm ();
  mark_xselect ();
#endif

#ifdef HAVE_ANDROID
  mark_androidterm ();
#ifndef ANDROID_STUBIFY
  mark_sfntfont ();
#endif
#endif

#ifdef HAVE_NS
  mark_nsterm ();
#endif
  mark_fns ();
#endif // not HAVE_MPS

2 实际的清理工作也不需要这里做了

#ifndef HAVE_MPS
  queue_doomed_finalizers (&doomed_finalizers, &finalizers);
  mark_finalizer_list (&doomed_finalizers);

  /* Must happen after all other marking and before gc_sweep.  */
  mark_and_sweep_weak_table_contents ();
  eassert (weak_hash_tables == NULL);

  eassert (mark_stack_empty_p ());
  gc_sweep ();

  unmark_main_thread ();
#endif

不过他用mps也是用的协作式的方式,这种貌似不是在独立的线程,对于用户体验而言没有根本性的提升

3 个赞

确实, 现在的改法 emacs 里面不会直接调用 gc 了, 应该是在 alloc 之类的地方偷一点 cpu 时间做 concurrent gc. 看 mps 的设计应该是能在另一个线程做一些 mark 之类的事情, 但是恐怕得 benchmark 下看看才知道有没有实际收益.

目前对用户体验可能也有点提升, mps 可以做 incremental gc, 就算是在主线程, 理想情况可以把 gc 时间分散成多个小阶段, 用户的感受到的长时间停顿会少点.

感觉上比起依赖外部 gc 可能还是针对自己的需求写一个比较好. 但是 emacs 历史包袱有点重, 看 igc 分支的改动, gc 和 lisp 还有编辑器一些功能都耦合在一起了, 改起来似乎也挺头疼的.

1 个赞