Mac OS X 下与 SSD 相关的优化

最近刚入手一块 Intel X25-M G2 80G SSD,使用效果不错,于是整理了下面这些跟 SSD 有关的优化经验:

安装

因为 SSD 比较小,而有些数据又不是那么需要快速的访问,所以我保留了 MacBook Pro 里自带的 320G 7200 RPM 硬盘,做了如下改动:

  1. 买了一个光驱位硬盘托架,最有名,也是最贵的一种要 $99,但山寨版可以在淘宝找到,对于 Unibody 的机器来说,找 9.5mm 高的那种型号就行,比如 Fenvi 的,建议买 100 以内的。

  2. 把 MBP 拆开,SuperDrive 取下,把原来的硬盘取出,放入光驱位硬盘托架,把 SSD 接在原来的硬盘所接的 SATA 口,然后把硬盘托架安装回原来光驱的位置。这种搭配是为了保证用 SSD 做系统盘 (挂载在 / 上) 时系统能正确的让硬盘休眠。当然,如果你比较奢侈地买两块 SSD,也可以让他们组成 RAID0,这样性能就更夸张了。

  3. 给在 SSD 所在卷分区,只需要分一个区,安装系统。新的系统会把原来那块硬盘也列出来。这时修改 /etc/fstab,加入:

    /dev/disk1s2 /Users/jjgod/Downloads hfs rw
    

    这个做法是把原来那块硬盘挂载在我自己这个用户的 Downloads 目录,这样所有下载的内容都会放在这块比较大的硬盘上 (当然有用的我以后会转移到 SSD 上),考虑到 Downloads 主要起的就是一个临时的缓冲作用,这样还是比较方便的。然后重启机器,就能发现这块硬盘只被挂载在上述目录下了。

  4. 除了下载内容,我还在这个目录放以下内容:

    • VMware Fusion 的虚拟机文件,这是从别的机器复制过来的,直接放在这个卷的 /Virtual Machines 目录下,然后在 VMware Fusion 里打开就行了。需要注意的是以后 VMware 里创建新虚拟机的时候也得记住选择目录,别放在自己的 $HOME/Documents 下面了。

    • iTunes 媒体库,这个可以在 iTunes 的 Preferences -> Advanced -> iTunes Media folder location 选择。我是先在这里选好了,然后从原来的机器里导入以前的 iTunes 数据。

    • 其他下载软件的默认下载位置,包括 uTorrent, Transmission 和 aMule。

    • Steam 的游戏目录,这个可以通过创建符号链接到 $HOME/Documents/Steam Content 实现。

优化

