PerlCon 2019に参加してきました!!!

こんにちは! id:anatofuzです。

皆さんのおかげで、ラトビアのリガで開催されたPerlCon2019に参加し、無事帰ってくる事ができました!!! 報告が遅れてしまいすいません 🙇

という訳でこのブログではPerlConの参加から帰国までのイベントレポートを乗せていこうと思います!!

旅レポート

初日

今回の旅は id:papixさんと共に行動をする旅でした。 日本からHelsinkiへの便はちょうどお昼というのもあり、当日沖縄から東京、そしてHelsinkiはかなり時間的に厳しいので、前日入りをするぞ!!!となっていました。

papixさんと「前日は大江戸温泉物語で」などと話していましたが、id:papixさんが成田空港内のいい感じのホテルを抑えていただくことに成功したので、二人でそこに宿泊することになりました。 その為、初日は、那覇から成田のホテルへの移動となりました。(なお完全にいつもどおりに羽田行きの便を取ってしまった)

成田へバスで。空港から空港へのバスってあるんですね..

今回のためにイオンモールイカムで購入した大きめのキャリーケースに、目印としてはてなブログと研究室のアイコン、そして旅のお守りとしてポプテピピックのステッカーを当日貼り付け、出発しました。自宅から那覇羽田空港までは順調に進み、そのままリムジンバスに乗り込んで成田空港へと向かいました。

f:id:anatofuz:20191229155848j:plain
今回のために買ったキャリーケース。イギリスの国旗があしらわれていて、気持ちで負けないようにポプテピピックのステッカーを貼った

成田空港ではすでにid:papixさんが資料作成のためにカフェに滞在しており、合流後、二人でひとまずホテルへ向かいました。 ホテルへは成田空港から無料バスが出ており、いい感じに輸送されます。 ホテルの部屋からは空港を見ることができ、「空港じゃん....」という小学生みたいな感想を抱きました。

ホテルからみた風景 空港じゃん.

成田で日本を経つ前の最後の晩餐(?)として、成田空港でとんかつを食べ、id:papixさんはFitBoxingをし、僕は資料を書きながらその日は寝ました。

東京 -> ヘルシンキ

次の日はシュッと朝起きて空港に移動、出国審査関連の手続きが始まるまで荷物を預けるなどしていました。 papixさんが昨年修行をしていた為、専用レーンでシュッと手続きをする事ができたので「修行すごい....」と思っていました。

初めての海外なのでテンションが上って電光掲示板を撮影した.

券です

朝食はまだ食べていなかったので、これもpapixさんの力でラウンジに乗り込み、修行パワーで無料で提供される朝食を食べていました。 (めちゃくちゃ海外旅行慣れてそうなスーツ姿の人々もいて、「これが国際線ラウンジ...」となっていました)

ラウンジでの朝食とpapixさん.

ラウンジです.

ラウンジ以外では国際線のゲートを超えた先の風景にテンションが上っていました。 なお今からラクダのプログラミング言語のカンファレンスに行くはずなのに、「らくだだめだ」という衝撃的なポスターを目撃する...

いろいろオタクてきなのがあった.

というわけで搭乗です。普段は国内線のLCC的な飛行機しか乗らないので、大きくて圧倒された。

ここに国内の地名以外が出るのが新鮮だった.

機内は定期的に食べ物がデプロイされるシステムになっており、国際線の便に乗っていれば餓死することはないなと感じました。 ポプテピピックの「ビーフオアチキン?」の再現はできなかった。

ハンバーガー作れたのははしゃいだ.

機内はまくらやUSB充電、タブレット端末などがありインターネットカフェか?という感じでした。 wifiも課金すれば使えたので、資料を書くためとTwitterをするためにwifi繋いでました。自宅のネット環境よりは早かった。 今現在航空機がいる場所が地図で出ていましたが、ほとんどロシア上空だったのが面白かったです。マジでロシアは大きい…

しばらくは資料づくりを頑張って、資料が終わった後はタブレットにinstallしていたシャークネードを見ていました。 papixさんはSwitchやってました。シャークネード1は良かったですが、シャークネード2を見ようとしたら、「開幕15分くらいで飛行機にシャークネードが突っ込む」シーンだったので、「これは国際線に乗っている今見るものではない」と思いそっ閉じしました。

充実の機内設備.

そしてついに海外上陸です!!! 我々がついたのはフィンランドヘルシンキ空港です。

ヘルシンキ空港

ヘルシンキ空港では「無限に続くかと思われる、ホラーゲームで絶対敵が出る廊下」や、「唐突の日本語」、「トイレの手を拭くやつがリサイクル式になっていて、伸ばして引っ込める」などの衝撃体験がありました。 入国審査は「Papers, Please」で鍛えたにも関わらず、先にpapixさんが行っていたためか、「Hello」の一言で通過という状況でした。

ヘルシンキ空港

そして空港のRkioskiで「DNA」という絶妙にPerl感があるSIMカードを購入しました。滞在日数に比べてかなり容量があるプランだったらしいのですが、僕のiphoneが「写真を問答無用でicloudにアップロードする機能が有効になっていた」為に、このプランに救われました。

そこからは電車に乗ってヘルシンキ中央駅へ!! なおフィンランドの公共交通はすべて同じ乗車券になっていて、滞在日数に合わせて3日分などの様に買うことができます。 特に改札などは存在しませんが、乗務員から提示を求められた際に期限が切れていたりすると問題が発生するタイプの様です。今回は提示を求められることは無かったです。

電車でgoです

ヘルシンキ中央駅の周辺は、いかにも「ヨーロッパです」という感じでした。街がすべてディズニーランドじゃん...。みたいな感じでした。 ここまでの電車や、この町並みで本当に海外に来たんだなぁと実感しました。

このあたりには「いかにもヨーロッパっぽい銅像」や「すごみのあるアフラック」「日本料理店」などがありました。

ヘルシンキの風景

ホテルBothにチェックインを済ませ、その日は近くのスーパーで買ったサンドイッチなどを食べました。 ヘルシンキでは、水だと思ったらだいたい炭酸水が売っていて、ビールは夜は発売禁止になる世界観です。 スーパーはベルトコンベアに商品を載せて精算するシステムでした。

スーパーです。ヘルシンキの小売店は、店内に大体スロットマシンが置かれている

ご飯です

というわけで時差が辛いのもあるので、この日の晩はpapixさんのFitBoxingを見届けて寝ました。

2日目

2日目のヘルシンキは10度スタートでした。早朝と深夜は冷えるという情報があり、薄手のパーカーを念のために持っていきましたが、常に寒かったのでほぼパーカーを着ていました。 とはいえ現地のおじさまは、半袖短パンでランニングをしており、「これが国の違い…」となっていました。  

市場

この日は朝市でご飯を食べようという流れになり、路面電車を利用して海岸沿いの市場に行きました。

移動風景

路面電車に乗ったのは初めてか久しぶりな気がします。 話には聞いていましたが、海外にはあまり電柱がない。

路面電車車内/車外

朝市会場は海沿いで、常にうみねこかカモメ的なのが鳴いていてのどかでした。 早く付きすぎて、まだ朝市は動いていなかったのでしばらく探索していました。

港の風景。おしゃれ...

ヘルシンキのオシャレ度は凄まじく、公衆トイレもめちゃくちゃオシャレでした。

これがまさかの公衆トイレ

付近には環状のオシャレな建物や、大きい碇などがあり記念撮影などをしていました

円周率を学ぶ辺りの算数の教科書に出てきそうな環状の建物

大きい碇とpapixさん。Ingressのポータルでした

町並みはやはりオシャレ…ドラマの世界じゃん....

ローラースルーゴーゴーみたいなのがよくおいてあって、レンタルできる

そんなことをしていると、そろそろ市場も開いてきたので適当な店で朝食を取りました。 飲食系の屋台以外には、民芸品を売る屋台などもありました。

今回はMooseのミートパイと、ムーミンのベリー系の炭酸飲料を買いました。 Mooseという名前を見た瞬間、Perlを感じてしまった。

店内はヒーターみたいなのがついていて、「夏の気温じゃねぇ...」という感じでした。

朝食を食べた店です。カード決済もできる

会計時にセントとユーロの硬貨がパッと区別がつかずちょっと焦りました。

鹿のパイとムーミンの炭酸飲料。ベリーっぽい

スオメンリンナ

ご飯も食べたので、観光再開です。ここの市場からフェリーでスオメンリンナ島に移動できます。 このフェリーは前日の電車や、朝方乗った路面電車と同じ乗車券で乗ることができます。

フェリーです

フェリー乗り場

デッキの上にでていました。風が心地よかった

著者近影

スオメンリンナ島に上陸した際は、朝早かったのでぼちぼちでしたが、帰るときには日本人旅行客や、様々な国の観光客の方が来ていました。 スオメンリンナ島、めちゃくちゃオシャレ度が高くて、建物も歴史を感じつつオシャレ度が高い。 要塞だけありかなり強そうな建物が並んでいました。

スオメンリンナ

道中いかにもゼルダとかに出てくる、「序盤と終盤に行くとイベントが発生する場所」を見ました。

光の当たり方も素晴らしい

ヨーロッパ==RPGみたいな図式があるので、「復活できるセーブポイントやんけ!!!」みたいな場所もありました。とはいえ実際にドンパチやっていた場所なので、第二次大戦などの戦争を感じさせるものも多かったです

教会です。鐘を鳴らすとイベントが起きそう

石で作られた要塞は当時の戦争風景を感じさせられました。 キングスゲートというカッコいい名前の場所もありました。 (当時は船でこのキングスゲートに国賓が来ていたらしい)

要塞です

そしてスオメンリンナ島には至るところに大砲があります。 今では玉の代わりにゴミが詰められている大砲もありましたが、大砲の存在感はすごかったです

大砲です

大砲を撮影している時に、id:papixさんが中国系の観光客の方から話しかけられ、記念撮影をしてあげるなどのやり取りがありました。

あとはなんか、日本では見たことがない鳥がめっちゃいました。 忍者龍剣伝で見たことがある気がする。

とりです

スオメンリンナ島には博物館も多数設立されていますが、今回は戦争博物館に行きました。 ちょっとした日本語ガイドみたいなのもあって便利。(とはいえ展示物はフィンランド語/英語で解説されているので、だいたいは英語を読む)

こういう博物館行ったことがないので「おおっ!!」と言っていた

papixさんがめちゃくちゃテンションが上っていた

スオメンリンナ島の象徴的なもの

教会の中に入れました

ひとしきりスオメンリンナ島を満足した我々は、フェリー乗り場の前で酒を飲みつつ休憩しました。ciderってサイダーじゃなくてりんご酒の方。

