前回の
高度なことをPerlだけで行うテクニック
Perlの基本的な機能をうまく活用するだけでも高度な処理を行えます。組込み関数はもちろん、
これらの基本的な道具は書籍やWeb上に情報がすでに多くあるので、
任意のシステムコールを呼び出す
組込み関数のsyscall
を使うと、man syscalls
などで見られます。Perlを含む多くのプログラミング言語において、
そのため、syscall
関数を利用しなければ目的を達成できない場合は多くはありません。また、syscall
関数を使うとよいでしょう。
システムコール番号を得る
syscall
関数によるシステムコールの呼び出しには、linux-libc-dev
パッケージが必要です。次のようにインストールします。
$ apt-get install linux-libc-dev
C言語では、sys/
を読み込めば#define
によって定義されたSYS_
で始まるシステムコール番号を示す定数を得られます。この定義をたどれば、
Perlからシステムコール番号をロードできるようにする
Perlでシステムコール番号を扱うにはC言語のヘッダファイルをPerlから読める形式に変換する方法が一般的です。そのためには、h2ph
コマンドを使います。詳細な使い方はperldoc h2ph
を参照してください。
sys/syscall.hのロードできるディレクトリに移動して実行する
$ cd /usr/include/x86_64-linux-gnu # 環境による
$ h2ph -d path/to/lib -a -l sys/syscall.h
成功すれば、-d
オプションで指定したパスに.ph
拡張子のファイルが生成されます。たとえば、sys/
であればそれからsys/
が生成されます。これをrequire
で読み込めば、#defined
によって定義された定数をPerlからも使えます。
もちろん、
sendfile
システムコールで高速にファイルをコピーする
実際にsyscall
関数を使って、sendfile
システムコールを利用して高速にファイルコピーを行う処理を書いてみます。sendfile
は、
Linuxにおいてはすべての入出力をファイルとして扱うため、sendfile
を利用できますSys::Sendfile
などのCPANモジュールでもsendfile
を利用できます。
先述したh2ph
で生成したsys/
を読み込み、SYS_
に続けて引数を渡すことで、sendfile
を呼び出します。
本稿のサンプルコードから重要な箇所のみ抜粋
require 'sys/syscall.ph';
my $size = -s $in_fh;
my $ret = syscall(
SYS_sendfile(), # システムコール番号
fileno($out_fh), # システムコールの引数
fileno($in_fh), # (同上)
0, # (同上)
$size, # (同上)
);
sendfile
にはファイルディスクリプタfileno
関数を用いてファイルハンドルからそれを得る必要があります。syscall
関数は数値と文字列を扱えるので、
定型的な処理を高速化する
ちょっとした問題解決のためにスクリプトを書いていると、
低レベルなファイルAPIを使う
Perlでは高レベルなファイルの取り扱いは行単位で行えますが、
例として、seek
を使います。また、
たとえば、
# SEEK_ENDなどの定数をインポート
use POSIX qw/:fcntl_h/;
# 末尾から1,024バイト前の位置に移動
seek $fh, -1024, SEEK_END
or die "failed to seek: $!";
# $bufに1,024バイト分読み込む
read $fh, my $buf, 1024
or die "failed to read: $!";
それなりに十分なサイズを読み込んで不要な部分を捨てれば数行分は得られるので、
seek
などを応用すると、Search::Dict
モジュールは、
constant
プラグマと定数畳み込みで処理を最適化する
Perlはインタプリタ言語ですが、3+4
と定数だけで書かれているPerlコードは、7
に畳み込まれます。
定数だけで完結する分岐も、
常に真なのでifが消えてブロックの内側の処理のみ残る
if (1) {
print "Always run it!";
}
常に偽なのでifがブロックごと消える
if (0) {
print "Remove it!";
}
これが定数畳み込みで最適化されると、
print "Always run it!";
このように、
この定数は、constant
プラグマで作った定数にも有効です。constant
プラグマは定数
効果的な場面は限定的ですが、constant
プラグマで定数にすることで、
use constant DEBUG => $ENV{DEBUG};
# 環境変数の値に応じて定数畳み込みが行われる
printf STDERR "DEBUG: ..." if DEBUG;
これを応用すれば、
まとめ
Perlの基本機能をうまく活用することで、
さて、
本誌最新号をチェック!
WEB+DB PRESS Vol.130
2022年8月24日発売
B5判/168ページ
定価1,628円
(本体1,480円+税10%)
ISBN978-4-297-13000-8
- 特集1
イミュータブルデータモデルで始める
実践データモデリング
業務の複雑さをシンプルに表現! - 特集2
いまはじめるFlutter
iOS/Android両対応アプリを開発してみよう - 特集3
作って学ぶWeb3
ブロックチェーン、スマートコントラクト、NFT