Azure DocumentDB が Azure Cosmos DB に名称変更したようです

Azure Functionsと共に利用すると便利なフルマネージドNoSQLサービスである「Azure DocumentDB」が名称変更して「Azure Cosmon DB」になったようです。

以下、Azure Portal上の説明を引用します。

Azure Cosmos DB is a fully managed, globally-distributed, horizontally scalable in storage and throughput, multi-model database service backed up by comprehensive SLAs. Azure Cosmos DB is the next generation of Azure DocumentDB. Cosmos DB was built from the ground up with global distribution and horizontal scale at its core – it offers turn-key global distribution across any number of Azure regions by transparently scaling and replicating your data wherever your users are. You can elastically scale throughput and storage worldwide and pay only for the throughput and storage you need. Cosmos DB guarantees single-digit millisecond latencies at the 99th percentile anywhere in the world, offers multiple well-defined consistency models to fine-tune for performance and guaranteed high availability with multi-homing capabilities – all backed by industry leading service level agreements (SLAs).

Cosmos DB is truly schema-agnostic – it automatically indexes all the data without requiring you to deal with schema and index management. Cosmos DB is multi-model – it natively supports document, key-value, graph and columnar data models. With Cosmos DB, you can access your data using NoSQL APIs of your choice – DocumentDB SQL (document), MongoDB (document), Azure Table Storage (key-value), and Gremlin (graph), are all natively supported. Cosmos DB is a fully managed, enterprise ready and trustworthy service. All your data is fully and transparently encrypted and secure by default. Cosmos DB is ISO, FedRAMP, EU, HIPAA, and PCI compliant as well.

委細について、本日深夜(日本時間)に開催される Build 2017 での発表が気になるところです。

なお、ライブビューイングがストリーミング配信される様ですので、そちらも見ておくと良いと思われます。

Azure Functionsのパフォーマンスに関する実験

昨日、ほんの出来心でMojoアプリをAzure Functionsで動かせたらどうだろうなどと考えてしまい、2時間弱くらいでMojo::Server::AzureFunctionsというものを作りました。

Mojo::Server::AzureFunctionsを作って使うまで

もともとAzure FunctionsのHTTP TriggerとHTTP output bindingをハンドリングするためのアダプタとして作ろうとあらかじめ決めており、Functions自体はリクエストごとにプロセスを起動する動作モデルであるため、これはCGIと同様であるとみなすことができました。

従って、既存のMojo::Server::CGIを参考に、環境変数の解析と入出力の調整だけでどうにかMojoアプリをAzure Functionsで動作させることに成功しました。

動作確認したソースコードはこちらのレポジトリに上げてあります。

苦しかった点

Mojoでは $c->render(json => {...}); とすることでJSONによるレスポンスを行うことができます。

ところが、Mojo::ServerMojo::Messageではrenderメソッドの第1引数に渡された値を一切検出できません。

そのため、レスポンスのContent-TypeやContentそのものを検証して、ContentがJSON Stringであるかどうかを検証する必要があり、そうしなければAzure FunctionsのHTTP output bindingでは単なる文字列として判別されてしまい、<string>{"message": "hello"}</string> という感じの残念なレスポンスを返してしまうことになるのです。