シードルと洗剤みたいな容器に入っているファンタっぽい飲み物。果肉入りで、ファンタより甘かった

そしてフェリー乗船。再びヘルシンキ市場に戻ります。

そしてスオメンリンナ島から戻りました

すでに1日終わった感がありましたが、まだ昼前になっていません。 市場にはうみねことか背中に円柱が刺さっているカメの石像がありました。

戻りました

近場の教会(生神女就寝大聖堂)

まずは近場の教会に行ってみます。「信号とかも縦じゃん! すごい!!」みたいな感じでした

自転車も借りれる

今回は中に入れなかったので、写真だけ取って楽しみました。

色合いとかの雰囲気もいい

ヘルシンキ大聖堂

そしてヘルシンキ大聖堂へ向かいました。 観光地なので様々なバスや路面電車が行き交っています。

ヘルシンキ大聖堂です

ヘルシンキ大聖堂は中にも入れました。

ヘルシンキ大聖堂です

ヘルシンキ大聖堂です

このあたり

そして路面電車テンペリアウキオ教会へと向かいます

路面電車に乗車。券売機もある

テンペリアウキオ教会

テンペリアウキオ教会は岩を重ねて作られている教会で、今はコンサート会場としても使われている様です。 僕たちが入ったときはジャズピアノのコンサートをしていました。 「サラウンド環境じゃん...音質めっちゃいい...」みたいな感想です

テンペリアウキオ教会です

テンペリアウキオ教会からは一旦ホテルに戻り休憩しました。 かなり活動した感じはありましたが、まだ時間があるので ヌークシオ国立公園へと向かうことになりました。

ヌークシオ国立公園

まずは最寄り駅へ電車で向かいます。

ヘルシンキ中央駅で路面ではない電車に乗りました

電車に乗る前に、ヘルシンキ中央駅の売店でご飯と水を買ったのですが、なんと水のペットボトルのキャップが半開きになっていました。店員のお兄さんがいい人で、変えてきていいよとか言ってくれた...

次にバスに乗り換えて向かいます。降りるバス停をミスって結構歩くことになりました。

バスです。バスは乗車券を運転手さんに見せたりする

国立公園まではひたすら山道を歩きます。 道中にマツダやホンダなどの、日本車のホイールが落ちていました。こんなとこで日本を...

山道です

公園は非常に森と湖が綺麗でした。桟橋みたいなのがグラグラして面白かった。

湖が綺麗でした

ベンチで先程買ったパン的なものと水(炭酸水)を飲んで休憩です。 パン的なもの、なんかネチョネチョしていてあまり美味しくはなかった...

ごはんタイムです

ここで休憩していたら、午前中にスオメンリンナ島でid:papixさんが記念撮影をしてあげた中国系観光客のおばさまと再開するというおもしろ体験がありました。おばさまも疲れていたみたいなので、ここから3人でヘルシンキ市内へと戻ります。

3人で帰宅

道中でいい感じのスポットにもよりました

かなりいい風景

この日は疲れからか、電車に乗っている時から頭痛が発生してしまい、早めに眠りにつきました。 ご飯は前日と同じでスーパーで購入しました。 あとはこういう体験もありました

3日目

3日目はしばらくヘルシンキに滞在した後、メイン目的地のラトビア、リガへと向かいます。

エロマンガで朝食

id:papixさんの提案で、2人でエロ漫画に行きました。 エロ漫画という名前ですが、エロ漫画は売っておらず、パン屋さんです。 店内にはお父様がご飯を食べていて、エロ漫画ではなく新聞を読んでいました。

ピロシキと、バナナとベリーが混ざったドロッとした健康飲料的なのを飲んでいました。

オシャレなエロ漫画です

近くにはどうみてもポータルな冬戦争のモニュメントなどもありました。

QRコードがあって、撮影すると詳細のwebページに飛ぶ

デザイン博物館らしく、いろいろアートっぽいものが立ち並ぶ

公園

近場には公園があり、ちょっと散歩しました。 ここにも教会的なものがあり、「こ、これが北欧...!」となっていました。

のんびり散歩しました

カンピ礼拝堂

そしてカンピ礼拝堂に向かいました。ここは静寂に包まれていて、弓道部時代を思い出しました。 都会は色々うるさいですが、こういう場所があると心落ち着きますね...。

中は写真は取れないですが、静寂に包まれていました

突然の語録....

ヘルシンキ市街

さていよいよヘルシンキともお別れをし、リガへと向かいます。 路面電車ヘルシンキ中央駅へと向かいます。

ヘルシンキ市街

再び電車に乗ってGo!

ヘルシンキ空港へ

ヘルシンキ空港

ヘルシンキ空港では、リガへの搭乗手続きを英語でしたり、手荷物預け入れをしたりと英語実践編みたいな感じでした。(手続きが無事できた時は感動した)

ヘルシンキ空港

お土産屋さんでは鹿の絨毯用の毛皮などが売っていました。 我々はid:papixさんの力を利用して、ヘルシンキ空港のラウンジに行っていました。

ラウンジはハチャメチャにオシャレで、鮭のスープが美味しかったです。 また、ラウンジで「沖縄の研究室のサーバーにssh」するという実績を解除し、「グローバルIPってすごい....」と実感しました。

ラウンジ

そしてFinAirでリガへ。プロペラ機です。 機内サービスのジュースの注文は焦りましたが、「指出してThis one」で乗り切りました。 夏に研究室に来ていたインド人留学生から学んだテクニックが役に立った...。

プロペラ機です

機内風景

リガ

そうこうしている内にリガです。リガへの入国は、特に入国審査などがなく、国内線のノリで乗る事ができます。まずはリガから、papixさんがboltという配車アプリでタクシーを手配し、ホテルへ向かいました。

リガです

リガのホテル

リガのホテルでは危うく予約されていないという状況になりそうでしたが、なんとか無事乗り込むことができました。音楽イベントをやっている関係で、どこのホテルも満員に近いらしい。

無事ついて一息

ついでに目の前のコンビニ(スーパー?)で飲み物とお菓子を買いました。

リガでの食事

近場にLidoといういい感じのレストランがあるようなので徒歩で行きました。 ラトビアの料理をいい感じにバイキングでトレーに載せ、載せた食べ物に応じて会計というシステムです。

町並み

ご飯はなかなか美味しく、注文も実物をバイキングすればいいのでなかなか良かったです。 (料理名を見ても瞬時に日本語でイメージがつかないですが、バイキングならその点が解消される)

料理です

3日目 (PerlCon 1日目)

移動

いよいよPerlConが始まります! まずは会場まで僕がBoltでタクシーを呼び、向かいます。 boltは最初から目的地を登録するので、後は乗り込むだけ。支払いもクレジットカードで、しかも友達の紹介だとかなり無料で乗ることができます。

配車アプリで移動しました

その為タクシーも日本のようなタクシーではなく、普通の車にbolt用のスマホが備え付けられているという世界観でした。

タクシーからの眺め

会場であるRadisson Blu Daugavaに到着です!(グループのホテルがラトビアに他に何個かあった)

会場到着

PerlConには日本からは、papixさん、 charsbarさん, skajiさん、福岡に今いらっしゃるgugodさん、私が参加しました。

会場

チェックインとPerlConの受付を済ませ、ぐるっと会場を見て回ります。 PerlConではメインホールが$部屋で、1階上がった先のサブ会場が、それぞれ@%というPerlのシジルがつけられた名前でした。

会場の前には旗がある

メイン会場前の旗 | 入り口のプログラム | サンドイッチなどの軽食が食べれるとこ

もちろんノベルティもあって、Perlのマークや「I love Perl5|6」のステッカー、LarryWallのポストカードなどがもらえました。2階の@%の部屋の前では、定期的に軽食が食べられる懇談の時間があります。

ノベルティとサンドイッチ

オープニング、聞いたトーク

オープニングではカッコいいPerlConの紹介ビデオがキル・ビルのテーマに乗って放送されていました。会場にはこれなかったLarryからのビデオメッセージが再生された後、PerlのメインコミッタであるSawyerXのキーノートが始まりました。

$会場の雰囲気

Sawyerのキーノート

Sawyerのトークは今までのPerl5のバージョンごとの歴史や、今後のアップデートで強化していきたいことなどの、日本ではあまり聞けないPerl自体の話でした。 SawyerはCPANなどのアイコンが自分の顔で、見た感じ怖そう...と思っていましたが、素晴らしく優しい良い方でした。charsbarさんが繋いでくれて、お話する事もできました。「Perl5のリポジトリにcommitするにはどうしたらいい?」という質問に「とりあえずcommitしてくれれば指摘するよ!!」みたいに答えてもらえました。当日は恋人(?)といつも一緒に行動していたのが微笑ましかったです。

Perl6の本とblack stories

PerlConは売店もあり、Perlの書籍やグッズが販売されていました。 Wendy Van Dijk さんが店員だったのですが、気さくに話しかけてくれて、クッキーの型付ききまでおまけでくれました。「Perl6の本ならどれがオススメ?」と聞いたら教えてくれた本を買っています。

PerlConには本棚のタペストリーがありましたがこれはWendyとLizの本棚だそうです。

売店では他に「Perl」というワインが売られていて、charsbarさんやgugodさんによると、これはドイツに「Perl」という地名の田舎町があり、その町のワインをWendyたちが売っているそうです。

初日のトークは、まさかのcharsbarさんの裏番組がpapixさんという日本人がブッキングするという事件もありましたが、面白いトークが多かったです。特にPerl6プロジェクトのメインコミッタであるJonathan Worthingtonによる、Perl6の高速化の話は面白かったです。MoarVMに関する最近のアップデートや、Perl6が驚くべきことにPerl5よりも早い処理があるなどの衝撃的な発表に興奮していました。

もともと卒研で触っていた内容でもあったのでかなり興味深く、Twitterでツイートしたところ、IRCに誘われました。

Perl6の高速化について

papixさんのトークでは、明日の僕のトークの宣伝も入りました。

突然のトーク順番変更

なんと本来は私のトークは初日だったのですが、Sawyerのトークを聞いてるあたり、こんなメールが来ました。

Hi Tokahiru,

We moved your talk to the Day 2 of the conference.

Please find us to get another lunch ticket.

Thank you for your understanding.

Andrey
-- 
PerlCon 2019
____________________________________________________________________________
The European Perl Conference in Riga, Latvia
https://perlcon.eu | mail@perlcon.eu

ま、まさかの当日順番変更....!!! というわけでスピーカーのランチチケットを取り替えてもらい、私のトークは翌日2日目に移動になったのです...。

部屋騒動