上述安装过程后,系统已经能保证数据各自放在比较合适的地方了,参考 Mac OS X SSD Tweaks 这篇文章的介绍,我们还可以做如下优化:

  1. noatime 方式挂载系统盘,这样可以减少不必要的 I/O 次数,虽然 SSD 做这些操作非常快速,但考虑到最后访问时间这个属性其实很少用到,大家关心的一般都是最后修改时间和创建时间,所以完全可以关闭这个属性,这在 Unix/Linux 下是非常常见的文件系统优化选项。一个简单的方法是,修改 /etc/fstab,加入:

    /dev/disk0s2 / hfs rw,noatime
    

    重启后,系统盘的挂载就带上 noatime 选项了:

    $ mount | grep " / "
    /dev/disk0s2 on / (hfs, local, journaled, noatime)
    
  2. 禁用冬眠 (hibernate) 模式以节省空间。在 Mac 耗尽电池时,会进入“冬眠”模式,将内存中的所有内容写入磁盘,下次唤醒后从这些内容恢复状态。所以系统会在 /var/vm 维护一个和内存等大的 sleepimage 文件,考虑到 SSD 空间宝贵,而一般绝少会遇到耗尽电池的情况,可以禁用掉这个功能以节省空间:

    $ sudo pmset -a hibernatemode 0
    $ sudo rm /var/vm/sleepimage
    
  3. 减少临时文件的读写。

    • RAMDisk 是常见的性能优化手段,对于内存充足的机器,把频繁读写的内容放到一个用内存为存储的虚拟磁盘中,能大大加快速度。虽然 Macintosh Performance Guide 最近的一个研究表明 RAMDisk 并不能很大地提升如 Photoshop 这类软件操作的性能,至少投入产出比不是很经济,但至少用它来保存一些本来就可以随意丢弃的数据是个很好的思路。Mac OS X SSD Tweaks 提供了创建 RAMDisk 的脚本,他的做法是在系统启动时创建一个 256M 的 RAMDisk,挂载在 /private/tmp 上面。他还建议把 ~/Library/Caches 也放到 RAMDisk 里。最大的问题是,如果你长期不重启 (我一般的重启周期是 80 ~ 100 天),那有的临时文件,比如 ~/Library/Caches/com.apple.Safari/Webpage Previews/ 会增长到数百 M 甚至上 G。如果被它占满了 RAMDisk 的空间,那其他缓存数据就写不进去了。

    • 考虑到 RAMDisk 是个比较有风险的优化手段,也可以用以下方法禁用掉 Safari 的 Webpage Previews 以减少临时文件读写。如果你像我一样从来不用 Safari 的“Top Sites”功能,一定会很讨厌它凭空占用大量的空间。

      $ defaults write com.apple.Safari DebugSnapshotsUpdatePolicy -int 2
      
    • 关闭 Spotlight 索引也是一个有用的优化手段,如果你像我这样从来不用 Spotlight 的话。

结语

  • Macintosh Performance Guide 还提供了一些更奢侈的优化手段,这里不一一介绍了,因为我自己也没试过,有兴趣可以自己尝试。值得尝试的是它提供的 DiskTester 工具有一个 recondition 功能,通过大量写空白块来优化长时间使用后的 SSD 性能。

  • “安装”部分感谢草莓数码的 iBook 和 MacBook 的帮助。

  • “优化”部分大部分思路来自 Mac OS X SSD Tweaks

是否该用 Core Data?

Core Data 是 Cocoa 里面一套非常受欢迎的框架,从 Mac OS X 10.4 提供以来,在 10.5 中引入了完善的 schema 迁移机制,再到 iPhone OS 3.0 时被引入 Cocoa Touch,这套完善的框架都被认为是管理大量结构化数据所首选的 Cocoa 框架,尤其是因为使用 Core Data 能大大减少需要手工编写的代码量,就使它更受开发者欢迎了。

不过最近却出现了一些不同的声音,先是传出消息说 Aperture 3.0 抛弃了 Core Data,改为直接操作 SQLite 数据库 (大家联想到 Apple Mail 3.0 也是直接用 SQLite,没有用 Core Data),但因为都是 Apple 内部的决定,大家只能凭空猜测理由;接下来,NetNewsWire 的开发者 Brent Simmons 也说在 NetNewsWire for iPhone从 Core Data 转向用 FMDB 来操作 SQLite 数据库 (FMDBGus Mueller 编写的一层很薄的 SQLite 在 Objective-C 下的封装),Brent 给的理由就很充分了:他要做的很多操作都是对数据批量进行的,其实不需要把所有数据都保存在内存里遍历执行,那样更慢,直接交给数据库,往往一条语句就搞定了,简洁而且快速。

然后 Jonathan ‘Wolf’ Rentzsch对此深表赞同,并推荐 Aaron Hillegass 的 <a href="http://weblog.bignerdranch doctissimo viagra en ligne.com/?p=232″>BNRPersistence 框架,这个框架用 Tokyo Cabinet 提供了一个类似 Core Data 接口的数据持久化方案,最大的优点是比 Core Data 快得多,根据 Aaron 自己的测试,常见的操作都要快 10 ~ 20 倍。其实快这么多也可以理解,毕竟 BNRPersistence 要比 Core Data 轻量得多,支持的功能也少很多,加上 Tokyo Cabinet 这样的 Key-value 数据库在处理适合它的操作时,多数要比 SQLite 这样的关系型数据库要快。

