|
|
本帖最后由 鼓掌之间 于 2026-1-12 10:29 编辑
转自:https://www.nexusmods.com/skyrimspecialedition/mods/163969
这款插件能让SKSE协同存档的保存速度提升高达150倍,加载速度提升高达15倍。
其核心目标是让MOD数量较多的游戏,拥有更快的保存和加载速度。
SKSE协同存档是与游戏主存档文件并存的附属文件。
本插件仅加速SKSE协同存档的保存/加载过程,对游戏主存档无任何影响。
因此,加载速度的提升幅度会低于保存速度的提升幅度。
·前言
《天际》脚本扩展器(SKSE)是一款极具价值的工具——几乎所有非基础功能的《天际》MOD,都依赖它才能存在。
事实上,从《上古卷轴4:湮灭》开始,贝塞斯达游戏工作室的几乎所有作品都是如此。
MOD社区(无论是作者还是用户)都应向脚本扩展器系列的开发团队致以深深的谢意——没有他们,我们如今的MOD体验无从谈起。
但脚本扩展器的保存系统是个例外——坦白说,它并不理想。
即便在性能强劲的高端电脑上,保存仅几兆字节的文件也可能需要数十秒。
这是因为自《湮灭》脚本扩展器时代以来,它的保存系统核心逻辑几乎没有任何变化†——设计于5400转硬盘、系统调用成本较低的年代,而且恕我直言,或许当时的开发者也相对缺乏优化经验;)
† 我绝非夸大其词:保存系统的大部分核心代码,与2008年的版本完全一致。
在继续往下说之前,我必须强调:我对脚本扩展器系列的开发者抱有十足的敬意。
本插件的诞生以及我写下的这些话,绝非为了攻击或贬低他们。
同样,也请不要去催促脚本扩展器的开发者修复协同存档保存缓慢的问题——他们早已知晓这一情况。
我希望大家以轻松的心态阅读这份说明,就像老师善意地提醒调皮的学生那样。
尽管SKSE的开发者显然是能力极强的程序员,但在这个案例中,他们的设计和实现却略显遗憾:对于性能至关重要的核心流程,他们提供的API中,最自然、最易用的使用方式,恰恰是速度最慢的方式——而这种状态竟持续了17年。
本插件的作用,就是重写SKSE的保存/加载代码,让它达到应有的速度水平。
·卸载
本插件可随时安全卸载。
安装插件后生成的存档,在卸载插件后仍可正常加载——也就是说,你可以放心试用本插件,之后再根据需求决定是否卸载。
·需求条件
- 一款DLL预加载器:《SSE引擎修复 - SKSE64预加载器》(SSE Engine Fixes - SKSE64 Preloader):https://bbs.3dmgame.com/thread-6587497-1-1.html
或《DLL插件加载器》(DLL Plugin Loader):https://www.nexusmods.com/skyrimspecialedition/mods/10546
均可。
- 《天际》脚本扩展器(SKSE)(这还用说):https://skse.silverlock.org/
·安装
确保已安装DLL预加载器后,即可通过你常用的MOD管理器,像安装其他MOD一样安装本插件。
所有优质的MOD管理器都会默认正确安装本插件。
安装成功后,插件DLL文件相对于游戏根目录的路径应为:“Data\DLLPlugins\Save&LoadAcceleratorForSKSECosaves.dll”。
另请注意:我仅测试过适配《天际》AE 1.6.1170版本和《天际》SE 1.5.97版本的插件,其他版本未经测试(但理论上可正常工作)。
·使用方法
插件安装后默认自动激活。
默认情况下,游戏保存/加载所需的时间会记录到游戏内控制台。
可编辑插件DLL文件同级目录下的“Save&LoadAcceleratorForSKSECosaves.ini”文件,对插件进行配置。
若想进一步提升保存速度,可通过“Save&LoadAcceleratorForSKSECosaves.ini”文件启用实验性的并行保存功能。
·兼容性
本插件理论上可与几乎所有MOD兼容,但可能与其他修改或挂钩SKSE代码的插件存在冲突。
实际上,修改或挂钩SKSE本身的插件极为罕见。
针对常见问题解答:本插件与《天际》存档系统大修(SSSO)兼容。
若使用《魔法修复与优化》(Magic Fixes and Tweaks)或其1.5版本移植版时出现崩溃,请更新至该MOD的最新版本。
·插件工作原理
本插件通过在运行时挂钩并修补SKSE的DLL文件,用高度优化的重新实现代码替换SKSE协同存档保存/加载API的功能,从而加快MOD较多的游戏的保存和加载速度。
这对于频繁使用种族菜单(RaceMenu)的存档效果尤为显著——例如使用OBODY、Mu骨骼编辑器,或XP32最大骨骼MOD中为NPC设计的武器风格等情况。
在我的设备†上,4.25MB大小的SKSE协同存档(测试时数据),保存需要8.7秒。4.25MB的文件耗时8.7秒,换算下来每秒仅保存0.49兆字节。
† 我的设备搭载锐龙7950X3D处理器和读写速度可达7GB/s的PCIe 4.0 NVMe固态硬盘——也就是说,保存4.25MB的文件完全不该需要8.7秒。
激活本插件后,同一个SKSE协同存档的保存时间仅需0.06秒(60毫秒),换算下来每秒可保存70.82兆字节,速度提升145倍。
进一步启用实验性并行保存功能(16线程)后,同一个协同存档的保存时间仅需0.04秒(40毫秒),每秒可保存106.25兆字节,速度提升217倍。
加载协同存档的速度提升则相对温和一些:
未使用插件时,加载该协同存档需要1.5秒;使用插件后,仅需0.1秒,仅提升了15倍。
·插件工作原理(简明版)
让SKSE协同存档的保存速度提升高达150倍,加载速度提升高达15倍。
·使用安全吗?
如果你对使用本插件心存顾虑,说明你很谨慎——任何修改保存/加载逻辑的工具,都有可能导致存档损坏或加载时游戏状态异常。
但我已竭尽全力确保本插件的保存和加载流程,与SKSE原版流程的效果完全一致。
在我的测试中,启用本插件生成的存档,与SKSE原版代码生成的存档完全一致(字节级相同)。
唯一例外是:启用并行保存功能时,各个插件的数据在文件中的保存顺序会略有不同且不可预测——但这并无害处,因为SKSE本身对已加载插件的保存顺序就没有明确规定。
同样,在加载测试中,启用本插件时调用API的序列,与SKSE原版代码完全一致。
·技术探讨
现在大家可能会问:“SKSE的保存系统为什么这么慢?”
问题的核心在于:SKSE提供的API看似由自身负责文件缓冲——但实际上它根本没有做任何文件缓冲,反而会执行数量惊人的系统调用。
SKSE为协同存档保存提供的API如下:
- bool WriteRecord (uint32_t type, uint32_t version, const void *buffer, uint32_t length);
- bool OpenRecord (uint32_t type, uint32_t version);
- bool WriteRecordData (const void *buffer, uint32_t length);
OpenRecord用于启动一个独立的数据块。
WriteRecordData用于向最近一次通过OpenRecord启动的数据块追加数据。
WriteRecord只是一个便捷封装,本质上是先调用OpenRecord,再调用WriteRecordData。
每次调用WriteRecordData,都会间接调用Win32函数WriteFile,即执行一次系统调用。
每次调用OpenRecord,都会间接调用2到3次SetFilePointerEx(执行2到3次系统调用),之后还会像WriteRecordData一样间接调用WriteFile。
因此,本插件的修复逻辑很简单:重新实现这三个函数,使其不执行任何系统调用或磁盘IO,仅将接收的数据直接复制到预先分配的内存区域。待所有插件都完成数据保存后,再将这块内存区域的数据一次性写入文件。
(通过虚拟内存和向量异常处理器的巧妙配合,内存区域会逐步提交,避免了边界检查带来的性能开销。)
加载方面的情况稍好:通常每次调用GetNextRecordInfo和ReadRecordData,仅会间接调用一次ReadFile。
本插件的优化逻辑也类似:先将整个文件一次性读取到内存中,API函数仅需将数据直接复制到调用者提供的缓冲区即可。
·鸣谢
2024年10月4日(一年多以前),GitHub用户“intorr”向SKSE项目提交了一个拉取请求(PR),通过简单巧妙的缓冲文件流实现,加快了SKSE协同存档的保存和加载速度。
在intorr的测试中,协同存档的保存时间从2秒缩短到了0.05秒。
但该拉取请求至今未被合并。
这也是本插件诞生的原因之一。
(说明:我的插件与intorr的拉取请求无任何关联。)
·预设问题解答(PAQ)
Q:插件缩写里为什么没有字母S?K又是哪儿来的?
A:少废话。
Q:你为什么这么粗鲁地回答上一个问题?
A:你试试想个顺口又能组成搞笑缩写的名字?
Q:缩写里为什么要加圆点?
A:不加圆点的话,就会侵犯某聊天软件公司的商标权了。
下载地址:
|
评分
-
1
查看全部评分
-
|