これを回避するコードがココでして、Contentが{で始まっていれば多分JSONだろうとみなし、evalで囲った中でdecode_jsonをかけるという非常に野蛮な方法で対応したことがお分かり頂けると思います。

実際に動作させてみた

さて、Azure Functionsにデプロイして実際に動作させてみた結果をお見せします。

Azure Functionsの大変便利な機能の一つとして、関数ごとにレスポンスにかかった時間のモニターをすることができる、その名もずばり「モニター」という機能があります。今回はそこで記録された動作速度を見ていただきたいと思います。

一番右側の丸括弧で囲われた値がレスポンスまでにかかった時間です。平均およそ23秒ほどでしょうか。これは遅いですね!

なぜ遅いか?

実は遅い理由は非常に単純でして、Mojoliciousをuseするコストがあまりにも高いことが挙げられます。これはMojoliciousが悪いわけでもありませんし、もちろんAzure Functionsが悪いわけでもありません。

どういうことかというと、そもそもAzure Functionsには非常にシンプルなものがデプロイされることが期待されています。Webアプリケーションフレームワークのような複雑なコードは毎回ロードするだけでもコストがかかりますので、当然のようにレスポンスまで時間がかかるのです。

証拠

例えば関数内のrun.shの内容が以下のように非常に単純なものだったとします。

perl -MJSON::PP -le 'print encode_json({message => "Hello, World!"})' > $res

その場合は、以下のようなレスポンス速度を実現することができるのです。

概ね0.5〜2秒程度でレスポンスを返すことができています。必要十分な速度と言えますね。

まとめ

Azure FunctionsでMojoアプリを動作可能にするMojo::Server::AzureFunctionsを作って、レスポンス速度を確かめてみました。

その結果わかったのは、Azure Functionsには複雑なコードをデプロイすべきではなく、徹底的にシンプルなコードをデプロイすることでその真価を発揮することができる、ということです。その先にはほぼ無限のスケーラビリティとメンテナンスフリーにほど近いシステムの実現があると私は思います。

Sisimai ♥ Azure Functions

先日のHokkaido.pmの帰りに時間が余ってしまったので、新千歳空港のロビーでダラダラしながら、SisimaiをAzure Functionsで動作させてみました。

Azure FunctionsでPerlが動く

Azure Functions上では、制約がありながらもPerlが稼働します。これは既にHokkaido.pmで発表した通りです。

Sisimaiとは

Sisimai

Sisimaiとは、バウンスメールの解析を行い、その情報を取得したりJSONに変換するためのライブラリです。
Perl版とRuby版が二条項BSDライセンスにて提供されており、GitHub上で開発されています。

本エントリではこれ以降、単にSisimaiと表記する場合は、特筆がない限りPerl版のp5-Sisimaiを指すものとします。

小さく堅牢なSisimaiに注目

ちょうどHokkaido.pmにSisimai作者のazumakuniyukiさんがいましたので、Sisimaiの依存モジュールについて聞いてみました。

わいとん:Sisimaiって依存モジュールどのくらいあるんです?

azumakuniyukiさん:確かJSONとClass::Accessor::Liteくらいだったんちゃうかなぁ・・・「小さく堅牢に」っちゅうのを心がけてるんで、そのくらいやったと思います。

実際metacpanのdependencyを見ても非常に依存が少なく、またXSを利用したモジュールに依存していません。Azure Functionsで稼働させる対象としては適任であると考えられます。

実際に作った

実は去年の夏にSisimaiに関連したエントリを公開しております。

当時はSisimaiが稼働するDockerコンテナを作りましたが、今年はSisimaiが稼働するサーバレスシステムを作りました。

Azure Portalにアクセスできる状態なら、Deploy to Azureボタンを押すだけでデプロイできるようにしてあります。

デプロイが終わったら、適宜バウンスメールが含まれたMailbox形式のファイルを以下のようにPOSTしてみてください。

ENDPOINT_URL="https://yourfuncname.azurewebsites.net/api/sisimai?code=..."
curl -X POST 
     -H "Content-Type: text/plain" 
     --data-binary @path/to/mailbox 
     $ENDPOINT_URL

以下が動作イメージです。

image

作る上でキモとなった点

Sisimaiをどうやってインストールするか

まずローカル環境でcpanfileを作って以下のような内容とします。

requires 'Sisimai';

あとはcpm installするとlocal以下にSisimaiとその依存モジュールが入ってきます。

これだけです。

それらをどうやってアップロードするのか

基本的にはGitHubにあがっていれば、Functionsにそのまま落としてくることが可能ですので、ひとまずGitHubにおきました。

Azure Functions の継続的なデプロイというエントリにそのやり方が書いてありますので、開発中はこの方法でデプロイ済みのAzure Functionsにファイルをアップロードしていきました。

ARM Template

これが一番難しかったのですが、以下のドキュメントを読みながら地道に作っていきました。

まとめ

小さく堅牢に作られたSisimaiをAzure Functionsにデプロイする方法を自動化し、数クリックでデプロイできるようにしました。またそのデプロイボタンを一般公開しました。

エラーメールの解析をサービスとして展開できるので、ぜひお試しいただけると幸いです。

Hokkaido.pm 13でサーバレスアーキテクチャの話をしてきた

昨日札幌で開催されたHokkaido.pm 13に参加し、Azure Functionsを使ったサーバレスアーキテクチャについて発表してきました。

大まかに以下の項目について話しました。

  • サーバレスアーキテクチャの概要
  • 歴史と現状
  • Azure Functionsを使う理由
  • Azure FunctionsとPerl
  • 実例とデモ

持参した回線の調子がよろしくなく、20分ほどの時間オーバーという大失態をしでかしてしまいましたが、温かく見守ってくださった皆様には、この場を借りて改めて御礼申し上げます。ありがとうございました。

落穂拾い

発表では説明しきれなかったことをざっくりとここでフォローアップしていきたいと思います。

Function Appへの関数のデプロイ

基本的にはAzure App Serviceのデプロイ方法に沿ってデプロイします。
Azure App Service へのアプリのデプロイという公式のエントリがありますので、詳しくはそちらを参照していただきたいと思います。
なお、ざっくりではありますが、以下のデプロイ方法が利用可能です。

  • FTP
  • FTPS
  • git(ローカルgitという方法で、App Service側にremote reposが作られる方法)
  • github
  • OneDrive
  • Dropbox
  • BitBucket
  • 外部gitレポジトリ(公開レポジトリにpushするとデプロイされる)

テスト手法について

基本的にFunctionsの環境を模倣するものはFunctions以外にありませんので、Azure上にテスト用のFunctionsを起こしておくのが良いと思います。
Functionsのダッシュボードにはテスト用のリクエスト発行をする仕組みが備わっていますので、そちらを使うのがスムーズでしょう。

CIについて

継続テストをもしやるのであれば、上述したテスト手法をガンガン定期的に実施する以外の方法はないと思います。
当然データストアなどへの副作用も発生しますので、その辺を考えて継続テストを行わないといけないです。
そんなわけで、今の時点ではあまり現実的な解は見つけられていませんが、どなたか知見のある方のご意見をいただけると幸甚です。

最後に

私個人としてはPerl5に対して、クラウド向けのグルー言語として、今後もまだまだ利用価値があると考えています。特にPure Perlで記述されたライブラリやロジックは、サーバレスな環境において、大変に便利な存在となり得ると思います。

今回そのことを、北海道という場所で語れたことを非常に嬉しく思っております。主催の永谷氏と参加メンバーの皆さん、そして会場についてのサポートをしてくださったJPAの皆様、本当にありがとうございました。

ブログ名を変更しました。

長いこと “All your bugs are belong to ass” というタイトルでブログを続けてきたのですが、今更ながら「いや長いだろw」などと思い、ブログ名を変更することにしました。

新しいブログ名は"Wyton"(読みはワイトン)です。わいとんが書いてるし、Newt◯nっぽくて、しかも短く覚えやすそうなので、この名前にしました。

ぜひ今後もよろしくお願いします。

Reactive Programmingの波がPerlにもやってきた?

どーも、わいとんです。

遅まきながら、新年おめでとうございます。

さて。近頃、個人的に気になっているCPANモジュールの一つにMoobXというものがあります。

今回は実際にMoobXを使って、PerlでのReactive Programmingを試してみましたので、ご紹介したいと思います。

MoobXについて

MoobXはReactive programming frameworkを標榜しており、JavascriptのMobXにインスパイアされたものであるようです。

本エントリ執筆時点での最新バージョンは0.1.0ですが、作者のYanick Champoux氏によって精力的に開発が継続されており、現在も3つのissueがopenとなっています。

なお、最初のリリースである0.0.1が今年の1/13にリリースされたばかりであり、0.1.0のリリース日も1/14であることから、今後まだまだ仕様が変わる可能性があると思われます。

Reactive Programmingとは

そもそもReactive Programmingとは何なのか、という疑問を持っている方もいらっしゃるかもしれません。

もっともわかりやすいReactive Programmingの例としては、Excelの数式が挙げられます。例えばセルA1に=A2+A3という定義がある場合、A2とA3の値を加算した結果がA1に反映されます。この時、A2の値を修正すると、即座にA1に結果が反映されます。

Excelの場合は、A1に値の関係性を記述することで、A2及びA3の値の変更を伝播させるデータフローが生じます。

このような値の関係性を記述して、値の変更を伝播させる、データの流れに着眼したプログラミングパラダイムのことをReactive Programmingと言うようです。

MoobXを使ってみる

では早速MoobXを使ってReactive Programmingをやってみます。

MoobXはcpanmcpmなどを使ってインストールできます。

cpanm MoobX

実際にMoobXを使った簡単なコードを書いてみました。

use strict;
use warnings;
use MoobX;

observable my $x;
observable my $y;

my $z = observer {$x + ($y * 2)};

$x = 3; $y = 5;
print "$zn";

$y = 2;
print "$zn";

この結果は以下のようになります。

$ perl ./observer.pl
13
7

$yの変化に応じて$zも変化していますね。

もしこれがReactive Programmingでなければ、おそらく以下のような結果になっていたことでしょう。

$ perl ./observer.pl
13
13

MoobXの基礎

MoobXを利用する上で覚えなくてはならないことは2つだけです。

observer

observerには、値の関係性をコードブロック(無名関数)として記述します。

my $z = observer {$x + ($y * 2)};

observable

observableには、observerで言及される対象となる変数定義を記述します。

observable my $x;
observable my $y;

まとめ

PerlにおけるReactive ProgrammingをMoobXで実現できる、ということを紹介しました。

応用することで、様々な有益モジュールの開発に貢献しそうな予感がしますね。

Perlで替え歌エンジンをつくる

※当エントリはPerl入学式 Advent Calendar 2016の第23日目のエントリです。

どーも、わいとんです。

皆さん、明日はクリスマスイブですね。ところで皆さんはカラオケは好きですか?そうでもない?では替え歌ならどうでしょうか?愉快な替え歌なら、ワクワクすると思います。

今日はそんな替え歌を、自動で作ってくれるスクリプトをPerlでつくってみました。

すでに上の画像の時点でネタばれなのですが、クリスマスソングとしておなじみの「赤鼻のトナカイ」の歌いだしの部分を、いい感じに悲壮感漂う風味に仕上がるようにちょっとだけ頑張りました。

と言っても、そんなに難しいことはしておらず、Perl入学式の卒業生であれば、比較的かんたんにつくれる程度のものです。

ソースコードをgistに公開していますので、何をやっているのか見てみると、参考になるかもしれません。

さすがに何の解説もないと理解に苦しむ個所もあろうかと思いますので、少しだけ解説。

動作概説

やってることをかいつまんでしまうと、以下のような手順になります。

  1. 替え歌のもとになる歌詞を形態素解析APIに投げ込み、結果をうけとる。
  2. 受け取った結果データをJSONからハッシュリファレンスに変換する。
  3. 2のデータから一般名詞だけを一定確率で別の単語にランダムで置き換える。
  4. 3のデータをぜんぶjoinしてprintfで出力する。

なお1のAPIは、わいとんお手製のAPIとなっています。いつまでも残しておく保証はないですが、ちょっと遊ぶくらいには使っていただいて構いません。

課題

Perl入学式の受講生ならびに卒業生の皆さんには、思い思いの改造を施して、あなたのオリジナルの替え歌エンジンを作ってみてください。

File::Spec/File::Basenameを使ったライブラリパス指定

※このエントリは Perl Advent Calendar 2016 の15日目のエントリです。

どーも、わいとんです。

早速ですが、みなさんはPerlのライブラリパスをどんな風に指定していますか?

そもそもスクリプト内で指定してない?絶対パスで直接指定?それともFindBinを使ってる?

今回はわいとんがよくやっているFile::SpecFile::Basenameを使ったライブラリパス指定の方法を紹介します。

以下のようなプロジェクトがあったとします。

./
|
+---cpanfile
|
+---bin/
|    |
|    +---run.pl
|
+---lib/
     |
     +---MyProj.pm
     |
     +---MyProj/
          |
          +---Client.pm

cpanfileは以下のような内容です。

requires 'perl', '5.008001';
requires 'LWP::UserAgent';
requires 'LWP::Protocol::https';
on 'test' => sub {
    requires 'Test::More', '0.98';
};

これをcpm installなんてやった場合、local/lib/perl5配下に依存ライブラリがどんどん入ってきます。(cpmについてはskajiさんのエントリを参照してください。)

で、bin/run.plからMyProj::Client->new(...);なんてやりたいなぁって思った時に、以下のように書くと思います。

use strict;
use warnings;
use MyProj::Client;
my $client = MyProj::Client->new(...);
...

でもって、実行時に

perl -Ilib -Ilocal/lib/perl5 ./bin/run.pl

なぁんて書くと思います。別に悪くないけど、面倒ですよね。

ちょっと賢くFindBinを使う

で、use libFindBinを組み合わせて、以下のようにしたりしますよね。(しますよね?したことありません?)

use strict;
use warnings;
use FindBin;
use lib (
    "$FindBin::Bin/../lib",
    "$FindBin::Bin/../local/lib/perl5",
);
use MyProj::Client;
my $client = MyProj::Client->new(...);
...

実行時にはこんな感じで。

perl ./bin/run.pl

楽になりましたね!でもでもでもでもチョッッット待ってwww それってカレントディレクトリがプロジェクトフォルダの直下にないと動かなくないっすか?(動かないですよね?)あとそれWindowsじゃ動かなくないですか?(Windowsなんか使わない?あ、すみませんww)

File::SpecとFile::BasenameとFILEを使う

まあそこで出てくるのがFile::SpecFile::Basenameなんですね。

超雑に解説すると、File::SpecってのはOS間で異なるパス表現(¥とか/とかそういうやつ)を吸収してくれるライブラリで、File::Basenameてのは、食わせた文字列をパス表現文字列とみなしてざっくりパーズし、フォルダまでのパスとかファイル名とかをよしなに引っ張ってきてくれるライブラリです。

あと__FILE__っていうのは実は組みこみ関数でして、__FILE__が記述されているファイルのパス(つまり実行されるスクリプトのパスそのもの)を返してくれます。

んで、こいつらを使ってさっきのbin/run.plを書き換えると、こんな感じになります。

use strict;
use warnings;
use File::Spec;
use File::Basename 'dirname';
use lib (
    File::Spec->catdir(dirname(__FILE__), qw/.. lib/),
    File::Spec->catdir(dirname(__FILE__), qw/.. local lib perl5/),
);
use MyProj::Client;
my $client = MyProj::Client->new(...);
...

若干冗長ですが、実行時は常にスクリプトを正しく指定するだけでOK。

perl ./bin/run.pl

例えばlib/MyProj/からでも

perl ../../bin/run.pl

と実行できます。

少し補足。 File::Spec->catdir(...)は、引数にリストを食わせることで、実行環境に応じたパス文字列を仕立て上げてくれます。dirname(...)は与えられたパス文字列のディレクトリパスを返してくれます。

これらを組み合わせて、どの環境でどのパスから実行されても、読み取りたいライブラリパスがずれずに利用可能となる、と。こういうわけです。

何より、File::SpecFile::Basenameもコアモジュールなので、Perl5が入っていればまず使えないってことはないんじゃないでしょうか。→ツッコミ参照ください

まとめ

  • ライブラリパスをがっちりつかんで離さないスクリプトを書くための方法を紹介しました。
  • File::SpecFile::Basenameを使うぞって言いながら__FILE__も使いました。
  • きっとWindowsの人もニッコリ。
  • cpmかわいいよcpm

ツッコミもらいました

だそうです。。。skajiさん、ありがとうございますm(_ _)m

benchmark.pl 2016 Winter – ベンチマークから考える効率的なロジック

※このエントリはPerl5 Advent Calendar 2016 第13日目のエントリです。

どーも、わいとんです。

タイトルはイベント名っぽくすることで、このエントリを毎年恒例にしていくぞ、という心構えを形にしたもので、特に大きな意味はありません。

効率的なロジック。プログラムを書くときに、突き詰めていくと気になってきますよね。

そんな時、自分の書いたコードに対してベンチマークを取ったりすることかと思います。

ベンチマークを取る?

早い話が「処理速度を測定する」ってことです。こう書いてしまえば身も蓋もないですねw

Perlでベンチマークを取るには

CPANにはBenchmarkというそのものズバリなネーミングのモジュールが存在します。

Benchmarkは、perl-5.004の頃からコアモジュールに含まれており(つまりperlが使える環境ならBenchmarkも使える)、時代の流れとともに進化してきた経緯があります。

基礎的なケースのベンチマーク

どんなプログラムも、基礎の組み合わせで成り立っています。今回は基礎的なケースをいくつか例に実際にベンチマークをとって、より効率的なロジックの模索をしていきたいと思います。

なお、ベンチマークは以下の環境(MacOSX + plenv + perl-5.24.0)で実施しました。基礎的なケースばかりですので環境ごとの揺らぎはほぼないと思いますが、念のため参考としてください。

$ perl -v

This is perl 5, version 24, subversion 0 (v5.24.0) built for darwin-2level
(with 1 registered patch, see perl -V for more detail)

Copyright 1987-2016, Larry Wall

Perl may be copied only under the terms of either the Artistic License or the
GNU General Public License, which may be found in the Perl 5 source kit.

Complete documentation for Perl, including FAQ lists, should be found on
this system using "man perl" or "perldoc perl".  If you have access to the
Internet, point your browser at http://www.perl.org/, the Perl Home Page.

配列への一括処理

ベンチマークのために書いたスクリプトは以下の通りです。

use strict;
use warnings;
use Benchmark ':all';
use Test::More;

## 処理対象となる配列
my @list = (0 .. 10000);


## 各パターンの定義

#- 配列でforを回してpushするタイプ
my $for_array_push = sub {
    my @result;
    for my $row (@list) {
        push @result, "num=".$row;
    }
    return @result;
};

#- 配列インデックスでforを回して直接インデックス指定して代入するタイプ。
my $for_array_index = sub {
    my @result;
    for my $i (0 .. $#list) {
        $result[$i] = "num=".$list[$i];
    }
    return @result;
};

#- Cスタイルのforで直接インデックス指定して代入するタイプ。
my $for_cstyle_index = sub {
    my @result;
    for (my $i=0; $i<=$#list; $i++) {
        $result[$i] = "num=".$list[$i];
    }
    return @result;
};

#- forを使わないでmapで写像を得るタイプ
my $mapping = sub {
    return map { "num=".$_ } @list;
};

## for_array_push を基準に、各パターンが同じ出力になることを確認する
is_deeply [$for_array_push->()], [$for_array_index->()], 'for_array_push eq for_array_index';
is_deeply [$for_array_push->()], [$for_cstyle_index->()], 'for_array_push eq for_cstyle_index';
is_deeply [$for_array_push->()], [$mapping->()], 'for_array_push eq mapping';

## 処理時間の測定
my $result = timethese(10000, {
    'for_array_push'   => $for_array_push,
    'for_array_index'  => $for_array_index,
    'for_cstyle_index' => $for_cstyle_index,
    'mapping'          => $mapping,
});

## 測定結果の比較表示
cmpthese($result);

## テスト完了
done_testing();

for_array_push for_array_index for_cstyle_index mapping の4つのパターンを用意し、それぞれ同じ結果となることを is_deeply で確認してから timethese で速度測定。最後に cmpthese で測定結果の比較を表示しています。

なおこのスクリプトはperlで実行可能ですので、手元でもお試しいただけます。

結果はmapping圧勝

以下、実行結果です。

$ perl ./loop.pl
ok 1 - for_array_push eq for_array_index
ok 2 - for_array_push eq for_cstyle_index
ok 3 - for_array_push eq mapping
Benchmark: timing 10000 iterations of for_array_index, for_array_push, for_cstyle_index, mapping...
for_array_index: 25 wallclock secs (24.37 usr +  0.01 sys = 24.38 CPU) @ 410.17/s (n=10000)
for_array_push: 23 wallclock secs (22.86 usr +  0.02 sys = 22.88 CPU) @ 437.06/s (n=10000)
for_cstyle_index: 31 wallclock secs (31.14 usr +  0.05 sys = 31.19 CPU) @ 320.62/s (n=10000)
mapping:  6 wallclock secs ( 6.09 usr +  0.03 sys =  6.12 CPU) @ 1633.99/s (n=10000)
                Rate for_cstyle_index for_array_index for_array_push  mapping
for_cstyle_index  321/s               --            -22%           -27%     -80%
for_array_index   410/s              28%              --            -6%     -75%
for_array_push    437/s              36%              7%             --     -73%
mapping          1634/s             410%            298%           274%       --
1..3

Cスタイルでの書き方に比べて、mapを使った書き方はなんと4.1倍も速いのがわかりますね。

このことから、配列を全件処理して別の配列を作る場合はmapを使うと速くて短く書ける、という結論が得られました。

ただ注意して欲しいのは、配列を全件処理するだけ(つまり別の配列を作らない)場合にmapを使うことは、可読性という面では好ましくないと考えます(ループ処理だけしたいのか、加工された配列が欲しいのか判断がつかない)。そういうケースでは for my $row (@list) {...} のやり方のほうが意図が読み取りやすいですね。

条件分岐

こちらのベンチマークコードは以下の通りです。

use strict;
use warnings;
use Benchmark ':all';
use Test::More;

## 処理対象の配列
my @list = map {[int(rand(6)+1), int(rand(6)+1)]} 1..10000;

## 処理パターン定義

#- if/elsif/elseを使った条件分岐
my $if_else = sub {
    map {
        if    ($_->[0] == $_->[1])      {1}
        elsif ($_->[0] + $_->[1] >= 10) {2}
        else {0}
    } @list;
};

#- 三項演算子を使った条件分岐
my $ternary = sub {
    map {
        $_->[0] == $_->[1]       ? 1 :
        $_->[0] +  $_->[1] >= 10 ? 2 :
        0;
    } @list;
};

#- ハッシュを使った分岐
my $map = {
    '1:1' => 1, '2:2' => 1, '3:3' => 1,
    '4:4' => 1, '5:5' => 1, '6:6' => 1,
    '4:6' => 2, '5:6' => 2,
    '6:4' => 2, '6:5' => 2,
};
my $hashmap = sub {
    map { $map->{join(':', $_->[0], $_->[1])} || 0 } @list;
};

## if_elseパターンを基準に同一性の担保をする
is_deeply [$if_else->()], [$ternary->()], 'if_else eq ternary';
is_deeply [$if_else->()], [$hashmap->()], 'if_else eq hashmap';

## 速度計測
my $result = timethese(10000, {
    if_else => $if_else,
    ternary => $ternary,
    hashmap => $hashmap,
});

## 結果比較
cmpthese($result);

## テスト完了
done_testing();

if_else ternary hashmap の3つのパターンを用意し、それぞれ同じ結果となることを is_deeply で確認してから timethese で速度測定。最後に cmpthese で測定結果の比較を表示しています。

なおこちらもコピペで実験できます。

三項演算子は速い

こちらが実行結果です。

$ perl ifelse.pl
ok 1 - if_else eq ternary
ok 2 - if_else eq hashmap
Benchmark: timing 10000 iterations of hashmap, if_else, ternary...
hashmap: 18 wallclock secs (18.45 usr +  0.01 sys = 18.46 CPU) @ 541.71/s (n=10000)
if_else: 20 wallclock secs (19.80 usr +  0.05 sys = 19.85 CPU) @ 503.78/s (n=10000)
ternary: 13 wallclock secs (12.22 usr +  0.03 sys = 12.25 CPU) @ 816.33/s (n=10000)
        Rate if_else hashmap ternary
if_else 504/s      --     -7%    -38%
hashmap 542/s      8%      --    -34%
ternary 816/s     62%     51%      --
1..2

目に見えて三項演算子が速いですね。可読性はよろしくないので、使いどころは単純な二分岐にとどめておいたほうが良いと思いますが、変数代入の判断基準のための二分岐にはもってこいと言えそうです。

まとめ

今回は基本的なケースでのベンチマークを試しました。

時代が進んでperlのインターナルな実装に手が入った時、どういうロジックが速くなるのか、ということは都度ベンチマークをする必要があります。そのため、「現時点ではこのくらいの感じなんだなぁ」という程度に参考にしてもらえると幸いです。

YAPC::Hokkaido 2016 SAPPORO が楽しかったという話

※このエントリはPerl5 Advent Calendar 2016の11日目のエントリを兼ねております。

どーも、わいとんです。

昨日のエントリで少しだけ触れましたが、YAPC::Hokkaido 2016 SAPPOROに参加しました。

なお、最強のYAPCレポーターと名高いhirataraさんによるエントリが大変参考になりますので、是非ご参照されることをお勧めします。

YAPCとは?

若い方の中にはYAPCってなんぞ?という方もいらっしゃることかと思います。

簡単に解説しておきますと、YAPCというのはYet Another Perl Conferenceの略称です。大変端折った書き方をするならば、「Perlのカンファレンス」です。そのままですね。

Yet Anotherという接頭辞については、元々Perl Conferenceが別にあるらしいから、という事ですが、詳しくはwww.perl.orgに記載がありますので、興味のある方は是非見てみてください。

初の国内地方都市開催

国内では東京以外で開催されたのは初めてのことでして、軽い観光を兼ねたスケジュールで臨むことにしました。

参加人数は265名(実質185名、後述のことが影響か)で、札幌のポテンシャルと試されっぷりに驚愕しております。

試されっぷり?

実は今回のYAPCは冬の札幌での開催ということもあり、まさにエクストリームYAPCと言える幕開けでした。

  • 新千歳空港が大雪で最終的に閉鎖
  • 閉鎖前に到着した便も2時間ほど遅延したり「途中で引き返す可能性」があってスリル満点
  • 当然空の便は結構欠航
  • 「札幌に着いても安心するなぁ、路面凍結で足元を掬ってやるからなぁ」(CV:大○洋)

とまあ、ホテルや会場に到着するまでエクストリームな心構えを忘れさせない北海道さんの本気を見せつけられた次第です。

あるお方は欠航被害に遭遇したので、機転を利かせてわざわざ成田から北海道新幹線と函館本線を乗り継ぐという、エクストリームの極みと言える旅程を体験したようです。冬の北海道の洗礼はそれだけに止まらなかったようですが・・・

全国各地からPerlつながりで人が集まった

北は北海道(というか札幌開催なので地元の方や道内各地の皆さん)から、南は沖縄まで(しかも学生)、全国津々浦々からPerlというトピックのためにたくさんの人が集まりました。

濃度高めのセッション

タイムテーブルhiratara氏のエントリを見てもわかるように、大変内容の濃いセッションが多かったと思います。

ザクッとまとめてもhttp/2やPerlの詳細なリリース情報、クラウド事業者4社によるパネルディスカッションなど、PerlとWebテクノロジーに興味のある方には垂涎ものの内容でした。

個人的には下記の発表がなかなか刺さる内容だったな、と感じています。

モジュールメンテナンスを通して感じる最近のPerl

モジュールの継続的なメンテナンスにかかるコストに着眼して、後継者へ引き継ぎしやすくする、諦める、などの考えがありますよ、という感じの内容でした。

僕個人としてもモジュールをリリースしている立場として、一言で言えば「身につまされる」セッションでした。Net::Azure::EventHubsのco-maintainerも探さないとな・・・w

PHPエンジニアが初めてPerlでwebアプリを作った話

近年Perlに関する書籍が少ない事や、そもそもドキュメンテーションがあまりないという感想を聞いた時、ドキュメンテーションの薄いモジュールなどを見た時の辛さを思い出してしまいました。

あるモジュールなどに関して、個人ブログにおけるいわゆる「使ってみた」エントリなどはそれなりにあるのですが、一箇所に情報が集約されていないのは確かにしんどいかも・・・

あと、発表者が「PerlのWAFはAmon2Mojolicious使ってみましたが、何か皆さんお勧めのWAFありますか?」という質問をしていましたが、個人的な意見としては中規模以上のWebAppを書くならMojoliciousかAmon2、APIなら自作WAF、小規模なら素のPlackを使うと、この場を持って回答したいと思います。

まとめ

このエントリはチェックアウト直前のホテルの中で慌てて書きました。さて、帰りもエクストリームとなりそうな予感ではありますが、YAPCは楽しいですので、まだ未体験のPerl/Web技術大好きな方は是非次回3月のYAPC::Kansaiなどを検討してみてはいかがでしょうか。

最後に

JPAの皆さん、スタッフの皆さん、そして発表者のみなさんのおかげで大変楽しい思い出ができました。ありがとうございました!

あと今度のYAPC::Amsterdamも行きたいと思います!

2016-12-12 追記

クロージングでJPAのnekokak氏はこう仰っておられました。

nekokak氏のありがたいスライド

さて実は、北海道は札幌でこの追記をしております。

言霊の恐ろしさを感じずにはいられないのです。。。。