所以突然 Core Data 就有点被墙倒众人推的意思,好像以前大家都知道它不好用,但都不好意思说,直到突然有经验足够丰富的开发者开头,就一涌而上开始骂了。我个人的观感是 Core Data 作为官方方案,给开发提供的许多便利还是不可小视的,但考虑学起来确实也不容易 (所以才有人专门写本书讲 Core Data),所以新上手的 Cocoa 程序员不妨先考虑一下 BNRPersistence, FMDB 这样的方案。

那是否 Core Data 就该被抛弃呢?目前的争议其实有点像 Web 开发里到底该不该用 ORM,区别是大多数 Web 开发者因为历史原因,对直接进行数据库操作有偏好 (或者说,本能地反感 ORM),而多数 Cocoa 开发者则坚定支持用对象操作数据,所以长远来看,数据持久化方案在 Cocoa (Touch) 开发里少不了,唯一的疑问是 Apple 会不会继续改进 Core Data 的性能和接口,以拉近与第三方方案的差距,只要 Apple 还在不断改进,Core Data 就有学习的必要。

我个人对 Core Data 的怨念主要是在要写的“胶水代码”上:有的时候为了方便与界面的 bindings 操作,不得不给 Model 写大量重复的胶水代码,所以如果第三方的方案如果在这方面有简化,我很乐意改用。

用 XBMC 整理电影收藏

随着硬盘上收集的电影多了起来,怎么管理它们就成了问题,我之前采用的方法很简单:先把电影下载到内置的硬盘上,然后把看过之后觉得值得保留的那些,或者还没来得及看,但内置硬盘空间不够的时候,转移到外置硬盘 (一般是大容量的仓库盘) 上。一般这样的仓库盘就直接在根目录下分个 Movies 目录,然后所有的电影每个占一个目录,全部按它们原来的名字移动到这个 Movies 下面。类似这样:

$ cd /Volumes/Foo/Movies
$ ls
Apocalypto.2006.720p.BluRay.x264-ESiR
Gake.no.ue.no.Ponyo.2008.BluRay.720p.DTSES.3Audio.x264-CHD
Lock.Stock.and.Two.Smoking.Barrels.1998.720p.BluRay.x264-CYBERMEN
Mission.Impossible.1996.720p.BluRay.DD.x264-ESiR
Surrogates.2009.720p.BluRay.x264.DTS-WiKi
...

而自从 XBMC 被移植到 Mac 上以来,就开始关注这个软件内置的多媒体管理功能,毕竟大部分多媒体文件不是从 iTunes Store 上买的,所以 FrontRow 并不那么合用,而这个软件从功能全面和界面美观的程度上看都合适。不过之前一直都在用 Plex 这个 fork 版本,因为 Plex 确实对 Mac 相关的一些细节处理的比较好点,但也只把它当作一个单纯的媒体播放器来用,并没有用到它的库管理功能。主要是因为 XBMC / Plex 要求的命名方式是“标题 (年份)”这样的类 imdb 方式,而我又想保留原有的目录名 (方便找字幕什么的),所以一直没有实施。

更新:发现 XBMC 还能根据目录下放置的 nfo 文件中的 imdb 地址来查找,而且这样更精确,所以假如你每个目录都保存了 nfo 文件,可以不必修改目录的命名。

最近想到的取巧做法是,对每个这样的电影目录,都在本地目录建立一个符合 XBMC 命名规范符号链接,然后让 XBMC 对这个本地目录进行索引。于是写了这么一个脚本 试了一下,果然很方便,效果如图:

