Perl Hackers Hub

第53回 Cを用いたPerl拡張入門―Inline::Cで体験してみよう!(3)

この記事を読むのに必要な時間:およそ 4.5 分

CでPerlの処理性能を補う

(1)の冒頭にて,Cを用いてPerlを拡張する利点の一つとして,ホットスポットの改善を挙げました。そこではPerlゆえに処理速度が遅くなるケースがあると紹介しましたが,それはいったいどんな処理なのでしょうか。実際にCとPerlのコードを計測,比較を行って調べてみましょう。

Cと比べたPerlの数値計算

PerlとInline::Cそれぞれで記述したaddの処理速度にどれだけ差があるかを計測するために,次のコードを記述します。

計測に用いたPerlのバージョンは5.28.0です。

use Inline C;
use Benchmark 'cmpthese';

my $n = 1;
cmpthese 0, {
    'PP'        => sub {
            pp_add($n, $n);
    },
    'Inline::C' => sub {
            c_add($n, $n);
    },
};

sub pp_add {
    return $_[0] + $_[1];
}
__DATA__
__C__
int c_add(int x, int y) {
    return x + y;
}

計測するために,Benchmarkモジュールのcmptheseを使います。cmptheseを使うと計測の結果がチャート形式で出力されます。

実行結果は次のとおりになりました。

                Rate       PP Inline::C
PP         5434403/s       --      -63%
Inline::C 14530606/s     167%        --

Rateの列は実行速度を表しています。値が大きくなればなるほど1秒で実行できる回数が多くなります。PP注2とInline::Cの列では,お互いの実行速度を比べてどれくらいの割合の速度なのかを示しています。Cで実行した場合,Perlより約1.7倍も速くなっていることが読み取れます。

注2)
Pure Perlの略です。Cを利用せず,純粋にPerlだけで記述されているという意味です。

Cと比べたPerlの文字列連結

では,文字列を連結する処理はどれくらいの差が生じるでしょうか。計測するために次のコードを記述しました。

use Inline C;
use Benchmark 'cmpthese';

my $a = "Hello";
my $b = ", World";

cmpthese 0, {
    'PP' => sub {
            pp_cat($a, $b);
    },
    'Inline::C' => sub {
            c_cat($a, $b);
    },
};
sub pp_cat {
    return $_[0] . $_[1];
}
__DATA__
__C__
char* c_cat(char* x, char* y) {
    /* 誌面の都合上エラー処理を省略します */
    char* tmp;
    size_t x_len, y_len;
    x_len = strlen(x);
    y_len = strlen(y);
    tmp = (char*)malloc(x_len + y_len); ―(1)
    memcpy(tmp, x, x_len); ―(2)
    memcpy(tmp + x_len, y, y_len); ―(3)
    return tmp;
}

記述したCのコードは次の挙動をします。

  • (1)malloc関数で文字列連結した結果を保存するために,メモリ領域を確保する
  • (2)確保した領域へ変数xに格納された文字列をセットする
  • (3)セットした文字列の最後から続けて,変数yに格納された文字列をセットする

Cでは文字列連結をするためにメモリを操作するコードを記述する必要があります。Perlの場合は内部で複雑なメモリ操作を代わりに行ってくれるので,シンプルに記述ができます。

計測結果は次のとおりになりました。

               Rate Inline::C     PP
PP        5596158/s        9%     --
Inline::C 5134979/s        --     -8%

ほとんど差はありませんが,今度はPerlが上回りました。何度かスクリプトを実行するとCが上回る場合もあります。しかし,どの結果もほぼ同じになるでしょう。

Perlはたった1行のコードでCとほぼ同じ速度で処理ができました。Perlが古くから文字列を処理するための言語だと言われ続けたことに納得できる結果です。

Cで書くべき勘どころ

これらの結果から,数値計算を行う場合,Cで記述するとPerlとほぼ同じ量で記述でき,処理速度が向上することに気付くでしょう。しかし文字列処理を行うコードは,Perlのほうがシンプルに記述できるうえにCとほぼ同等な速度で処理ができます。

PerlとCを比較すると,Perlのほうが直感的に記述でき,より安全に処理を行える利点があります。でもすべての処理を高速に行えるわけではありません。対照的にCで記述すると,高速に処理を行えますが,自由度が高すぎるゆえにメモリを扱う処理ではコードも複雑になります。

