Perl Hackers Hub
第64回 少しマニアックなPerlのテクニック―特殊変数,低レベルの標準関数を使いこなす(2)
前回の
高度なことをPerlだけで行うテクニック
Perlの基本的な機能をうまく活用するだけでも高度な処理を行えます。組込み関数はもちろん,
これらの基本的な道具は書籍やWeb上に情報がすでに多くあるので,
- 注1)
- CentOSではコアモジュールは
perl
とは別のperl-core
という別パッケージでインストールする必要があります。
任意のシステムコールを呼び出す
組込み関数の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
関数は数値と文字列を扱えるので,
- 注2)
- C言語の標準ライブラリです。
- 注3)
- 実用上,
sendfile
システムコールはファイルシステムなどによっては問題が発生する場合があるので,慎重に利用してください。 - 注4)
- オープン済みのファイルを識別するための識別子となる数値です。
定型的な処理を高速化する
ちょっとした問題解決のためにスクリプトを書いていると,
低レベルなファイル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;
これを応用すれば,
- 注5)
- 実際には定数サブルーチンと呼ばれる引数を取らない特殊なサブルーチンになります。
まとめ
Perlの基本機能をうまく活用することで,
さて,
本誌最新号をチェック!
WEB+DB PRESS Vol.122
2021年4月24日発売
B5判/168ページ
定価1,628円
(本体1,480円+税10%)
ISBN978-4-297-12119-8
- 特集1
上から下まで全レイヤ解説! 複雑化した世界を体系的に学ぶ
Web技術総整理 - 特集2
新バージョン登場! PythonによるWeb開発の基本
はじめてのDjango - 特集3
Rustで実装!
作って学ぶRDBMSのしくみ
バックナンバー
Perl Hackers Hub
- 第66回 モジュールによる時間の多様な取り扱い(1)
- 第65回 依存モジュールの更新 ―update-cpanfile,GitHub Actionsで実現!(2)
- 第65回 依存モジュールの更新 ―update-cpanfile,GitHub Actionsで実現!(1)
- 第64回 少しマニアックなPerlのテクニック―特殊変数,低レベルの標準関数を使いこなす(2)
- 第64回 少しマニアックなPerlのテクニック―特殊変数,低レベルの標準関数を使いこなす(1)
- 第63回 PPIとPerl::Tidyを組み合わせて作るコード整形ツール(2)
- 第63回 PPIとPerl::Tidyを組み合わせて作るコード整形ツール(1)
- 第62回 Perl歴史散策 ―インタプリタの実装と,構文の進化をたどる(3)
- 第62回 Perl歴史散策 ―インタプリタの実装と,構文の進化をたどる(2)
- 第62回 Perl歴史散策 ―インタプリタの実装と,構文の進化をたどる(1)