前回の
高度なことを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