这里简单介绍一下步骤,供有兴趣的朋友参考:

  1. 推荐使用最新的 XBMC 9.11 版本,Plex 最新的 0.8.5 版本这方面支持得不好;

  2. 图中使用的是 Rapier 这个 skin,需要单独下载,推荐使用;(下载后将解压得到的目录移动到 ~/Library/Application Support/XBMC/skin 目录下,然后在 XBMC 的 Settings -> Skin 中选择 Rapier)

  3. 下载前面提到的这个 makelib.py 脚本,使用方法如下:

    $ chmod +x makelib.py
    $ ./makelib.py /Volumes/Megatron/Movies ~/Movies
    Apocalypto.2006.720p generic viagra 100mg.BluRay.x264-ESiR -> '/Users/jjgod/Movies/Apocalypto (2006)'
    Gake.no.ue.no.Ponyo.2008.BluRay.720p.DTSES.3Audio.x264-CHD -> '/Users/jjgod/Movies/Gake no ue no Ponyo (2008)'
    Mission.Impossible.1996.720p.BluRay.DD.x264-ESiR -> '/Users/jjgod/Movies/Mission Impossible (1996)'
    Surrogates.2009.720p.BluRay.x264.DTS-WiKi -> '/Users/jjgod/Movies/Surrogates (2009)'
    The.Cove.2009.LIMITED.DVDRip.XviD-AMIABLE -> '/Users/jjgod/Movies/The Cove (2009)'
    ...
    

    这么做的效果是扫描 /Volumes/Megatron/Movies 目录下所有匹配的目录,在自己的主目录下的 Movies 目录中建立对应的符号链接。

    需要注意的是,这个脚本会要求所有内容都是目录,而且命名都是 Movie.Title.Year.*.*.*.-Group 的格式,中间一段可以任意,但必须包含年份和小组后缀,如果你手头存的目录名称不规范,必须先修改为这种格式,有的来源目录名称就不包含年份,这时可以根据 themoviedb.org 或者 imdb.com 提供的年份来自行添加。如果要求不那么规范,可以修改脚本来忽略小组。另外还要注意的是,Director’s Cut, DC, Unrated, Limited 这样的词必须放到年份后面,要么自己改改脚本,过滤掉它们也行。

  4. 打开 XBMC,进入 Videos 部分,选择 Add Source,用 Browse 选择这个 ~/Movies 目录,在 Set Content 中,选择用 themoviedb.org 作为内容来源,选上 Use folder names for lookup,如图:

  5. 然后 XBMC 就会开始索引所有创建好符号链接的目录,如果能在 themoviedb.org 上找到对应记录,就会自动下载。这个时候你切换到 XBMC 的 Movies 部分,就能看到如前面截图一样的效果了。选择其中任何一个,按回车就会开始播放。按 c 键或者用 Apple Remote 的 menu 按钮则可以打开上下文菜单,这里可以编辑选定项目的信息、删除特定项目 (如果导入的内容不对)、设置已经看过的标志等等。注意在 XBMC 扫描的时候不要断开存储实际内容的硬盘,否则 XBMC 会认为本地存的是无效的链接而直接跳过。如果出现个别电影名称不规范导致找不到对应记录的,可以在更名后按 c 选择重新扫描。

  6. 如果你下载了新的内容,可以再次以同样的参数运行前面提到的脚本,这个脚本会跳过已经添加的内容。另外,还可以在 XBMC 的 Settings -> Videos -> Library 部分选上 Update Library on Startup,这样每次启动 XBMC 的时候就会自动扫描你添加为库的目录,检查是否有新的内容了。

homebrew — Mac OS X 下新的软件包管理工具

虽然 Mac OS X 自带了不少 Unix 下的开源软件,比如 vim, ruby, python, perl 等等,也自带了许多常用的库,包括 iconv, zlib 等等,但我们仍然有时会需要自己动手安装一些这样的软件或者库,要自动化这样的安装,现有最常见的选择是 MacPortsFink,其中 MacPorts 是基于源代码的包管理,并不在自己的库里储存软件的实际内容,只有一个定义如何编译代码的 Portfile 和一些专门针对这个平台的 patch;而 Fink 则是 Apt 包管理系统在 Mac OS X 下的一个克隆,采用二进制分发,用户直接从 Fink 的仓库中下载安装软件。

这两种方式各自有各自的优点和缺陷:

