硬盘告急这件事,玩模拟器的朋友大概都经历过:一个 Wii 镜像动辄 4.7 GB,一个 GameCube 镜像 1.4 GB,攒上几十个,一块 1 TB 的盘就见底了。而真正被游戏用到的数据,往往只有镜像体积的六成甚至更少,剩下的全是填充、冗余和从未被读取的扇区。RVZ 就是冲着这部分浪费来的——它把镜像按固定大小的块切开,逐块压缩,再把索引塞到文件尾部,读的时候按块解压还原。我手上几个 GameCube 镜像转成 RVZ 之后,体积普遍只剩原来的三成上下,存储效率提升 70% 这个数字不是营销话术,是能复现的。但这 70% 里藏着一个绝大多数人从没动过的参数:块大小(Block Size)。它决定了你到底是压到 30% 还是 45%,也决定了转完之后游戏会不会在某个场景莫名其妙顿一下。这篇就把这个参数从原理到实操拆开讲,适合已经会用 RVZ、但总觉得自己压出来的文件「好像还能再小一点」的人,也适合刚接触这套流程、不想走弯路的新手。
1. 先把「70%」这个数字背后的账算清楚
1.1 RVZ 到底把镜像拆成了什么
很多人以为 RVZ 就是一个压缩包,解开就是完整镜像。这个理解只对了一半,而且会让你在调参数时完全找不到方向。RVZ 的结构其实很机械:文件头之后是一段原始数据区,镜像被按固定大小的块顺序切分,每一块单独压缩后依次写进数据区;所有块写完之后,文件尾部再追加一张块索引表,记录每个块用了哪种压缩算法、压缩后占多少字节。读取的时候,模拟器拿一个偏移量去索引表里查,算出目标数据落在第几块,把那一块解压到内存,再从块内偏移取出要的字节。
这个设计有个关键性质:块是独立的。第 N 块的压缩过程完全看不到第 N-1 块的内容,压缩器在每一块里都是从零开始建立自己的字典和上下文。这跟 zip、7z 那种「整个文件一个流」的做法完全不同。好处是随机访问快——想读文件末尾的数据,不需要从头解压一遍;坏处是压缩率天然吃亏,因为跨块重复的模式无法互相引用。
所以块大小在 RVZ 里不是一个「随便填填」的存储参数,它直接决定了压缩器每一轮能看见多少上下文,也决定了每次读取时最小要解压多大的数据。这两个方向是打架的:块越大,压缩越狠,但每次读取的解压负担越重;块越小,随机访问越轻快,但压缩率往下掉。所谓「深度调优」,调的就是这个平衡点。
顺带说一句索引表的体积。块数越多,索引项越多,文件尾部的开销就越大。按每块几十字节的索引开销算,一个 1.4 GB 的镜像用 32 KiB 块会切出四万多块,索引本身能到几百 KB 量级;用 2 MiB 块只有七百块,索引缩到十几 KB。这个差值放在 GB 级的镜像面前可以忽略,但当你把块大小压到 32 KiB 这种极端值时,光是索引和哈希校验数据就能吃掉你辛苦压出来的几个百分点收益。
1.2 块大小是唯一一个转完就改不回来的参数
RVZ 的块大小一旦写进文件头,就是只读属性。你没法像改文件名那样事后调整,也没有「重新打包但保留原压缩数据」这种省事的操作——想换块大小,只能拿原始镜像重新转一遍。这意味着这个参数必须一次选对,或者你手里得留着原始 ISO 做退路。
这就解释了为什么大多数人都停在默认值上:Dolphin 的转换对话框里,块大小默认是 128 KiB,压缩算法默认是 zstd 等级 5。这个组合确实是个不错的通用解,转换速度快、体积也不错、兼容性好。但「不错的通用解」和「针对你的场景的最优解」之间,往往还有百分之十几的体积差距或者一两个小时的转换时间差距。
我在最开始的一批镜像上就是无脑默认值,后来拿一个体积最大的镜像做对照实验,同样的压缩等级下把块大小从 128 KiB 换到 2 MiB,最终文件小了将近 9%。这 9% 在单个文件上不算什么,但乘以几十个镜像就是几十 GB 的差别。反过来,如果你把镜像放在机械硬盘或者存储卡上跑,2 MiB 块带来的读取粒度变粗可能让游戏在某些场景掉帧——这时候省下来的空间反而变成了体验成本。
提示:动手之前先把原始 ISO 归档到另一块盘或者备份盘上。RVZ 转回 ISO 虽然可行,但一旦原始镜像被删、RVZ 又出了问题,那就真的没得救了。
2. 块大小在压缩率、解压延迟、内存占用之间做三角博弈
2.1 为什么块越大压得越狠:zstd 的上下文长度
要理解块大小和压缩率的关系,得先知道 zstd 这类算法在干什么。它在压缩时会维护一个「窗口」,窗口里存着刚刚处理过的数据,遇到新的字节序列就去窗口里找有没有出现过相同的片段,找到了就用一个短引用替代。窗口能覆盖的范围越大,能找到的重复模式就越多,压缩率自然越高。
关键在于:每个块是独立压缩的,窗口在块的开头是空的。块切得越碎,压缩器「热身」的次数就越多,每个块开头的那一段数据几乎享受不到任何跨块冗余的好处。反过来,块越大,压缩器能在一个连续上下文里跑得更久,长距离的重复模式(比如游戏里反复出现的同一套纹理、同一批音频数据)才有机会被捕捉到。
更微妙的一点是 zstd 的窗口大小和压缩等级绑定。低等级(比如默认的 5 级)窗口大小在 2 MiB 这个量级,高等级才会开到 8 MiB 甚至更大。这意味着当块大小逼近 2 MiB 之后,单块内的可回溯范围已经基本被窗口吃满了,继续加大块大小,收益曲线会明显变平,甚至因为窗口滑动把早期数据挤出去而出现轻微波动。所以我一直建议:2 MiB 是块大小的实用上限,再往上加只是让转换时间更长,压缩率未必跟着涨。
这条规律也解释了为什么 32 KiB 这种小块这么吃亏。块只有 32 KiB 的时候,压缩器刚建立起一点点上下文就得收工,下一块重新来一遍。实测下来,同等级压缩下 32 KiB 块的最终体积能比 2 MiB 块大出两成以上,而索引开销还翻了几十倍。小块唯一的优势在读取侧,下面说。
2.2 为什么块越大读得越「顿」:一次解压一整块
读取侧的逻辑正好反过来。模拟器要读镜像里某个偏移的 4 KB 数据,它不能只解压这 4 KB——因为块是压缩的最小单位,必须把整块解压出来。块是 128 KiB 就解压 128 KiB,块是 2 MiB 就解压 2 MiB,不管你实际只需要其中多小的一部分。
这个差异在顺序读取时无关紧要,因为读完一块的数据紧接着就要读下一块,解压开销被摊薄了。麻烦出在随机小读取上:某些游戏在加载场景时会大量读取零散的资源条目,每读一条就触发一次整块解压。块越大,每次解压的 CPU 时间越长、内存峰值越高。按当前的解压吞吐量估算,解压 2 MiB 数据大约需要几毫秒,128 KiB 只要零点几毫秒。单次差异看着微不足道,但如果一秒内爆发几十次随机读,累积起来的延迟就足以让音频缓冲见底,表现为爆音或者卡顿。
还有一个隐性成本是块缓存。模拟器内部通常维护一个已解压块的缓存,缓存容量是有限的。块越大,能同时缓存的块数越少,缓存命中率就越容易崩。想象一下你有一个固定大小的书架,书越厚,能放的本数越少,找书时翻箱倒柜的概率就越高。在内存紧张的设备上(比如各种掌机形态的便携设备),这个效应会被放大——2 MiB 的块配上一个不大的缓存,就会出现「刚解压完的块马上被挤出去,下次又要重新解压」的抖动。
所以我的判断标准很朴素:如果你的运行设备是有充足内存的台式机,块大小往大了调没坏处;如果跑在内存吃紧的便携设备上,宁可牺牲几个点的压缩率,也要把块大小压到 256 KiB 以内。
3. 32 KiB 到 2 MiB:一张实测对照表和四类场景的选型建议
3.1 实测数据表与解读
下面这张表是我在一台八核台式机上跑出来的量级数据,样本是一个约 1.35 GB 的 GameCube 镜像和一个约 4.38 GB 的 Wii 单层镜像,压缩算法统一用 zstd 等级 5,勾选了移除无用数据。具体数值会随游戏内容组成、CPU 型号、存储介质变化,但相对趋势是稳的。
| 块大小 | GC 镜像输出 | 相对原镜像 | Wii 镜像输出 | 相对原镜像 | 转换耗时(GC / Wii) |
|---|---|---|---|---|---|
| 32 KiB | 约 610 MB | -55% | 约 2.35 GB | -46% | 22 s / 70 s |
| 64 KiB | 约 560 MB | -59% | 约 2.16 GB | -51% | 18 s / 58 s |
| 128 KiB | 约 520 MB | -61% | 约 2.00 GB | -54% | 16 s / 52 s |
| 256 KiB | 约 480 MB | -64% | 约 1.86 GB | -58% | 15 s / 50 s |
| 512 KiB | 约 450 MB | -67% | 约 1.75 GB | -60% | 15 s / 49 s |
| 1 MiB | 约 425 MB | -69% | 约 1.66 GB | -62% | 16 s / 51 s |
| 2 MiB | 约 405 MB | -70% | 约 1.60 GB | -63% | 17 s / 55 s |
从 32 KiB 到 2 MiB,GameCube 那个样本的体积从 610 MB 一路降到 405 MB,整整三分之一的差距——这就是「块大小」这一个参数的全部能量。Wii 那个样本的压缩率整体偏低,因为 Wii 镜像里有相当比例的数据本身已经是压缩格式(音频、视频、部分资源包),zstd 对这些内容束手无策,无论块多大都压不动。
还有一点值得注意:转换耗时在 128 KiB 附近是最低的,两头都会略微上升。小块是因为块数暴增、调度和索引开销变大,大块是因为单块压缩时间拉长、并行度下降。所以 128 KiB 这个默认值其实是拿转换速度换来的,并不是压缩率上的最优。
3.2 按存储介质和设备分场景给参数
数字摆在面前,但选型不能只看体积。我按实际使用场景分四类,每类的推荐配置不一样:
第一类:台式机 + 固态硬盘,追求极致体积。块大小直接拉满到 2 MiB,压缩等级 5 到 8。这条路线适合「镜像躺在备份盘里,偶尔拿出来跑一跑」的收藏型玩家。固态硬盘的随机读取能力足够强,2 MiB 块的解压延迟被硬件抹平了大半,你基本感知不到副作用。压缩等级不建议一上来就拉到 19 及以上,那会让转换时间从十几秒变成几分钟甚至十几分钟,而体积收益往往只有两三个百分点,性价比很差。
第二类:机械硬盘或网络存储,追求加载稳定。块大小压在 128 KiB 到 256 KiB。机械硬盘的寻道和顺序读取都不快,大块意味着每次读取要等更久的数据搬运,配合解压延迟容易在加载界面上拖出明显的等待。128 KiB 这个默认值在这类介质上反而挺合适,不用折腾。
第三类:便携设备或内存有限的机器,追求不卡顿。块大小 64 KiB 到 128 KiB,压缩等级 3 到 5。这类设备解压能力有限、内存也紧张,把块切小能让每次解压的负担变轻,缓存命中率也更高。你会在体积上损失五到八个百分点,但换来的是稳定的帧生成时间,这笔交易划算。
第四类:要分享给别人或者长期归档。用 128 KiB 或 256 KiB 加压缩等级 5。这个组合的兼容性最好,任何版本的模拟器都能正常读取,对方拿到手里也不会因为设备太弱而出问题。分享场景下,「在我这儿跑得好」不算数,「在别人那儿也跑得好」才算数。
注意:块大小的可选值必须是 2 的幂,范围从 32 KiB 到 2 MiB。填一个 300 KiB 这样的怪数字,工具会直接拒绝或者悄悄回退到默认值,别在这上面浪费时间。
4. 动手改块大小:界面操作和命令行两条路
4.1 图形界面:转换对话框里那几个容易被忽略的勾选
图形界面的操作路径很简单:在游戏列表里右键目标条目,选转换文件的选项,弹出对话框之后把输出格式切到 RVZ,块大小下拉框就会亮起来。这个下拉框平时是灰的,因为它只对 RVZ 和 WIA 生效——CISO 和 GCZ 格式的块大小是写死的,改不了。
对话框里除了块大小,还有三个东西值得留意。第一个是压缩等级,它和块大小是两个独立维度,别混为一谈。块大小决定「压缩器能看多远」,压缩等级决定「压缩器愿意花多少力气去找」。两者叠加才有最终效果:2 MiB 块配等级 5,通常比 128 KiB 块配等级 19 更快也更小,因为高等级在窗口吃不满的情况下等于白费力气。
第二个是移除无用数据这个勾选,它只对 Wii 镜像有效。Wii 的镜像结构里有分区概念,分区内存在大量未被文件系统引用的扇区,这些扇区的内容往往是随机填充,压缩器完全压不动。勾上这个选项能把这些区域直接裁掉,效果比调整任何压缩参数都明显。GameCube 镜像没有这个机制,勾了也没用,不要指望它能给 GC 镜像瘦身。
第三个是转换后校验。这个选项会让工具在转换完成后逐块比对哈希,代价是转换时间翻倍。我的习惯是:只在新参数第一次试用的时候勾上,确认参数没问题之后,后续批量转换就关掉,省时间。验证过一次的组合,没必要每次都验。
顺便提一个容易踩的坑:某些版本的转换对话框里,块大小的下拉选项和压缩等级的输入框是联动的——切换压缩算法会重置等级。所以你改完算法一定要回头确认等级还在你想要的位置,别以为设过一次就一劳永逸。
4.2 命令行:dolphin-tool 的批量转换写法
镜像多了之后,图形界面一个个点太慢。Dolphin 附带了一个命令行工具,路径在模拟器安装目录下,可以做批量转换。典型的调用长这样:
dolphin-tool convert \ -i "/path/to/game.iso" \ -o "/path/to/game.rvz" \ -f rvz \ -b 524288 \ -c zstd:5 \ -j 8参数含义:-i输入、-o输出、-f指定输出格式为 RVZ、-b块大小(单位是字节,524288 就是 512 KiB)、-c压缩算法加等级、-j并行线程数。线程数一般设成物理核心数,超线程带来的收益在压缩这种计算密集任务上并不明显。
批量处理可以写个循环:
#!/bin/bash mkdir -p rvz_out for f in *.iso; do name="${f%.iso}" dolphin-tool convert -i "$f" -o "rvz_out/${name}.rvz" \ -f rvz -b 131072 -c zstd:5 -j 8 echo "done: $name" done提示:不同版本的命令行工具,参数名和取值格式可能略有出入,尤其是压缩参数的写法(有的版本用冒号分隔算法和等级,有的版本拆成两个独立参数)。动手之前先跑一次帮助命令核对一遍,比照着旧教程硬填要靠谱得多。
命令行的另一个好处是错误可见。图形界面转换失败时往往只弹一个笼统的提示,命令行会把具体哪一块出错、是读取失败还是写入失败直接打出来。我遇到过一次镜像末尾有坏扇区的情况,图形界面反复失败但不说原因,换成命令行一眼就看到了错误位置,直接把那一小段数据跳过就解决了。
还有一个实用技巧:转换前先算一遍原始镜像的哈希,转完再算一遍 RVZ 的哈希。如果两者一致,说明数据完整无损;不一致就说明转换过程出了问题,或者你勾选了会裁剪数据的选项。这个比对是判断「转换是否可信」最硬的标准,比任何主观感受都可靠。
dolphin-tool md5 -i "/path/to/game.iso" dolphin-tool md5 -i "/path/to/game.rvz"注意:如果转换时勾选了裁剪无用数据的选项,两个哈希本来就不会一致——被裁掉的部分读出来会是填充值。这种情况下比对就没有意义了,只能靠转换时勾选的校验选项来保证完整性。
5. 转完怎么验收:体积、哈希、实机加载三条线
5.1 哈希校验是唯一靠谱的证据
转换完成之后第一件事不是急着打开游戏,而是确认文件真的没问题。体积变小只能说明压缩生效了,不能说明数据完整。我见过有人转完之后文件小得离谱,兴高采烈地删了原始镜像,结果进游戏发现某个关卡的音乐全是杂音——压缩过程中出了错,但因为没校验,问题一直潜伏到实际读取那一块时才暴露。
校验方式前面提过,用命令行算 MD5 对比是最直接的。如果嫌每次都要算两遍麻烦,至少要在第一次使用新参数组合时做一次完整比对,确认这套参数在你的环境下是可靠的,之后就可以放心批量跑了。
另一个检查点是转换工具自带的校验功能。勾选转换后校验,工具会在写完之后把整个文件重新读一遍,逐块解压并比对原始数据的哈希。这个过程会显著拉长转换时间,但它是唯一能保证「文件里的每一个字节都正确」的手段。对于那种转完就打算删原始镜像的场景,这个时间值得花。
第三个检查点比较轻量:用模拟器自带的属性查看功能,打开 RVZ 文件的信息页,确认格式标识、压缩算法、块大小这几个字段和你设定的值一致。有时候工具会因为参数不被支持而悄悄回退到默认值,你以为自己设的是 2 MiB,实际写进去的是 128 KiB,光看文件体积未必能发现。属性页是确认参数真正生效的最快途径。
5.2 实机加载要看的不是帧率而是这几处
参数生效、哈希通过,接下来才是实机验证。这里我要纠正一个常见误区:不要用平均帧率来判断块大小选得好不好。块大小对帧率的影响微乎其微,绝大多数情况下完全在测量误差范围内。你盯着帧率数字看半天,什么也看不出来。
真正该看的是这几个地方。第一是加载时间的一致性。同一个场景反复进出几次,看加载条走得是不是均匀。块太大的话,某些需要读取大量零散资源的场景会出现加载中途「顿一下再继续」的现象,这就是单次解压负担过重的表现。
第二是音频。音频缓冲对读取延迟最敏感,一旦读取跟不上,最先出问题的就是声音——表现为爆音、断续或者音画不同步。如果你在某个场景听到反复的杂音,而原始 ISO 没这个问题,那就说明块大小或者存储介质的组合在这台设备上撑不住,该往回退了。
第三是过场动画和流式加载。这类场景会持续读取大块数据,是检验压缩格式读取性能的天然压力测试。如果过场动画播放时出现周期性卡顿,间隔还比较规律,基本可以确定是块大小导致的解压节奏问题。
第四是极端场景下的内存表现。在内存紧张的设备上,2 MiB 块可能让整体内存占用明显上浮。这个不容易直接观察到,但如果你发现游戏运行一段时间后开始变卡、重启模拟器又恢复正常,那大概率是内存压力导致的缓存抖动,把块大小降一档通常就能缓解。
我的习惯是给每个新参数组合留一天观察期:把常玩的几个游戏各跑半小时,覆盖加载、过场、战斗、菜单这几个典型场景。一天下来没问题,这批镜像就可以放心归档了。
6. 我踩过的坑和几条不写在文档里的经验
6.1 转换中断留下的半成品文件
这个坑我踩过两次,都是因为磁盘空间算错。转换过程中工具会边压边写,目标文件的体积会一路涨到最终大小,也就是说你需要至少等于输出体积的空闲空间,而不是「输出体积减去原始体积」。我有一次看着盘里还剩 2 GB,觉得转一个预计输出 1.5 GB 的镜像绰绰有余,结果转到一半空间耗尽,工具直接中断。
麻烦的地方在于,中断留下的半成品文件在文件管理器里看起来和正常文件一模一样,体积甚至已经接近预期值。如果你没注意到中断提示,很可能就把它当成转换成功的产物,删掉原始镜像之后才发现根本打不开。
判断方法有两个:一是看文件尾部。正常完成的 RVZ 末尾有完整的块索引表,半成品文件通常缺这一段或者索引不完整,模拟器打开时会直接报错或者干脆识别不出格式。二是用命令行工具做一次完整性校验,半成品文件会在校验时明确报出索引缺失或者块数据越界。
预防措施很简单:转换前先确认空闲空间至少是预期输出体积的两倍,转完之后立刻做一次校验,通过之后再考虑删原始镜像。这两步花不了几分钟,但能省掉重下一遍镜像的几小时。
6.2 块大小与低配设备缓存不匹配导致的卡顿
这个坑更隐蔽,因为我一开始完全没往块大小上想。当时是在一台内存不大的便携设备上跑一批 2 MiB 块的镜像,游戏能进、大部分场景也正常,但每次打开道具菜单都会卡半秒左右。我一度怀疑是模拟器版本问题,换了好几个版本都一样;又怀疑是游戏本身的兼容性问题,查了半天资料也没有结论。
后来做对照实验才找到原因:同一个游戏,换成 128 KiB 块的版本,菜单卡顿消失了。根因是菜单界面需要读取大量零散的小资源,每个资源都落在不同的块里,2 MiB 块意味着每次读取都要解压 2 MiB 才能拿到其中几 KB,而设备的块缓存容量有限,连续几次读取就把缓存冲干净了,于是每次进菜单都是一轮完整的解压风暴。
这件事给我一个教训:块大小的选择不是看设备性能强弱,而是看设备的缓存容量和解压吞吐能不能匹配这个粒度。一台性能很强但内存拮据的设备,照样会被大块拖累。现在我给便携设备准备镜像,一律用 128 KiB,不再纠结那点体积差距。
6.3 裁剪掉的东西不一定是垃圾
最后一个坑跟块大小的关系稍远,但既然聊到 RVZ 的体积优化,绕不开它。转换时那个「移除无用数据」的选项,字面意思是移除「没被引用的数据」,但「没被引用」不等于「没被使用」。有些游戏的保护机制或者特殊功能会去读取文件系统索引之外的扇区,这些数据在常规扫描下看起来是垃圾,裁掉之后游戏本体没问题,但某个隐藏功能或者校验流程就会失败,表现为启动时报错或者进入某个模式后卡死。
我遇到过一次,某款游戏的存档校验在裁剪后失败,游戏能进但一存档就提示损坏。排查了很久才定位到是被裁掉的某个扇区参与了校验计算。
所以我的做法是:对不确定的游戏,第一次转换时不勾裁剪,只做纯压缩。纯压缩是完全无损的,无论块大小怎么调,读出来的数据和原始镜像逐字节一致。等确认游戏一切正常、而且你确实需要那点额外空间,再考虑带裁剪重转一遍,转完立刻做完整校验和实机测试。这个顺序看起来麻烦,但比事后排查一个莫名其妙的存档损坏要省心得多。
说到底,块大小这个参数的调优空间比大多数人想象的大得多,从 32 KiB 到 2 MiB,同样一个镜像的输出体积能差出三成。但它也不是越大越好、越小越好的单向指标,而是要跟你实际使用的存储介质、运行设备的缓存和解压能力、以及这个镜像是自己用还是要分享出去这几个因素一起考虑。我自己的做法是维护两套参数:归档用的镜像统一 2 MiB 块配中等压缩等级,追求体积;日常在便携设备上跑的镜像统一 128 KiB 块,追求稳定。两套镜像各存一份,代价是磁盘占用翻倍,但省掉了每次都要纠结参数的时间。
如果你手上镜像不多,其实没必要搞这么复杂,先用默认的 128 KiB 跑一段时间,遇到卡顿了再往下调,觉得体积不够了再往上加,用实际体验来定参数,比看任何教程都准。