PerlCon初日が終わり、ホテルの部屋に行こうとしましたが色々と問題がありました。 部屋はカードキーで入るのですが、手書きで書いてもらった部屋番号が「651」にしか見えなくて、その部屋を開けようとしたのですが、一向に開かない....。 ロビーで問い合わせようと1Fに降り、偶然居たcharsbarさんに聞いたところ「651」じゃなくて「657」だったというオチでした。

そして部屋に入ったにも関わらず、我々を迎えていたのは...

ベット*2と思っていたのに、まさかのダブルベッド1つ。流石にid:papixさんとダブルベッドはお互いにきついので、フロントに「I want an extra bed!! Please separate bed!! today!!! 」みたいに頼み込んでエクストラベッドをもらいました。

夜はcharsbarさん、skajiさん,papixさんで食事に行きました。 charsbarさんが終始優しくて本当にありがとうございました...! (明日トークだったのですでに緊張していた)

おいしいビールをいただきました

4日目 (PerlCon 2日目)

朝ごはん

すでに緊張していましたが、ご飯を食べました

自分のトーク

PerlCon2日目ではいよいよ僕のトークです。

というわけで異常に緊張していました。具体的に緊張の例を上げると

  • 昼ごはんがほとんど食べられない
  • トイレに6回行く
  • 部屋に内鍵したことに気づかない
  • お腹が以上に痛くなる
  • 部屋に水を忘れたことに気づかない
  • 発表前に自分のPCから映像が映らず異常に焦る

などがありました。緊張していましたね...。

とーくのスライドは、

  • Google翻訳で違和感ない日本語に戻せるかチェック
  • Grammalyでチェック
  • DMM英会話などの文例を使う

などをして作りました。

会場は前日のpapixさんと同じで@部屋でした。

トークですがスライドはこちらです。フロッピーディスクの辺りでウケがあって良かった。

日本からきていた皆さんが来てくださったのですが、あのSawyerXも来ていただいていて、非常にびっくりしました。(気づいたのはトークが終わった後の質疑応答のタイミングだったと思う)

トーク自体はYoutubeに上がっています。


Takahiro Shimizu. How to build traditional Perl interpreters

トークは極度に緊張していたのですが、一種のクライマーズ・ハイというか、実際に話していたときは夢中になっていて、あまり覚えていないです。聞いたところ「所々日本語が出たり怪しそうだったが、勢いで乗り切っていた」という話だったので、ま、まぁ良かったかな...という感じです。トーク終わった後にSawyerに握手を求められたのがすごい嬉しかった。

トークが終わって、廊下で休んでいたら、先程のトークを聞いていてくださった方から「面白かったよ、いつもそういうことしてるの?」と言われて「他にPython0.9.1ビルドしてます...」とか話しら興味深そうに聞いてくれました。

聞いたトーク

2日目はLTなどもあり、密度が結構濃かったです。 最初はElizabeth Mattijsenによるキーノートで、Perl6の現状や、Perl6の噂を検証するというものでした。この日はElizabethから「Perl6をCameliaという名前にしない?」という提案がキーノート内でありました。(最終的にはRakuになりましたが)

この方はトークをすべてターミナルでやっていて面白かったです。 (おもしろコマンド, API紹介みたいなトークでした)

次のPerlConの会場投票の開票

僕は別のトークに行っていましたが、WebPerl という凄まじい発表があったのも2日目です。 また、Twitterのフォロワーでもある、AlexanderのRakuのWAFであるCroに関するトークを聞いていました。

かなりPerl6成分が多めだったので、聞いていて楽しかったです。スライドをLaTeXで作っている方もいました。トークの内容は(僕が英語がそこまでよく理解できないのもありますが)、日本人の方が(話し方|技術的に)に上手いというのもあったと思います。

スピーカーディナーなど

2日目ではスピーカーディナーがありました。偶然にもリガに来て初日に行ったレストランと同じ店でした。スピーカーディナーには、NW在住のMakoto Nozakiさんブルガリアのエンジニア、カナダで図書館システムを開発しているPerl6が好きなおじさんなどと喋りました。他にはcharsbarさんの紹介で、Perl6のコアコミッタの方ともお話する事ができました。

あまり懇親会が日本でも得意ではなかったのがあるのですが、もっと英語を喋れるようになってガンガン話したい...!と思いました。

5日目 (PerlCon 3日目)

3日目はトークも終わり、リラックスして聞いていました。

朝ごはんです

聞いたトーク

KeynoteはJonathan Worthingtonで、1日目と少し似ているPerl6の今後についてのトークでした。 3日目はMoarVMの高速化というより、文法やモジュールの話、並列構文などが中心でした。

他にはXSを使ってPerlのオペコードを上書きする話や、skajiさんのcpmの話、Perl6を使って実装した同時進化アルゴリズムの話などがありました。

LTや合間で入ってくるジョーク、英語が上手く聞き取れないので意味が理解できなくて笑えないのが悔しい...!となりました。LTはLTなので早くて集中力が試される感じがありました。

抽選会

3日目の終わりに差し掛かっている頃、BlackStoriesの提供で、参加者の名前が呼び上げられたらプレゼントが貰えるくじがありました。 偶然にも名前が呼ばれて、BlackStories海外版をもらいました。

BlackStories英語版をもらいました! (留学生とやりたい)

close後

PerlConのクロージングは、この3日間のスライドショーとスタッフのエンドロールなどが再生されました。あの規模のカンファレンスを2名+技術班で運用しているのはすごかった...!

その後は日本からの人々で集まって、はちみつビールやうさぎなどを食べていました。

うさぎです

うさぎは鶏肉に似ていましたが、鶏肉よりも獣感が強いと言うか、肉を食べている感じが強かったです。

はちみつビールは非常に甘くて美味しかったです。

黒いビールがはちみつビール、にんにくのおつまみとか

その後はpapixさんと夜のリガを歩きながらホテルへ向かいました。 コンテンツ力が高かった「ボタンを押し続けないと上昇しないエレベーター」は面白かったです。

ボタンを押し続けないと上昇しないエレベーター

夜のリガ市街

6日目 リガ->ヘルシンキ->日本

楽しかった旅も終わり、いよいよ帰宅です。 その前にリガをもう少し観光します

~自由の記念碑

昨日の晩の散歩でみかけたような道を歩きます。 ラトビアヘルシンキと比べると落ち着いては居ますが、ヨーロッパらしさは相変わらずです。

普通の町並みも良い絵になる

そして自由の記念碑へ! 兵隊さんが警備していました

自由の記念碑

ねこ

PerlConのマークにもなっている猫を見かけました。かわいいですね。 意外と写真を取っている人が少なかった気もする。

ねこです

聖ペテロ教会の塔

塔です

エレベーターに乗ると上にいける

塔の中も非常に美しい

そして近くの売店で水を買い、boltでリガの空港へ向かいました

コンビニでの買い物はしっかりできるようになった

リガ->ヘルシンキ

リガの空港でお土産やコーラを買ったりしてちょっとゆっくり。 自動販売機はクレジットカード決済に対応していてこれがキャッシュレス...となりました。

ヤクザとコーラです

リガからヘルシンキはまたプロペラ機です。

プロペラ機に乗り込むぞ!!

ヘルシンキ->日本

ヘルシンキではムーミン関連のお土産を買ったり、謎の味千ラーメンを見たり、自販機で飲み物を購入しました。お土産売り場では唐突に店員に日本語で話しかけられたり、サルミアッキを買うなどの体験をしました。

ヘルシンキへの入国で、自動でパスポートの顔と認証するシステムがったのですが、なぜか僕だけスムーズに反応せず、「2分くらい顔認証のブース内で足止め」という悲しい出来事も体験しました。

空港内。なんかラーメン屋がある

最後に自動販売機でムーミンの炭酸飲料を買いました

そして飛行機に乗り込みます。まさかのタラップではなくバスで移動...。

そして飛行機へ...

機内では炭酸がおいてあったり、アイスを貰ったり、またご飯を無限にもらうなどしました。 寝ないといけなかったのですが、なかなか眠れず、日本についたときは大変でした。

機内風景。帰りは疲れていました

~日本

そして日本に ...!!

Perlのカンファレンスに参加したので健康相談室に行かなきゃ...と思いながら入国しました。

とりあえず成田のレンタルシャワーでシャワーを浴びて、そのまま実家山梨にバスで移動しました。 (本当は大江戸温泉とかに行こうとしたが、疲れていて早く家かホテルに行きたかった)

ということで無事帰国しました!!

感想

今回は初めての海外、初めての国際線、初めての海外カンファレンスと初めてづく目だったので、無事に行って返ってこれて良かった!! という感じでした。特にほぼ日一緒にいたpapixさんや、現地で合流したcharsbarさん、skajiさん、gugodさんにはお世話になりました。ありがとうございました!!!

初めての海外では「英語って本当に言語なんだな...」という感想を得ました。変かもしれませんが、普段書籍や映画,洋曲などで英語が使われているメディアとは触れていますが、実際に英語話者が多数派の世界には行ったことはありませんでした。今回始めて海外に行き、日本語話者が圧倒的に少ない世界に行ってみると「英語って本当に会話で使われている!!!」という、当たり前ではありますが、実感として持つことができたと思います。英語勉強しっかりやっていきたい。

またPerlConでは海外のPerlMongerとお会いできたのが非常に刺激になりました。CPANでアイコンで見たことがある方々が実際に動いて喋っているのは「実在したんだ!!!」みたいな体験でした。特にSawyerとAlexandreに会えたのは良かったです。Perlを作っている彼らも1人の人間なんだなと感じました。僕も彼らと開発してみたいなと強く感じました。

PerlConは日本のYAPCと同じような熱気もありつつ、Perlの内部の話や、Perl6の話が多かったのが印象的です。RubyKaigiに近いものがあるなと感じました。もちろんハイレベルな発表もありましたが、日本の勉強会でしている話が英語にできればPerlConでも通用するなと感じました。

またid:papixさんが「英語は度胸」と言いながら英語コミュニケーションをすごいしていて、カッコいい...!と思いました。僕ももう少し度胸をつけたいと思い、ぼちぼちduolingoとかを始めています。

カンパについて

今回のPerlConは、インターネットの皆様からのカンパで行くことができました! 改めてお礼申し上げます。ありがとうございました!!!!

id:papixさんと相談しながら、小さくブログで募集をしようとしたところ、想像以上のスピードでカンパがあつまり、本当にありがたかったです。 Perlコミュニティの皆さんから、沖縄のコミュニティの皆さん、そしてインターネットでしか面識がない方や、面識が今までなかった方からもカンパを頂きました。

皆さん共通してカンパのときにかけていただいた言葉は「若者だから頑張ってこい」と「以前Perl(コミュニティ)にお世話になったから」という言葉です。Perlという言語がよく、「人と人とをつなぐグルー言語」と呼ばれている理由が、実感できたと思います。