MacPorts 基于源代码的管理优点是非常灵活,更新很快 (很多时候更新只需要修改一下 Portfile 里的版本号和压缩包校验码就可以),用户要订制安装也可以简单的通过修改 Portfile 实现,而且很多开源软件的安装配置会有多种模式 (典型的大都通过 configure 步骤配置),在 MacPorts 中可以方便地通过 variants 参数指定,而不必像二进制分发那样,在远程服务器上编译的时候就定死了。而 MacPorts 的问题是,它希望自己安装的每套软件,所有的依赖都在它自己这个系统内 (一般就是你的 /opt/local) 解决,就算 Mac OS X 系统原生自带了满足依赖的库,它也坚决不用,这样就给你的系统增加了许多冗余,也客观增加了管理上的难度,典型的情况是:你的系统里装了两套 Python,该怎么管理外部安装的 Python 模块?比如通过 easy_installsetup.py 安装的,往往很难记住到底装到哪里了。

而 Fink 虽然不会这么自作主张地添加依赖,最大的问题是更新不够及时,这也是缺乏维护人手导致的。二进制安装的缺点在上面也提到了:不便定制。

所以 Homebrew 的出现,也许不是很及时,但在现在仍然是很必要的,它有这么一些优点:

  1. 尽可能的利用你的系统里自带的库,包括 zlib, OpenSSL, Python 等等,只要 Mac OS X 自带了,它就不会另装一份。

  2. 定制简单,通过用 Ruby 写的 Homebrew formula 来定制,甚至可以灵活的跟踪直接来自版本管理库的最新软件

  3. 用 Git 管理和同步自身

  4. 直接装在 /usr/local 下,这样可以少定义很多各种 PATH 环境变量

其中第一点尤为重要。好的,下面简单介绍一下 Homebrew 的安装,以及它是如何工作的。

安装

首先,Homebrew 的原则是“No sudo”,也就是说,既然 Mac OS X (client 版本) 绝大部分情况下都是归你这个有管理员权限的用户,为什么在自己的 /usr/local 下安装程序还需要 sudo 呢?所以,首先:

sudo chown -R `whoami` /usr/local

然后可以正式开始安装,我推荐的安装方式是先用 git-osx-installer 装上 git,然后用 git 安装:

cd /usr/local
git init
git remote add origin git://github.com/mxcl/homebrew.git
git pull origin master

这么做的实际作用是把你的 /usr/local 目录变成了一个本地 git 仓库,只不过这个仓库只跟踪跟 Homebrew 相关的更新,并不影响任何其他软件的安装。

这样安装会在 /usr/local 下创建 Library 这个目录,然后在 /usr/local/bin 中加入 brew 这个 ruby 脚本。

使用

安装完毕,下面就可以试试了:

brew search

这个命令用来搜索所有可以通过 homebrew 安装的软件,不带任何参数的时候就是列出所有的。可以看到数量已经不少了。

下面就是选择安装,比如我想安装 unrar:

$ brew search rar
gnu-scientific-library     unrar

$ brew install unrar
Warning: It appears you have Macports or Fink installed
Although, unlikely, this can break builds or cause obscure runtime issues.
If you experience problems try uninstalling these tools.
/usr/local/Library/Formula/unrar.rb:3: warning: already initialized constant ALL_CPP
==> Downloading http://www.rarlab.com/rar/unrarsrc-3.9.4.tar.gz
######################################################################## 100.0%
==> g++ -O4 -march=core2 -mmmx -msse3 -w -pipe -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE all.cpp -o unrar
/usr/local/Cellar/unrar/3.9.4: 3 files, 320K, built in 13 seconds

可以看到,unrar 被安装到了 /usr/local/Cellar/unrar/3.9.4 这个目录下,但这样我们访问起来显然很不方便,所以 Homebrew 会在 /usr/local/bin 下面创建到 unrar 程序的符号链接,如果安装的是库之类的,也会对应在 /usr/local/lib 这样的目录下创建符号链接。所以这是一套类似 GoboLinux 的软件管理方式。