簡単にホットスポットを改善したい場合,複雑なメモリ操作を行わない,たくさんの数値計算を行う処理をCで記述するとよいでしょう。実際にたくさんの数値計算を行う例として,前節の最大値,最小値,平均値を求める処理を計測した結果,約11倍の速度向上を図れました。

               Rate        PP Inline::C
PP         335081/s        --      -92%
Inline::C 4186717/s     1149%        --

XSで書かれたPerlモジュール

CPANをより使いやすくしたWebサービスであるMetaCPANでは,XSを用いたモジュールが公開されています。その中から,XSで作成されているからこそ本領発揮しているモジュールを紹介します。

Time::Moment─⁠─XSによる高速化

Time::Momentは,日付や時間周りを操作できるモジュールです。XSで記述されていることから,現在時刻を取得するnow()は同様の機能を持つ組込み関数のlocaltime()より高速に呼び出せます

Encode─⁠─Perlが苦手とするバイト操作のサポート

Encodeは,Perlで扱う文字列のエンコーディングに用いられるモジュールです。モジュール内部ではPerlが苦手とするバイト列の操作を行っています。

JSON::XS─⁠─Perlの型へ高速変換

JSON::XSは,JSONのエンコードやデコードをXSを使って高速に行います。JSONモジュールを利用していれば基本的に裏側でJSON::XSを呼び出してくれますが,もし利用できなかった場合注3は,Perlだけで記述されたJSON::PPを呼び出します。

注3)
XSモジュールのインストールは対応したCのコンパイラが必要なので,インストールできない環境がまれにあります。

autobox─⁠─PerlのAPIを使った構文操作

autoboxは,PerlのAPIをハックすることでPerlの構文をRubyライクに変更できるモジュールです。

参考ドキュメント

もっと深掘りしたい方のために,いくつか読むべきドキュメントを挙げておきます。

perlguts─⁠─Perl APIへの入り口

perlgutsは,Perlインタプリタへ必要な基礎知識について解説しているドキュメントです。Perl APIの使い方も把握できます。

perlxstut─⁠─XSのチュートリアル

perlxstutは,XSチュートリアルのドキュメントです。

perlclib─⁠─PerlでC標準ライブラリをどう扱うか

perlclibは,Cの標準ライブラリを使う際に注意すべき点や,それに代わって使うべきAPIが記述されています。

illguts─⁠─Perl内部の説明書

illgutsは,Perlの内部構造を画像で解説しているPDFドキュメントです。

CによるPerl拡張入門(α)─⁠─日本語によるXSの解説サイト

CによるPerl拡張入門(α)は,XSに関する知見が日本語でとてもわかりやすくまとめられています。

まとめ

今回は,Cを用いてPerlの拡張を行う方法として,Inline::Cを用いた方法を解説しました。Inline::Cを用いたPerlの拡張で得た知識は,今後XSにも挑戦したい方々への助けになるでしょう。ぜひ,Cを用いたPerlの拡張に挑戦してください。

さて,次回の執筆者はわいとんさんで,テーマは「サーバレスでもPerl」です。お楽しみに。

WEB+DB PRESS

本誌最新号をチェック!
WEB+DB PRESS Vol.112

2019年8月24日発売
B5判/168ページ
定価(本体1,480円+税)
ISBN978-4-297-10787-1

  • 特集1
    React/Vue.jsで実践!
    コンポーネント設計
    モダンフロントエンドの構造化と分割の新提案
  • 特集2
    RDBMS徹底比較
    PostgreSQL,MySQL,SQL Server,Oracle Database
  • 特集3
    実践Scala
    オブジェクト指向×関数型
  • 一般記事
    自作キーボードのススメ
    デザイン,配列,打鍵感……自由自在

著者プロフィール

上川慶(かみかわけい)

1995年,沖縄生まれPerl育ち。2018年に株式会社メルカリへ新卒入社し,現在はメルカリのマイクロサービスを開発している。大好きな言語はPerlとGoであり,特性としてよく沖縄に帰ることが多い。

学生時代は沖縄のPerlユーザーコミュニティであるOkinawa.pmを運営しており,一応今でも中の人の一人。