今回は若者であるということと、Perlコミュニティの皆さんの力でPerlConに行く事ができました。 私も近いうちに社会に出て、1人のエンジニアとしてやっていくと思いますが、「あの時の若者がだめになった」と言われないように、僕もPerlコミュニティに還元できるようになっていこうと思います。

本当にありがとうございました!!!

カンパしていただいた皆様

  • kfly8 さん
  • xtesuji さん
  • anzu_mmm さん
  • monamomi さん
  • fujiwara さん
  • Intel0tw5727 さん
  • hitode909 さん
  • sironekotoro さん
  • magnolia_k_ さん
  • hogas さん
  • sugyan さん
  • e2_kan さん
  • toku_bass さん
  • ミノタケ さん
  • kaz_hiramatsu さん
  • aokabin さん
  • sago25tk さん
  • ytnobody さん
  • nakaraxp さん
  • hoto17296 さん
  • k_nishijima さん
  • DQNEO さん
  • yasu47b さん
  • masakyst さん
  • mackee_w さん
  • hkoba さん
  • monmon さん
  • kazuhooku さん
  • shinotra さん
  • masayuki.uehara さん
  • _ _ gfx _ _ さん
  • perlzemi さん
  • uzulla さん
  • akiym さん
  • bokutin さん
  • papix さん
  • codehex さん
  • skaji さん
  • karupanerura さん
  • tokubass さん
  • momen さん

(この他にも様々な方が協力してくださいました!!)

皆さん本当にありがとうございました!!

カンパ集計

合計 ¥588,000 でした!

カンパ収支

  • 航空券
    • ¥205,990
  • Tシャツ費用
    • ¥2,990
  • リガ宿泊費
    • ¥33,163

  • 収支
    • +¥345,857

カンパの余剰分について

カンパの余剰分は当初の予定通りPerlの団体にお渡しという形を取ろうと思います。

具体的には、私以外のPerlに関わる学生とPerlコミュニティに還元したほうが良いと思い、次回YAPC::KyotoなどのYAPCの学生旅費支援スポンサー費として利用致します。

最後になりますが、ありがとうございました!!!

PerlConの渡航費のカンパのお願い

追記(2019/07/08)

カンパのご協力ありがとうございました!!!

目標金額を達成していましたので一旦停止致します!!!

皆さんありがとうございました!!!!!!!!!!!!


こんにちは id:anatofuzです。

8月の7-9日の三日間にラドビアのリガでPerlConというPerlの国際カンファレンスが開催されます。

PerlCon 2019 | The European Perl Conference in Riga, Latvia

学生のうちに海外カンファレンスには行ったほうが良いという話を聞いており、 以前YAPC::Tokyoで登壇した「レガシーPerlビルド」をベースにPerlCon用にCfPを書いた結果、プロポーザルが通ったのでリガで登壇する予定です。

ですが、ラドビアへの交通費が予想以上にかかってしまい、行くことは可能ですが、今後の大学生活で利用したい資金(下宿先の家賃、生活費などなど)に支障が出てしまう事が考えられます。アルバイトや奨学金などである程度は貯金がありますが、今度無茶な予算の配分になりそうというのが見込まれます。

完全に個人的な事情で申し訳ないのですが、リガ行きの資金をいくらかカンパにて援助していただけると非常に助かります!

勝手なお願いで恐縮ですが、できればご協力をお願いします。

目標金額

  • 合計で30万ほど
  • カンパの金額はおまかせします!

用途

  • リガへの渡航費 + リガでの宿泊費 + Tシャツ費のみに充当
    • リガへの往復渡航費とTPCiR会期中のリガでの宿泊費(3泊4日)以外に利用しません
  • オーバーした分はPerl関連の団体や勉強会などに寄付致します

特典

  • 発表スライドにアイコンが乗って紹介します
  • もちろんこのブログでもご紹介致します
  • アイコン入りのTシャツを作り、それを着てPerlConで発表します

カンパの方法

申し訳ないですが口座振込 or LINE Payでの送金でお願いします。 (kyashなどでは現金化する事が難しく、宿泊費などの支払いで支障をきたす可能性がある為です)

送っていただきたい口座及びLINE Payなどは、個別にお知らせ致しますので、DMかメールなどで連絡をお願いします。

(メールアドレス anatofuz@gmail.com )

備考

  • TPCiR終了後、リガへの渡航費 + リガでの宿泊費 + Tシャツ費及び、支援していただいた金額の収支を公開致します
  • Okinawa.pmなどでPerlConのレポートと支援していただいた皆さんへの感謝のトークをする予定です

mac osでPerlのビルドでld関連で死ぬ時

趣味はPerlのビルドなので、plenv経由でPerl5.28.1を入れようとしたところ以下のようなエラーが発生しました。

./miniperl -Ilib make_ext.pl DynaLoader.o  MAKE="/Library/Developer/CommandLineTools/usr/bin/make" LIBPERL_A=libperl.a LINKTYPE=static CCCDLFLAGS=
Generating a Unix-style Makefile
Writing Makefile for DynaLoader
"../../miniperl" "-I../../lib" DynaLoader_pm.PL DynaLoader.pm
rm -f DynaLoader.xs
cp dl_dlopen.xs DynaLoader.xs
"../../miniperl" "-I../../lib" "../../lib/ExtUtils/xsubpp" -noprototypes -typemap '/Users/anatofuz/.plenv/build/1549952483.53003/perl-5.28.1/ext/DynaLoader/../../lib/ExtUtils/typemap'  DynaLoader.xs > DynaLoader.xsc
mv DynaLoader.xsc DynaLoader.c
cc -c   -fno-common -DPERL_DARWIN -mmacosx-version-min=10.14 -fno-strict-aliasing -pipe -fstack-protector-strong -I/usr/local/include -DPERL_USE_SAFE_PUTENV -Wall -Werror=declaration-after-statement -Werror=pointer-arith -Wextra -Wc++-compat -Wwrite-strings -O3   -DVERSION=\"1.45\" -DXS_VERSION=\"1.45\"  "-I../.."  -DLIBC="" DynaLoader.c
rm -rf ../../DynaLoader.o
cp DynaLoader.o ../../DynaLoader.o
rm -f libperl.a
/usr/local/bin/ar rc libperl.a op.o     perl.o  gv.o toke.o perly.o pad.o regcomp.o dump.o util.o mg.o reentr.o mro_core.o keywords.o hv.o av.o run.o pp_hot.o sv.o pp.o scope.o pp_ctl.o pp_sys.o doop.o doio.o regexec.o utf8.o taint.o deb.o universal.o globals.o perlio.o perlapi.o numeric.o mathoms.o locale.o pp_pack.o pp_sort.o caretx.o dquote.o time64.o   DynaLoader.o
cc -o perl -mmacosx-version-min=10.14 -fstack-protector-strong -L/usr/local/lib  perlmain.o   libperl.a `cat ext.libs` -lpthread -ldl -lm -lutil -lc 
ld: warning: ignoring file libperl.a, file was built for archive which is not the architecture being linked (x86_64): libperl.a
Undefined symbols for architecture x86_64:
  "_PL_csighandlerp", referenced from:
      _main in perlmain.o
  "_PL_do_undump", referenced from:
      _main in perlmain.o
  "_PL_exit_flags", referenced from:
      _main in perlmain.o
  "_PL_perl_destruct_level", referenced from:
      _main in perlmain.o
  "_PL_sig_name", referenced from:
      _main in perlmain.o
  "_PL_sig_num", referenced from:
      _main in perlmain.o
  "_Perl_newXS", referenced from:
      _xs_init in perlmain.o
  "_Perl_rsignal", referenced from:
      _main in perlmain.o
  "_Perl_rsignal_state", referenced from:
      _main in perlmain.o
  "_Perl_sys_init3", referenced from:
      _main in perlmain.o
  "_Perl_sys_term", referenced from:
      _main in perlmain.o
  "_boot_DynaLoader", referenced from:
      _xs_init in perlmain.o
  "_perl_alloc", referenced from:
      _main in perlmain.o
  "_perl_construct", referenced from:
      _main in perlmain.o
  "_perl_destruct", referenced from:
      _main in perlmain.o
  "_perl_free", referenced from:
      _main in perlmain.o
  "_perl_parse", referenced from:
      _main in perlmain.o
  "_perl_run", referenced from:
      _main in perlmain.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make: *** [perl] Error 1
Installation failure: make at /Users/anatofuz/.plenv/plugins/perl-build/bin/perl-build line 10353.
ABORT

plenvやperl-buildのエラーではない(ldなのでコンパイルが走った後のエラーである)事から、Perl自体の問題ではあると思っていたので、Perlのバグのページなどを見ていました。

rt.cpan.org

(一応Mojave独自のエラーとしてはこれがあり、ヘッダーファイルをコピるdmgを実行することで回避出来るそうです)

ただbugsのページを確認していても、ld関連のバグ報告がなく、Perl入学式のslackで相談してみました。

僕以外のmojave環境では普通にビルドが通るらしく、これは...となっている時にこのエントリが発見されました。

naoyat.hatenablog.jp

見事にbinutilsをいれており、arが被り破滅していました。

ということで brew uninstall binutils して強制的に解決。

その結果見事にperl 5.28.1がビルド出来ました!!!

Perl入学式の皆さん有難うございました!!! 流石にこれは訓練されてないと気づかない...!

完全にMojaveだと疑っていましたがおま環問題でした。Appleの皆さんすいません :bow:

YAPC::Tokyo 2019に「レガシーPerlビルド 〜現代に蘇るPerl[1..5].0とPerl6〜」 で登壇します

皆様こんにちは.最近はMoarVMと格闘している id:anatofuzです.

YAPC::Tokyoに応募していたトークが通り,登壇することになりました.

yapcjapan.org

タイトルは「レガシーPerlビルド 〜現代に蘇るPerl[1..5].0とPerl6〜」です.

このトークでは以前Roppongi.pmでお話させていただいた内容をベースに次のような内容で喋ります.

みなさんが今使用しているPerlのバージョンはいくつですか?5.24?5.28?はたまたPerl6...? 互換性を大事にするPerlと言っても,みなさんが今動かしているPerlはPerl5以降のものかと思います.一部Perl4のプロジェクトがあるかもしれませんが.... でも待ってみて下さい,なにか忘れていませんか...?

そうです.Perl"5"と言うことは,Perl1からPerl4がかつて存在していたという事です.もし,Perl2.0やPerl3.0のソースコードが目の前にあったら動かしてみたくなりませんか?