安装后就可以用 list 命令列出:

$ brew list
pkg-config  unrar

更新

如果用了一段时间,需要更新同步上游的 Formula,可以简单地:

$ brew update
From git://github.com/mxcl/homebrew
 * branch            master     -> FETCH_HEAD
Updated Homebrew from 60600885 to 60600885.
No formulae were updated.

Homebrew 会通过 git 完成同步。

总结起来,Homebrew 是一套很有新意的软件包管理工具,虽然它的更新及时程度还有待考验,但至少在目前还是比较令我满意的解决方案。

Mac OS X 视频解码技术之现状

最近,Adobe 在 Flash Player 10.1 的 release notes 中这样写到:

In Flash Player 10.1, H.264 hardware acceleration is not supported under Linux and Mac OS. Linux currently lacks a developed standard API that supports H.264 hardware video decoding, and Mac OS X does not expose access to the required APIs. We will continue to evaluate adding the feature to Linux and Mac OS in future releases.

然而实际上 Linux 下已经有了比较完善的 VDPAU API 用于视频硬件解码 — ffmpeg 和 mplayer 都对它有很好的支持。Mac OS X 呢?让我们罗列一下事实:

  • 这里说的解码 — 主要是指视频,尤其是 H.264 视频的解码,因为音频解码功能需要的资源较少,最耗资源的就是 H.264 解码。
  • 对于有 nVIDIA 9400M 显卡的机器,Snow Leopard 的 QuickTime X 支持使用未公开的接口来硬件解码特定 profile (low, standard) 的 H.264 视频,这个兼容性虽然没有人详细测试过,但可以认为是比较有限的。QuickTime X 最大的限制是限死了对封装 (container) 格式的支持,在它支持的封装格式中,能使用 H.264 视频编码也就是 mp4 和 mov 两种,而在电脑上观看的高清视频大部分是 mkv 封装的。
  • 传统的 QuickTime 7 (在 Snow Leopard 中仍然可用) 虽然可以通过 Perian 支持更多的格式,没有见到具体的报告讨论 QuickTime 7 能否使用 Apple 的 H.264 硬解码模块 (AppleVAH264HW.component),Perian 社区的讨论也没提到能否利用这个模块来解码 H.264,同时用 ffmpeg 来解析 mkv 封装格式。
  • 因为 Mac OS X 下所有显卡的驱动都是通过 Apple 分发的 — 显卡厂商自己没有发布过这些驱动,也没有权力提供用户空间的库来调用自己显卡中的硬件解码 API (实际上这显然还是得内核支持的),如果 Apple 不公开视频硬解码 API,造成的结果就是没有一个第三方的应用程序能利用 Mac 上完全充足的硬件 (9400M, 9600M 等等) 来解码 H.264 视频。
  • 除了硬件解码之外的方案还有纯软件解码和基于 GPGPU 的方案,前者,Mac OS X 下可用的是 ffmpeg 的 H.264 解码功能,mplayer, Perian, Movist 等播放器用的都是它,还有一种是 CoreAVC 开发者的 CorePlayer Pro,这是一个封闭的播放器,在 Mac 上不提供单独的解码器;至于 GPGPU 方案,市场上现有的只有 CoreAVC 基于 CUDA 的解码技术,他们确实有计划开发基于 OpenCL 的解码,但看起来仍然是一个很漫长的周期,而其他开发者因为缺乏足够的经验,很难涉足这个领域 — 没错,视频编解码的水很深。

以上这些事实造成的恶果是,Mac 用户空守着性能充足的显卡,在 OS X 上播放起 1080p 视频时往往 CPU 占用率在 100% 以上,播放高码率的 720p 视频也能到 70% ~ 80%。如果用了硬件解码,CPU 占用率会在 5% 以下,而基于 CUDA 的方案占用率也只有 10% 左右。

那我们能做什么呢?不管出于减少能耗还是减少机器发热的角度,都很有必要向 Apple 提交 bug 报告,要求提供公开的 H.264 硬解码 API。