FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
man cp(1) での -z オプションに関する説明:
-z Fast Copy. cp will reflink the source and destination files. For more information, see the reflink(3C) man page.
man reflink(3C) での関数宣言:
int reflink(const char *path1, const char *path2, int preserve); int reflinkat(int fd1, const char *path1, int fd2, const char *path2, int preserve, int flags);
表記上 3C = C Library 分類扱いになっているが、 実際には引数を積んで reflinkat システムコールを呼び出しているだけ
以下の方法で確認済み
資料を書いていて、ふと思ったのですが……
引数を「積む」って「スタック渡し」前提な表現ですよね
SPARC は勿論 x64 (AMD64) も一定数以下ならレジスタ渡し
そのうちに引数を「詰める」的な表現になるのかな? (笑)
ところで ARM の ABI ってどうでしたっけ?
富嶽とか次期 Apple 製デスクトップとか
HPCシステムズ株式会社は、 富嶽と同じ ARM V8-A ベースの A64FX CPU (48 core) 搭載スパコン PRIMEHPC FX700 を税別 4,155,300円 の キャンペーン特別価格 で発売中
https://pc.watch.impress.co.jp/docs/news/yajiuma/1261462.html
富士通の PRIMEHPC FX700 (ノードあたり "約 4,000,000 円" ) の販売条件は 2ノードから なので、
1 ノード構成で売ってくれるのは安いのかも
10 年経過すると SPARC Enterprise M4000 (約6,000,000円) もヤフオクで捨て値状態
あと 10 年待てば PRIMEHPC FX700 も投げ売りされているのかなぁ
そういえば京の一般モデル PRIMEHPC FX10/FX100 は中古の話を聞かないなぁ
と思ったら、そもそも FX10/FX100 は ラック単位の販売 でした……
それはさておき
reflink(path1, path2, preserve)
イコール
reflinkat(AT_FDCWD, path1, AT_FDCWD, path2, preserve, AT_SYMLINK_FOLLOW)
オンラインマニュアル上は preserve 引数の仕様に関する言及が一切無いが、 cp(1) コマンドに対して各種属性維持 (preserve) を指示する -p オプション指定時に 1 が指定されることを truss で確認済み
int call_reflink(const char* src, const char* dst){ int preserve = 0; if (reflink(src, dst, preserve)){ return -1 } return 0; }
単独実行可能な最小実装の、コンパイル可能なコードは以下の通り。
#include <errno.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> int main(int argc, const char* argv[]) { int preserve = 0; if (reflink(argv[1], argv[2], preserve)){ fprintf(stderr, "%s: %s: from %s to %s", argv[0], strerror(errno), argv[1], argv[2]); exit(1); } return 0; }
reflink(3C) による CoW 併用複製は ZFS の De-Duplication 機能で実現される
但し、当該 ZFS データセットにおける dedup 機能の有効/無効とは 無関係
※ 実際の挙動をデモ
参考までに、以前動作確認した際の出力を貼っておきます。
$ df . /export/home/fujiwara(rpool/export/home/fujiwara):15871256 blocks 15871256 files $ zfs get all rpool/export/home/fujiwara | grep -i dedup rpool/export/home/fujiwara dedup off default $ sudo zdb -DDDd rpool All DDTs are empty Dataset rpool [ZPL], ID 18, cr_txg 1, 4.32M, 274 objects $ ./cp_reflink 1M_file 1M_reflink $ zfs get all rpool/export/home/fujiwara | grep -i dedup rpool/export/home/fujiwara dedup off default $ sudo zdb -DDDd rpool DDT-XT: 1 entries, size 2048 on disk, 18446744073709543424 in core bucket allocated referenced ______ ______________________________ ______________________________ refcnt blocks LSIZE PSIZE DSIZE blocks LSIZE PSIZE DSIZE ------ ------ ----- ----- ----- ------ ----- ----- ----- 16 1 128K 128K 128K 16 2M 2M 2M DDT histogram (aggregated over all DDTs): bucket allocated referenced ______ ______________________________ ______________________________ refcnt blocks LSIZE PSIZE DSIZE blocks LSIZE PSIZE DSIZE ------ ------ ----- ----- ----- ------ ----- ----- ----- 16 1 128K 128K 128K 16 2M 2M 2M Total 1 128K 128K 128K 16 2M 2M 2M dedup = 16.00, compress = 1.00, copies = 1.00, dedup * compress / copies = 16.00 Dataset rpool [ZPL], ID 18, cr_txg 1, 4.32M, 274 objects $
複製元ファイル 1M_file は mkfile(1) で作成した 0 フィルファイルですが、 128KB の同一内容ブロック 8 個で 1MB を構成していることが確認できます。
reflink(3C) による CoW 併用複製先 1M_reflink も同内容の 128K ブロック 8 個で構成されるため、 128KB の 0 フィルブロックへの参照が合計 16 になります。
ちなみに man(1) でのオンラインマニュアルには zdb(8) の詳細が一切記載されていないのですが、 zdb -? で使用可能オプション一覧を含む簡易ヘルプが表示されます。
-D は dedup statistic, -d は dataset の表示指示。
オプションを繰り返し指定することで、 表示内容が詳細化されます。
オンラインマニュアル曰く -z は Fast Copy のためのオプション
しかし、実際にはそれほど fast ではない
少なくとも 初回 適用時に関しては very slow
初回 reflink(3C) のカーネル内処理手順の推測
reflink(3C) の初回適用と2回目以降の適用の差:
※ reflink(3C) と dd(1) での実行速度の比較デモ
参考までに、以前動作確認した際の出力を貼っておきます。
cp_reflink コマンドは、 "単独実行可能な最小実装のコンパイル可能なコード" から生成された実行バイナリです。
以降で例示する性能計測結果は、 いずれも VirtualBox ゲスト上における計測値なので、 傾向は見て取れるものの、 値そのものの厳密性には欠けている点にご注意ください。
$ time dd if=1M_file of=1M_dd bs=1k 1024+0 records in 1024+0 records out real 0m0.042s user 0m0.005s sys 0m0.039s $ time ./cp_reflink 1M_file 1M_reflink1 real 0m0.218s user 0m0.001s sys 0m0.008s $
dd(1) は愚直に read/write を繰り返すが -z なしの cp(1) は:
参考までに、以前動作確認した際の出力を貼っておきます。
$ time dd if=1M_file of=1M_dd bs=1k 1024+0 records in 1024+0 records out real 0m0.042s user 0m0.005s sys 0m0.039s $ time cp 1M_file 1M_cp real 0m0.015s user 0m0.002s sys 0m0.012s $
なお対象ファイルサイズが 64MB の場合、 1K バッファでのループによる複製に対してなら、 初回の reflink(3C) 適用でも十分高速であることが確認できます。
$ time dd if=64M_file of=64M_dd bs=1k 65536+0 records in 65536+0 records out real 0m1.664s user 0m0.215s sys 0m1.393s $ time ./cp_reflink 64M_file 64M_reflink1 real 0m0.925s user 0m0.001s sys 0m0.025s $ time ./cp_reflink 64M_file 64M_reflink2 real 0m0.045s user 0m0.001s sys 0m0.011s $ time cp 64M_file 64M_cp real 0m0.219s user 0m0.001s sys 0m0.216s $
流石に初回 reflink(3C) こそ mmap(2) 併用で高速化している cp(1) には及びませんが、 2回目以降の reflink(3C) は cp(1) よりも大幅に高速に複製できることが確認できます。
一番わかり易いユースケース
変更機会の少ない (or 無い) 大容量ファイルを多数複製
Oracle Cluster File System (OCFS) の reflink 機能に関するブログエントリ (2009-05-04) 曰く:
The reason we implemented reflink is for Oracle VM
「大容量なベースファイルを元に、小規模改変された多数のファイルが存在」 するようなケースには reflink(3C) がハマりそう。
最近思い付いたユースケース
そこそこブロック消費している大規模 sparse ファイルの複製
SEEK_HOLE について掘り下げてみる
Jeff Bonwick の 2005 年のコラム 曰く:
As part of the ZFS project, we introduced two general extensions to lseek(2): SEEK_HOLE and SEEK_DATA.
(snip)
At this writing, SEEK_HOLE and SEEK_DATA are Solaris-specific. I encourage (implore? beg?) other operating systems to adopt these lseek(2) extensions verbatim (100% tax-free) so that sparse file navigation becomes a ubiquitous feature that every backup and archiving program can rely on. It's long overdue.
Linux (4.15.0/Ubuntu 18.04.4 LTS) の man lseek(2) 曰く:
Since version 3.1 (注釈: 2011-10-25 リリース), Linux supports the following additional values (注釈: SEEK_DATA と SEEK_HOLE のこと) for whence:
(snip)
SEEK_DATA and SEEK_HOLE are nonstandard extensions also present in Solaris, FreeBSD, and DragonFly BSD; they are proposed for inclusion in the next POSIX revision (Issue 8).
ls 本 を書いた 2013 年には Linux でも SEEK_DATA や SEEK_HOLE が利用可能になっていた筈なのに、 書籍では "Linux は ioctl(FS_IOC_FIEMAP) でブロック割り当て状況取得" と書いてしまってる…… orz
reflink 成立の歴史的経緯に関して掘り下げてみる
Linux における reflink(2) の追加提案に関する議論が 2009-05 頃
Oracle Cluster File System (OCFS):
2009 年秋には GNU coreutils の cp(1) で "reflink 機能" が利用可能になっていた旨を 2011-09-31 の Oracle のブログエントリ が言及
For Btrfs, you can invoke this feature by using the cp(1) utility with the --reflink option, which was added to the GNU coreutils in version 7.5 (released in Aug. 2009)
ブログエントリ公開時点での OCFS2 向け "reflink 機能" に対する GNU coreutils cp(1) の対応状況は:
Adding support for the reflink implementation of OCFS2 to cp still seems to be under development.
さてここで再確認
頼みますよ Oracle さん …… orz
ほぼ終わりですが……
以下のようなユースケースでは reflink(3C) の利用がハマる筈
そんなあなたに reflink(3C) !!!
ご清聴ありがとうございました