このトークでは過去のPerlソースコードから,動きのしくみ,そして現代のosではどのようにすれば過去のPerlが動くのかについて解説します. バージョンごとの特性や, C言語で書かれたPerl処理系のおもしろポイントと,年代別の修正ポイントも一気に見ていきます. また, 実際にPerl1からPerl5,Perl6まで一気に走らせた場合どの様な処理速度の違いが出るのかなどについても紹介します.

対象とするのはPerl1から現在の主流であるPerl5,そしてレガシーと言いつつ最先端の(?)Rakudo Perl6です. メインはPerl5までのPerlですが,Perl6の内部構造などについてもお話するかもしれません.

という訳で具体的には以下のような事をお話しすると思います(変わるかもしれませんが...)

アジェンダ(仮)

20分で勢いよく見ていくのでご期待下さい!

ちなみにPerl[1..6]まで20分で見るので1バージョン3分くらいの勢いなので頑張っていこうな

基本的には初心者向け(Perlのビルドに初心者も何もいるのかというのは置いといて)ですが, 以下のようなパッションをお持ちの方は特に興奮すると思います.

  • Perl自体に興味が有る方
  • トリビア的に無駄な知識を入れることに興奮する
  • 今の職場で1980年代のコードを動かす必要がある
  • 上司にPerl2.0を動かすように言われた
  • 転生した先ではPerl1.0しか無かった

とう言うわけで1月26日(土)に浅草ヒューリックホール&ヒューリックカンファレンスで僕と握手!!!!!

ドキドキPerl1.0探検隊

はじめに

こんにちはid:anatofuzです. これはPerl Advent Calendar 2018 1日目の記事です.

なぜ僕が初日を担当しているかというと以下のような経緯です.

...というわけで id:kfly8 さんの思惑で(?)トップバッターになりました. よろしくお願いします.

さて,初日なのですが, 当初の予定通りPerl1.0を動かす話にしようかな...とは思ったのですが 実際の所Perl1.0に関しては謎のPerlハッカーがメンテナンスを行っており, 現在難なくビルドする事が可能です.

以前はbitbcketにchastaiさんという方がリポジトリを上げていましたが,アカウントごと消えてしまい, 現在僕がforkしたリポジトリgithubにあげています.

github.com

この「Renovation of Perl 1.0」はほぼPerl1.0の動きを維持しているので, 当時のLarryが書いたコードの雰囲気を読むことが出来ます.

実際にどのような修正で現代にPerl1.0が蘇ったかは,またの機会にお話するとして,今回はPerl1.0の動作を見ていこうと思います.

プログラミング言語処理系を読んでみよう

Perl1.0はC言語で書かれています. 動作を見るには実際に書かれたCプログラムを読むのが1番ですね. Cで書かれているので,このプログラムを読むとすると, Cのファイルをvimなどのエディタで開き,どういう処理をしているか先頭から見る……という技を想像するでしょうか.

初見のプログラム, ましてやPerlなどのちょっと大規模なプログラムの場合ソースコードをただ眺めても動きが見えない事があります.

こういう時は,Cコンパイラデバッグオプションを付けてbuildする事で, gdbなどのデバッガでデバッグを可能にし, デバッガを利用してトレースしながら読むのがおすすめです.

ではPerl1.0のトレースをしてみましょう.

トレース準備

今回の動作環境は次の通りです

clangとlldbを使うのは,MacOSのデフォルトコンパイラLLVMというコンパイラ基盤をバックエンドとしたclangであり, LLVMプロジェクトのデフォルトデバッガがlldbだからです.

linux環境の方の場合は, 同様にllvm/clangとlldbをお使いになるか, gcc/gdbなどをお使いになると同様の事が可能です.

  • まずはPerl1.0のソースをcloneしましょう.
$ git clone git@github.com:AnaTofuZ/Perl-1.0.git
$ cd Perl-1.0
  • 次に Configure を実行します.これはmakedependやMakefileを環境に合わせて作成するものです.
$ ./Configure
  • いろいろ聞かれますが大体画面に出てる通りにエンターを押し続ければなんとかなります.

    • installしたい場合は書かれてるとおりにpathを設定してください.
    • 実はここに書いたコンパイラオプションは使われないという悲しさがあるので, コンパイラオプションは一旦無視して進めます
  • 次にMakefileをエディタで開きます.

  • この際にCコンパイラデバッグ出来るようにgオプションを, トレースをする為に最適化を無効にするO0をつけます.
  • MakefileのCFLAGSを次の様に修正します.
 CFLAGS = -W -Wall -Wformat=2 -Wshadow -O0 -g
  • ここまで出来たらmakeしましょう
$ make
  • ここまでするとカレントディレクトリに perl というバイナリが生成されています
    • 試しに lldb ./perl と入力し l main などでmain関数が見れるか確認します
~/w/p/a/Perl-1.0 » lldb ./perl
(lldb) target create "./perl"
Current executable set to './perl' (x86_64).
(lldb) l main
File: /Users/anatofuz/workspace/perl/adventcal/Perl-1.0/./perly.c
   65      #define TMPPATH "/tmp/perl-eXXXXXX"
   66      char *e_tmpname;
   67      FILE *e_fp = Nullfp;
   68      ARG *l();
   69
   70      main(argc,argv,env)
   71      register int argc;
   72      register char **argv;
   73      register char **env;
   74      {
   75          register STR *str;
  • この表示が出れば完了です!
  • オブジェクトファイルなどは余計なので make clean してみましょう

Perl1.0のトレース

ではPerl1.0のトレース環境が整ったのでPerl1.0を読んでいましょう. 今回ターゲットにするのはスカラー型の代入 です.

配列まで追いたい気持ちもありますが, ちょっと長くなるので今回はスカラ変数だけ見てみます. いやハッシュやリファレンスは?とお思いの方もいらっしゃると思いますが,この当時のハッシュは試験段階としての運用であり, リファレンスはそもそも存在しません.

という訳で,今回Perl1.0に実行してもらうコードは次のようなものです.

$hoge = "hello";
print "$hoge\n";  # hello

これは $hoge に helloをいれ出力する例ですね. 代入なので, 実はprintは読みません.

試しに実行してみましょう

$./perl examle/test.pl
hello

無事出力されましたね! では実際にトレースしていきましょう

main関数を見る

では実際にスカラ変数の代入が行われている箇所を見ていきましょう. lldbでデバッグするバイナリに引数を与えるには -- を使います.

~/w/p/a/Perl-1.0 » lldb -- ./perl examle/test.pl
(lldb) target create "./perl"
Current executable set to './perl' (x86_64).
(lldb) settings set -- target.run-args  "examle/test.pl"

target.run-args として example/test.pl が設定されている事がわかります.

では, 実際に先程のhelloが出力されるか確認しましょう.

lldbでデバッグ対象を実行するには process launch を使います.

(lldb) process launch                                                                                                   Process 44366 launched: './perl' (x86_64)
hello
Process 44366 exited with status = 0 (0x00000000)

無事出力されましたね!!

まずはとりあえず, Perl1.0の実装はC言語なのでmain関数から見てみましょう. デバッガで関数で動きを止めるには,break pointを関数にかけます.

この時,すでに1回走らせているので, 再び動かす r で動かします.

(lldb) b main
Breakpoint 1: where = perl`main + 29 at perly.c:78, address = 0x0000000100018dbd
(lldb) r
Process 44906 launched: './perl' (x86_64)
Process 44906 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
    frame #0: 0x0000000100018dbd perl`main(argc=2, argv=0x00007ffeefbff478, env=0x00007ffeefbff490) at perly.c:78
   75          register STR *str;
   76          register char *s;
   77      
-> 78         uid = (int)getuid();
   79          euid = (int)geteuid();
   80          linestr = str_new(80);
   81          str = str_make("-I/usr/lib/perl ");    /* first used for -I flags */
Target 0: (perl) stopped.

すると次の様な表示で止まると思います. これがPerl1.0のCのmain関数となっています.

frame情報の部分を読んでみると, Perl1.0のmain関数はperly.cというファイルの78行目で書かれている事がわかります.

lldbなどのデバッガでは -> が書かれている場所が,次に実行する関数を意味します. その右の数字はソースコードの行数です.

ここの75,76行目では register というキーワードが使われています. これは「できるだけ実マシンのレジスタにこの変数をマッピングしてくれ!!!!!!!!!!!」というお気持ちです.C++の世界ではC++11から非推奨になったらしいです.

さて,ここに出ているmain関数を読んでみると

とりあえずuidとeuidを保存して -I/usr/lib/perl をオブジェクトとして生成し, strに保存している

みたいな事がわかります.

この str_make 関数で生成するオブジェクトはSTRという構造体ですが, これがPerl1.0におけるスカラ変数の正体です.

では, 関数実行を進めて, 与えられたPerlプログラムファイルを読み取り, スカラ変数を生成する所まで行ってみましょう.

関数実行を進めるには n を押します.

init_eval

ひたすら n していくと突然 init_eval などという如何にもevalの準備をしていそうな関数が登場します

(lldb)
Process 45227 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = step over
    frame #0: 0x000000010001929d perl`main(argc=1, argv=0x00007ffeefbff480, env=0x00007ffeefbff490) at perly.c:160
   157
   158         str_set(&str_no,No);
   159         str_set(&str_yes,Yes);
-> 160        init_eval();
   161
   162         /* open script */
   163
Target 0: (perl) stopped.

-> が指す関数の中に入るには step とタイプします

(lldb)
Process 46172 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = step over
    frame #0: 0x000000010001929d perl`main(argc=1, argv=0x00007ffeefbff480, env=0x00007ffeefbff490) at perly.c:160
   157
   158         str_set(&str_no,No);
   159         str_set(&str_yes,Yes);
-> 160        init_eval();
   161
   162         /* open script */
   163
Target 0: (perl) stopped.
(lldb) step
Process 46172 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = step in
    frame #0: 0x00000001000051fb perl`init_eval at arg.c:1235
   1232        register int i;
   1233
   1234    #define A(e1,e2,e3) (e1+(e2<<1)+(e3<<2))
-> 1235       opargs[O_ITEM] =      A(1,0,0);
   1236        opargs[O_ITEM2] =     A(0,0,0);
   1237        opargs[O_ITEM3] =     A(0,0,0);
   1238        opargs[O_CONCAT] =        A(1,1,0);
Target 0: (perl) stopped.

すると init_eval の中に入る事が出来ます. init_eval では 配列 opargs にマクロAを利用して値を初期化している様に見えます.

では何が代入されるのでしょうか. O_ITEM などはマクロの様です. このマクロは arg.h で定義されています

#define O_NULL 0
#define O_ITEM 1
#define O_ITEM2 2
#define O_ITEM3 3
#define O_CONCAT 4
#define O_MATCH 5
#define O_NMATCH 6
#define O_SUBST 7

なるほど O_ITEM は1の様です. では, この代入の前後で値がどの様に変わるか見てみましょう.

変数を見るには p を使います

(lldb) step
Process 46426 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = step in
    frame #0: 0x00000001000051fb perl`init_eval at arg.c:1235
   1232        register int i;
   1233
   1234    #define A(e1,e2,e3) (e1+(e2<<1)+(e3<<2))
-> 1235       opargs[O_ITEM] =      A(1,0,0);
   1236        opargs[O_ITEM2] =     A(0,0,0);
   1237        opargs[O_ITEM3] =     A(0,0,0);
   1238        opargs[O_CONCAT] =        A(1,1,0);
Target 0: (perl) stopped.
(lldb) p opargs[1]
(char) $2 = '\0'

代入前は0が入っているようです.

(lldb) n
Process 46426 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = step over
    frame #0: 0x00000001000051ff perl`init_eval at arg.c:1236
   1233
   1234 #define A(e1,e2,e3) (e1+(e2<<1)+(e3<<2))
   1235     opargs[O_ITEM] =        A(1,0,0);
-> 1236      opargs[O_ITEM2] =       A(0,0,0);
   1237     opargs[O_ITEM3] =       A(0,0,0);
   1238     opargs[O_CONCAT] =      A(1,1,0);
   1239     opargs[O_MATCH] =       A(1,0,0);
Target 0: (perl) stopped.
(lldb) p opargs[1]
(char) $3 = '\x01'

一つ進めると \x01 が代入されている事がわかります.

この O_ITEM などは実はPerl1.0のオペタイプと呼ばれるものです. これはPerlが実行するPerlVMのアセンブリの様なものです.

完全なバイトコードインタプリタとなったPerl5と比較すると粒度がほぼPerlの文法と対応しています. 例えば次のようなオペタイプが存在します.

#define O_PUSH 42
#define O_POP 43
#define O_SHIFT 44

Perl1.0ではこのオペタイプに対応した番号で並んでいる配列opnameが存在します. このopnameにはオペタイプの名前が入っています.

char *opname[] = {
    "NULL",
    "ITEM",
    "ITEM2",
    "ITEM3",
    "CONCAT",
    "MATCH",
    "NMATCH",
    "SUBST",
    "NSUBST",
    "ASSIGN",
    "MULTIPLY",
    "DIVIDE",
    "MODULO",
    "ADD",

この opname などは実際にPerlが与えられたプログラムを解釈する eval という関数で利用します.

yaccのぞき見

Perl1.0が与えられたプログラムをオペタイプに分解して解釈していそう.ということまでは探検しました. では, Perl1.0はプログラムをどの様に分解しているのでしょうか.

実はこの分解に関してはPerl5と同じyaccと呼ばれるプログラムを利用しています. yaccとはコンパイラコンパイラとも言われ, コンパイラ構文解析というフェーズを担当し,プログラムがどのような表現になっているか木構造に変換してくれます.

自然言語処理の文脈では構文解析などと呼ばれるフェーズですね.

Perl1.0におけるYACCperl.y というファイルで定義されています.

ちょっと読んでみると

char *tokename[] = {
"256",
"word",
"append","open","write","select","close","loopctl",
"using","format","do","shift","push","pop","chop",
"while","until","if","unless","else","elsif","continue","split","sprintf",
"for", "eof", "tell", "seek", "stat",
"function(no args)","function(1 arg)","function(2 args)","function(3 args)","array function",
"join", "sub", "file test",
"format lines",
"register","array_length", "array",
"s","pattern",
"string","y",
"print", "unary operation",
"..",
"||",
"&&",
"==","!=", "EQ", "NE",
"<=",">=", "LT", "GT", "LE", "GE",
"<<",">>",
"=~","!~",
"unary -",
"++", "--",
"???"
};

%}

以下にもトークンっぽいものが定義されていますが, 実はこれはyacc側では利用しません. yaccで利用するトークンは次のものです.

%token <cval> WORD
%token <ival> APPEND OPEN WRITE SELECT CLOSE LOOPEX
%token <ival> USING FORMAT DO SHIFT PUSH POP CHOP
%token <ival> WHILE UNTIL IF UNLESS ELSE ELSIF CONTINUE SPLIT SPRINTF
%token <ival> FOR FEOF TELL SEEK STAT_
%token <ival> FUNC0 FUNC1 FUNC2 FUNC3 STABFUN
%token <ival> JOIN SUB FILETEST
%token <formval> FORMLIST
%token <stabval> REG ARYLEN ARY
%token <arg> SUBST PATTERN
%token <arg> RSTRING TRANS

%type <ival> prog decl format
%type <cmdval> block lineseq line loop cond sideff nexpr else
%type <arg> expr sexpr term
%type <arg> condmod loopmod cexpr
%type <arg> texpr print
%type <cval> label
%type <compval> compblock

%nonassoc <ival> PRINT
%left ','
%nonassoc <ival> UNIOP
%right '='
%right '?' ':'
%nonassoc DOTDOT
%left OROR
%left ANDAND
%left '|' '^'
%left '&'
%nonassoc EQ NE SEQ SNE
%nonassoc '<' '>' LE GE SLT SGT SLE SGE
%nonassoc FILETEST
%left LS RS
%left '+' '-' '.'
%left '*' '/' '%' 'x'
%left MATCH NMATCH_
%right '!' '~' UMINUS
%nonassoc INC DEC
%left '('

トークンはいろいろありますが, 今回見てみたいのは代入で使われてそうな =print ですね.

=トークンが来た場合の処理を見てみると, この辺りが気になります.

sexpr   :       sexpr '=' sexpr
                        {   $1 = listish($1);
                            if ($1->arg_type == O_LIST)
                                $3 = listish($3);
                            $$ = l(make_op(O_ASSIGN, 2, $1, $3, Nullarg,1)); }
        |       sexpr '*' '=' sexpr
                        { $$ = l(make_op(O_MULTIPLY, 2, $1, $4, Nullarg,0)); }

ここの文法規則sexprはおそらくS-expression,つまりS式っぽいリストを理解するという意味でしょう. とすると, ここでは代入する際に = の左側がリストであるならば, listish=の右側の値を入れています. そして, make_op という関数でオペタイプ O_ASSIGNを生成しています.

ということは, 「O_ASSIGN だったら xxをする.」処理がPerl1.0のコードのどこかに書かれており,おそらくそこで代入が行われている事がわかります.

スカラ変数の代入

ではどこで代入が行われているのでしょう.

ヒントは O_ASSIGNでした.まずはこれでソースコードgrepしてみます.

~/w/p/a/Perl-1.0 » grep -n O_ASSIGN *.c
arg.c:1243:    opargs[O_ASSIGN] =       A(1,1,0);
arg.c:1629:    case O_ASSIGN:
perl.c:2279:                (yyval.arg) = l(make_op(O_ASSIGN, 2, (yyvsp[-2].arg), (yyvsp[0].arg), Nullarg,1)); }
perly.c:1476:    else if (arg->arg_type == O_ASSIGN &&
perly.c:1947:        (type == O_ASSIGN && 
perly.c:2503:       cmd->c_expr = l(make_op(O_ASSIGN, 2, /* fake up "$_ =" */

結構な箇所で呼ばれている様です.

この中でperly.cで書かれている箇所は, make_op に関する関数や, make_op 自身で呼ばれています. ここでは, 与えられたプログラムから, $hoge = "hello""hello" に対応する文字列の構造体を生成しています. 構文解析の際にオブジェクトを生成するのはRubyなどでも同様です.

実際に生成された構造体から, 変数$hogeに代入が行われているのはおそらく arg.c のcase文でしょう.

ではarg.cを見てみます

    case O_ASSIGN:
        if (arg[2].arg_flags & AF_SPECIAL)
            do_assign(str,arg);
        else {
            if (str != sarg[2])
                str_sset(str, sarg[2]);
            STABSET(str);
        }
        break;

お,以下にもな do_assignstr_sset が呼ばれていますね!

このcase文はevalという関数の中にあるのでlldbでeval関数を確認してみましょう 関数を読むには l を使います

(lldb) l eval
File: /Users/anatofuz/workspace/perl/adventcal/Perl-1.0/arg.c
   1360    static int (*ihand)();
   1361    static int (*qhand)();
   1362    #endif
   1363
   1364    STR *
   1365    eval(arg,retary)
   1366    register ARG *arg;
   1367    STR ***retary;      /* where to return an array to, null if nowhere */
   1368    {
   1369        register STR *str;
   1370        register int anum;

ではcase文が出てくるまで進めましょう.実はlldbは直前のコマンドを繰り返すのでそのままEnterを押します

(lldb) l
   1371        register int optype;
   1372        register int maxarg;
   1373        double value;
   1374        STR *quicksarg[5];
   1375        register STR **sarg = quicksarg;
   1376        register char *tmps;
   1377        char *tmps2;
   1378        int argflags;
   1379        long tmplong;
   1380        FILE *fp;
   1381        STR *tmpstr;
(lldb)
   1382        FCMD *form;
   1383        STAB *stab;
   1384        ARRAY *ary;
   1385        bool assigning = FALSE;
   1386
   1387        if (!arg)
   1388        return &str_no;
   1389        str = arg->arg_ptr.arg_str;
   1390        optype = arg->arg_type;
   1391        maxarg = arg->arg_len;
   1392        if (maxarg > 3 || retary) {
(lldb)
   1393        sarg = (STR **)safemalloc((maxarg+2) * sizeof(STR*));
   1394        }
   1395    #ifdef DEBUGGING
   1396        if (debug & 8) {
   1397        deb("%s (%lx) %d args:\n",opname[optype],arg,maxarg);
   1398        }
   1399        debname[dlevel] = opname[optype][0];
   1400        debdelim[dlevel++] = ':';
   1401    #endif
   1402        for (anum = 1; anum <= maxarg; anum++) {
   1403        argflags = arg[anum].arg_flags;
(lldb)
   1404        if (argflags & AF_SPECIAL)
   1405            continue;
   1406          re_eval:
   1407        switch (arg[anum].arg_type) {
   1408        default:
   1409            sarg[anum] = &str_no;
   1410    #ifdef DEBUGGING
   1411            tmps = "NULL";
   1412    #endif
   1413            break;
   1414        case A_EXPR:
(lldb)

case文は 1407行目のswitchから始まっているようです. ここを見ると arg[anum].arg_type にオペタイプの番号が格納されており, これに応じて命令を実行する仕組みになっている様です.

この arg とは ARG というPerl1.0の構造体の配列となっています.

では, O_ASSIGN の処理にbreak pointをかけてみましょう.

O_ASSIGNは1629行からでした.実は行にbreakpointを書ける事が可能です.

(lldb) b 1629
Breakpoint 2: where = perl`eval + 4556 at arg.c:1630, address = 0x00000001000065ac
(lldb) c
Process 47700 resuming
Process 47700 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1
    frame #0: 0x00000001000065ac perl`eval(arg=0x0000000100500280, retary=0x0000000000000000) at arg.c:1630
   1627        str = arg->arg_ptr.arg_str;
   1628        break;
   1629        case O_ASSIGN:
-> 1630      if (arg[2].arg_flags & AF_SPECIAL)
   1631            do_assign(str,arg);
   1632        else {
   1633            if (str != sarg[2])
Target 0: (perl) stopped.
(lldb)

O_ASSIGN で止まりましたね!

(lldb) n
Process 47768 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = step over
    frame #0: 0x00000001000065d5 perl`eval(arg=0x0000000100500170, retary=0x0000000000000000) at arg.c:1633
   1630        if (arg[2].arg_flags & AF_SPECIAL)
   1631            do_assign(str,arg);
   1632        else {
-> 1633           if (str != sarg[2])
   1634            str_sset(str, sarg[2]);
   1635            STABSET(str);
   1636        }
Target 0: (perl) stopped.

ifの条件はfalseになり, elseに落ちました. elseではstrsarg[2]を比較しています. それぞれに何が入っているのでしょうか.

(lldb) p str
(STR *) $10 = 0x0000000100500060
(lldb) p sarg[2]
(STR *) $12 = 0x0000000100500130

strsarg[2] もSTR型へのポインタのようです.

中身を確認したいのでデリファレンスを意味する * を先頭につけて...

(lldb) p  *str
(STR) $11 = {
  str_ptr = 0x0000000000000000 <no value available>
  str_nval = 0
  str_len = 0
  str_cur = 0
  str_link = {
    str_next = 0x0000000000000000
    str_magic = 0x0000000000000000
  }
  str_pok = '\0'
  str_nok = '\0'
}

(lldb) p *sarg[2]
(STR) $13 = {
  str_ptr = 0x0000000100500160 "hello"
  str_nval = 0
  str_len = 10
  str_cur = 5
  str_link = {
    str_next = 0x0000000000000000
    str_magic = 0x0000000000000000
  }
  str_pok = '\x01'
  str_nok = '\0'
}

なるほど! sarg[2]"hello" へのポインタが str_ptr として入っているようです.

(lldb) p *sarg[2]->str_ptr
(char) $15 = 'h'
(lldb) p sarg[2]->str_ptr
(char *) $16 = 0x0000000100500160 "hello"

そこだけ見てみると, char型のポインタとなっています. char型ポインタはCで文字列を扱う際の方法ですね.

ここで解ることはPerl1.0のスカラ変数 STRは文字列をcharのポインタとして握っているという事です.

*str はメンバの値が初期値です.これは $hoge = "hello" で代入する $hogestr であり, なおかつ今から代入する事を意味しています.

つまり str が今回では $hoge という事です. その為代入する str_sset を実行するはずです.

(lldb) n
Process 47865 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = step over
    frame #0: 0x00000001000065ed perl`eval(arg=0x00000001002025b0, retary=0x0000000000000000) at arg.c:1634
   1631         do_assign(str,arg);
   1632     else {
   1633         if (str != sarg[2])
-> 1634          str_sset(str, sarg[2]);
   1635         STABSET(str);
   1636     }
   1637     break;
Target 0: (perl) stopped.

if文の判定の結果 str_sset に落ちている事がわかりますね!

では, この関数の中に入ってみましょう. s で入って...

(lldb) s
Process 47865 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = step in
    frame #0: 0x00000001000139d0 perl`str_sset(dstr=0x00000001002024a0, sstr=0x0000000100202570) at str.c:132
   129     STR *dstr;
   130     register STR *sstr;
   131     {
-> 132        if (!sstr)
   133         str_nset(dstr,No,0);
   134         else if (sstr->str_nok)
   135         str_numset(dstr,sstr->str_nval);
Target 0: (perl) stopped.

str_ssetに入りました. str_ssetは2引数を取り, dstr が先程のstr , sstrsarg[2] です. stepで実行していきましょう.

(lldb) s
Process 47865 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = step in
    frame #0: 0x00000001000139f8 perl`str_sset(dstr=0x00000001002024a0, sstr=0x0000000100202570) at str.c:134
   131     {
   132         if (!sstr)
   133         str_nset(dstr,No,0);
-> 134        else if (sstr->str_nok)
   135         str_numset(dstr,sstr->str_nval);
   136         else if (sstr->str_pok)
   137         str_nset(dstr,sstr->str_ptr,sstr->str_cur);
Target 0: (perl) stopped.
(lldb) s
Process 47865 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = step in
    frame #0: 0x0000000100013a20 perl`str_sset(dstr=0x00000001002024a0, sstr=0x0000000100202570) at str.c:136
   133         str_nset(dstr,No,0);
   134         else if (sstr->str_nok)
   135         str_numset(dstr,sstr->str_nval);
-> 136        else if (sstr->str_pok)
   137         str_nset(dstr,sstr->str_ptr,sstr->str_cur);
   138         else
   139         str_nset(dstr,"",0);
Target 0: (perl) stopped.
(lldb) s
Process 47865 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = step in
    frame #0: 0x0000000100013a2e perl`str_sset(dstr=0x00000001002024a0, sstr=0x0000000100202570) at str.c:137
   134         else if (sstr->str_nok)
   135         str_numset(dstr,sstr->str_nval);
   136         else if (sstr->str_pok)
-> 137        str_nset(dstr,sstr->str_ptr,sstr->str_cur);
   138         else
   139         str_nset(dstr,"",0);
   140     }

見ていくと, 今回はstr_nset を実行するようです.

(lldb) s
Process 47865 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = step in
    frame #0: 0x0000000100013a93 perl`str_nset(str=0x00000001002024a0, ptr="hello", len=5) at str.c:147
   144  register char *ptr;
   145  register int len;
   146  {
-> 147       GROWSTR(&(str->str_ptr), &(str->str_len), len + 1);
   148      bcopy(ptr,str->str_ptr,len);
   149      str->str_cur = len;
   150      *(str->str_ptr+str->str_cur) = '\0';
Target 0: (perl) stopped.
(lldb) l
   151      str->str_nok = 0;        /* invalidate number */
   152      str->str_pok = 1;        /* validate pointer */
   153  }
   154
   155  str_set(str,ptr)
   156  register STR *str;
   157  register char *ptr;
(lldb)

str_nset は上に示しています. 見てみると GROWSTR という謎マクロを実行し, bcopyでメモリ内のバイトコピーを行い, 長さを設定, その後文字列の末尾にCのヌル文字 \0 を代入している様です.

このGROWSTRとは perl.h:#define GROWSTR(pp,lp,len) if (*(lp) < (len)) growstr(pp,lp,len) であり, growstrを実行してくれるマクロの様です.

growstrは次の通りになっています.

/* grow a static string to at least a certain length */

void
growstr(strptr,curlen,newlen)
char **strptr;
int *curlen;
int newlen;
{
    if (newlen > *curlen) {             /* need more room? */
        if (*curlen)
            *strptr = saferealloc(*strptr,(MEM_SIZE)newlen);
        else
            *strptr = safemalloc((MEM_SIZE)newlen);
        *curlen = newlen;
    }
}

ここから見ると, 条件によってreallocやmallocを安全に実行してくれる君の様です.

さて,ここまでの探検でわかったことは「Perl1.0の文字列の代入は,STR型のchar型文字列をbcopyしている」ということですね!

ちなみにbcopyはすでにmemcpyに置き換えられており, 実際にこのPerl1.0でもbcopyはmemcpyの別名としてマクロになっています.

終わりに

さて, ドキドキPerl1.0探検隊, いかがだったでしょうか. 今回は変数の代入にフォーカスを絞ってみてみましたが, 意外とPerl1.0から頑張って書かれているようです. 流石Larry Wallですね...

ちなみにこういった話をひょっとするとYAPC::Tokyoでするかもしれません. なんと12/3までトーク応募が可能なようなので, みなさんぜひ応募しましょう.

明日は@yumlonneさんで「他の言語っぽくperlを使う話」です! お楽しみに!!!

Roppongi.pm #1 でトークしてきました

こんにちは! id:anatofuz です.今日はid:codehexさんとid:magnoliakさんにお誘いいただいてRoppongi.pm#1に参加してきました.

以前オープンソースカンファレンス沖縄でもトークをした事はあったのですが,あの時は河野先生と共同だったので一人でのトークは初めてでした いつもはLT芸人なので5分以上のトークはいつもの癖で5,6分で終わらせてしまうような気がしており尺が持つか心配でした.

当日のトーク

speakerdeck.com

当日のトークなのですが「あれ...これはYAPC::Tokyoなのでは....!?」と錯覚するような濃いトークで非常にあぁ来てよかったなと感じました. 今回のRoppongi.pmのテーマが「振り返れば、遠くへやってきた」だったので,近藤さんのラクダ本の歴史や制作秘話,DQNEOさんのAmazon::S3::Thinを設計するまでのhistoryPerlコミュニティの関わり,tokuhiromさんの今までのPerlとの関わりなど,明日でPerlが終わると言っても問題ないような(?)歴史散策なトークを聞けました.

そんな中初めての20分トーク+Perlの5に至るまでのversionの話をしたのは緊張と「場にあってるかな…」という不安が直前までありましたが無事トークをする事ができて良かったです.

僕のトークについて

僕のトークですが,個人的にPerlの昔のバージョンを何故か動かしたくなってしまい以前のOkinawa.pmではcharstaiさんのリポジトリを使ってPerl1.0を動かした話をLTしていました.

今回は現在ではメンテされてないPerl2.0~Perl4.0な話を中心に折込,現在のPerl5.28に至るまでの簡単な歴史としてまとめてみました. 技術的な内容や実際の言語実装の話などには今回はあまり踏み込めなかったのでYAPCは何かの.pmで話す際はそのあたりを今度は話そうかと思っております.

調査の方法ですが,トークの序盤でも書いたとおりPerlのVersionリリースの際のtagにつけられたcommitメッセージやmanの記述 後は実際にPerlのCコードのdiffやテストコードを参照してまとめています.

コンパイルの方法自体にはあまりフォーカスしていませんが,これは直前にid:magnoliakさんに,どういう内容をトークしたほうが盛り上がりそうかと相談した際に 「Cコードの変更点はgistに貼るくらいでも良いじゃないか」とアドバイスを頂いたためです.結果的に内容の遍歴を扱う流れができた為良かったと思っています.

このあたりはまたブログなどにまとめたいと思っていますが,それぞれbuildしたPerlがテスト100%通ってないのでそのあたりも調整したいですね.

懇親会では様々な方とお話する事ができましたが,僕がPerlを勉強する時に使ったラクダ本の作者の近藤さんに「良かった」と言っていただけたのが個人的にはすごい嬉しかったです.

またインターネットで良くしていただいている@monamomiさんと初めてお会いすることもできました.

懇親会の後は id:papixさんとid:sironekotoro さんとカレーラーメンを食べるなどしました.

最後に初めてOkinawa.pmではないpmに参加しましたが非常に楽しかったです. 誘っていただいた id:codehex さん id:magnoliakさん ありがとうございました!!

Perl6と多言語の起動時間の比較(Perl5,Ruby,Python,Java,Perl6 on MoarVM,Perl6 on JVM)

追記(2018/07/19)

処理速度ではなくてプログラムの起動時間というご指摘を受けたのでタイトルを修正しました🙏🙏 純粋な処理時間の測定は別途行おうと思います.後半の感想の部分は読み流して頂けると 🙇 🙇

目的

今回は以前のエントリで書いたようなPerl6の正規表現などを使い同じ処理をするプログラムが,各言語でどういったパフォーマンスの差が出るかの検証を行います.

題材

題材としてはmac os X/var/log/system.logのデーモンの回数を数え上げるというプログラムです. 処理の流れとしてはまずコマンドライン引数を確認し,それに応じてファイルを開きます. 開くことが想定されている/var/log/system.logファイルは次のような形式になっています.

Jul 18 01:31:55 anatofuzMBP Dropbox[96084]: [0718/013155:WARNING:dns_config_service_posix.cc(306)] Failed to read DnsCo>
Jul 18 16:57:52 anatofuz-15 idea[66753]: BUG in libdispatch client: kevent[mach_recv] monitored resource vanished befor>

ここから正規表現でデーモンの名前をキャプチャし,一度連想配列を利用してそれぞれのデーモンの出現回数を数え上げます. 最終的に連想配列のkeyをfor文でループさせながら,それぞれの合計を計算するという流れです.

実験

今回はPerl6(MoarVM)とPerl6(JVM)の速度比較を中心に行いました. 解説するとPerl6はベースとなるVMが現在MoarVMかJVMか選択出来るようになっている為,この2つの速度差を図ります. またライバルとなるRuby,Python,Perl5らスクリプト言語や,JVM自体の速度の測定のためにjavaでも実験しました.

  • perl v5.26.2
  • ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-darwin17]
  • Python 3.7.0
  • java 10.0.1 2018-04-17
  • Rakudo version 2018.04.1 built on MoarVM version 2018.04.1 (debugオプション付き)
  • Rakudo Star version 2018.04.1 built on MoarVM version 2018.04.1
  • Rakudo version 2018.06-163-g612d071b8 built on JVM

実験コード

今回使用したコードは次のリポジトリのpushしています.

github.com

Members/anatofuz/Perl6_log_analyze_example: 4f7103163762 /

書き方をある程度統一し,純粋な言語機能の測定を行います.

Perl5

#!/usr/bin/env perl
use strict;
use warnings;

my $file = "/var/log/system.log";

if(@ARGV == 2){
    if ( $ARGV[0] eq "-f"){
        $file = $ARGV[1];
    }
}

my $user_name = qr/anatofuzMBP|anatofuz-15/;
open my $fh, "<",$file;
my $count = {};

while (my $line = <$fh>) {
    if ( $line =~ /\w \d{0,2} (?:\d{2}:?){3} $user_name ([\w.]+)\[\d+\]/){
        $count->{$1}++;
    }
}

my $sum = 0;

for my $key (keys %$count){
    $sum += $count->{$key};
}

print "$sum\n";

素朴にハッシュリファレンスに加算しています

Ruby

#!/usr/bin/env ruby

file = "/var/log/system.log"

user_name = Regexp.new("anatofuzMBP|anatofuz-15")
count = Hash.new(0)

File.open(file,'r') do |f|
    f.each_line do |line|
        if line =~ /\w+ \d{0,2} (?:\d{2}:?){3} #{user_name} ([\w.]+)\[\d+\]/
            count[$1] += 1
        end
    end
end


sum = 0

for key in count.keys
    sum += count[key]
end

p sum

書き方を統一するためにあえて最後はfor文でLoopをさせています. Rubyでforとか数百年ぶりに書いたかも知れない

Python

#!/usr/bin/env python
import re
import sys
from collections import defaultdict

file_path = "/var/log/system.log"
args = sys.argv

if args == 3:
    if args[1] == "-f":
        file_path = args[2]

count = defaultdict(int)

with open(file_path) as f:
    for line in f:
        match = re.search(r'\w+ \d{0,2} (?:\d{2}:?){3} (?:anatofuzMBP|anatofuz-15) ([\w.]+)\[\d+\]',line)
        if match:
            count[match.group(1)]+=1

total = 0

for key in count.keys():
    total +=count[key]

print(total)

Perl6

#!/usr/bin/env perl
use v6;

unit sub MAIN(:f($file) where { .IO.f } = '/var/log/system.log');

my $user_name = /'anatofuzMBP'|'anatofuz-15'/;
my $fh = open $file,:r;
my %count =();

for $fh.lines -> $line {
     if ( $line ~~ /\w+ \s \d**0..3 \s [\d**2\:?]**3 \s $user_name \s (<[\w.]>+)\[\d+\]/) {
         %count{$0}++;
    }
}
$fh.close;
my $sum = 0;

for %count.keys -> $key {
    $sum += %count{$key};
}

$sum.say;

ちなみになんですがPerl6の正規表現リテラル上の空白を無視する為,明示的に空白があると書かないといけません. また範囲演算子**をつけるルールとなっています.

java

package com.google.anatofuz;

import java.io.File;
import java.io.FileReader;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.*;
import java.util.regex.Pattern;
import java.util.regex.Matcher;

public class LogAnalyzer {

    public static void main(String args[]) {

        File file = new File("/var/log/system.log");

        if (args.length != 0) {
            if (args[0].equals("-f")) {
                file = new File(args[1]);
            }
        }

        try {
            FileReader filereader = new FileReader(file);
            BufferedReader bufferedReader = new BufferedReader(filereader);

            String line;
            Map<String,Integer>  map = new HashMap<String,Integer>(0);
            Pattern p = Pattern.compile("\\w+ \\d{0,2} (?:\\d{2}:?){3} (?:anatofuzMBP|anatofuz-15) ([\\w.]+)\\[\\d+\\]");


            while ((line = bufferedReader.readLine()) != null) {
                Matcher matcher = p.matcher(line);
                if (matcher.find()) {
                    map.merge(matcher.group(1),1,Integer::sum);
                }
            }

            int sum = 0;

            for (String key :map.keySet()){
                sum += map.get(key);
            }

            System.out.println(sum);


        } catch (FileNotFoundException ex){
            System.out.println(ex);
        } catch (IOException ex){
            System.out.println(ex);
        }
    }
}

なおjavaはbuildツールのgradleを利用してjarに固めています

計測

素朴にzshのtimeを使って計測しました. 簡単な次のようなスクリプトを作成しています.

#!/bin/zsh
#

echo 'perl5'
time perl log_analyze.pl
echo '====='

echo 'ruby'
time ruby log_analyze.rb
echo '====='

echo 'python'
time python log_analyze.py
echo '====='

echo 'java'
time java -jar java/build/libs/anatofuz-1.0-SNAPSHOT.jar
echo '====='

echo 'perl6'
time perl6 log_analyze.p6
echo '====='

echo 'perl6 (debug)'
time /Users/anatofuz/workspace/cr/Basic/build_perl6/bin/perl6 log_analyze.p6
echo '====='

echo 'perl6(jvm)'
cd  /Users/anatofuz/workspace/cr/Basic/jvm/rakudo/
time /Users/anatofuz/workspace/cr/Basic/jvm/rakudo/perl6 /Users/anatofuz/workspace/cr/Basic/perl6/sandbox/log/log_analyze.p6

実行結果

perl5
524
perl log_analyze.pl  0.04s user 0.03s system 84% cpu 0.090 total
=====
ruby
524
ruby log_analyze.rb  0.12s user 0.05s system 81% cpu 0.220 total
=====
python
524
python log_analyze.py  0.06s user 0.05s system 79% cpu 0.137 total
=====
java
524
java -jar java/build/libs/anatofuz-1.0-SNAPSHOT.jar  0.24s user 0.05s system 140% cpu 0.203 total
=====
perl6
524
perl6 log_analyze.p6  0.37s user 0.03s system 137% cpu 0.294 total
=====
perl6 (debug)
524
/Users/anatofuz/workspace/cr/Basic/build_perl6/bin/perl6 log_analyze.p6  0.78s user 0.07s system 112% cpu 0.752 total
=====
perl6(jvm)
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by org.perl6.nqp.runtime.Ops (file:/Users/anatofuz/workspace/cr/Basic/jvm/nqp/install/share/nqp/runtime/nqp-runtime.jar) to field sun.management.RuntimeImpl.jvm
WARNING: Please consider reporting this to the maintainers of org.perl6.nqp.runtime.Ops
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
524
/Users/anatofuz/workspace/cr/Basic/jvm/rakudo/perl6   21.26s user 0.67s system 439% cpu 4.983 total

トップはPerl5で0.04秒です.かなり早いですね. 続いて処理として早いのはPythonです.正規表現などがモジュール化されていましたが高速ですね. Rubyはeach文を使ってないためか0.12と比較的LLの中では低速です.

JavaはPerl5の6倍,Rubyの2倍ですが,JVMが苦手な正規表現を行っているにもかかわらずそこそこの速度が出ています. またこれでJVM自体が遅いわけではない事が分かります.

さてPerl6ですが,MoarVMの場合0.37とPerl5の10倍遅くなっています. debug optionをつけた場合は速度が0.78sとPerl5の19.5倍の速度となっています.1.0s内に収まっていますがやはりちょっと遅いですね.

一番の問題はJVMに乗ったPerl6で,何かしら厳しいエラーがひとしきり出ている上に21.26s掛かっております. 単純計算でPerl5の531.5倍となっています.Javaと比較しても88倍とJVMが悪いわけではないことが分かります.一体どこが原因なんだろう...

感想

やはりなんというかPerl6全体的に遅く,このPerl6を高速化するというのは非常にロマンがある世界かと思われます. 時間はかかりますが,使えないことはないので皆さん試してみたらどうでしょうか.