Mobile Factory Tech Blog

技術好きな方へ!モバイルファクトリーのエンジニアたちが楽しい技術話をお届けします!

ISUCON10でPerlの参考実装をしました

こんにちは。id:kfly8 です。普段はヒューマンリレーションズ部でエンジニア組織開発をしています。

先日、ISUCON *1でPerlの参考実装をやらせてもらったのですが、とても楽しかったです!貴重な機会をありがとうございました。また、"あのISUCON"の運営裏側を見れて、苦労、凄さなど身近な所で感じることができました。 微力ながら協力できて嬉しかったです。

この記事では、Goの参考実装からPerlへの移植をして考えたことを書きたいと思います。今後、移植をされる方の何かの参考になれば幸いです。注意として、ここでの考えは公式の見解ではなく、あくまで個人的な見解です。

できるだけGo実装に寄せる

移植は、できるだけオリジナル実装のGoに寄せるよう心がけました。 実装の乖離が大きいと競技としてフェアでない、移植ミスの際に気づきやすくなりそう、そんなことが理由です。

具体的には、次の2つを行いました。

  1. Cpanel::JSON::XS::Typeで、JSONレスポンスを明示
  2. 返り値でエラーも返してみる

1. Cpanel::JSON::XS::Typeで、JSONレスポンスを明示

これまでのISUCONのPerl実装では、ベンチマーカーの期待通りのJSONレスポンスをエンコードするために、JSON::Typesを利用していました。 ですが、今回はCpanel::JSON::XS::Typeを利用し、JSONレスポンスの構造を明示的にしました。

# JSONレスポンスを明示しておく
use constant Chair => {
    id          => JSON_TYPE_INT,
    name        => JSON_TYPE_STRING,
    description => JSON_TYPE_STRING,
    thumbnail   => JSON_TYPE_STRING,
    price       => JSON_TYPE_INT,
    height      => JSON_TYPE_INT,
    width       => JSON_TYPE_INT,
    depth       => JSON_TYPE_INT,
    color       => JSON_TYPE_STRING,
    features    => JSON_TYPE_STRING,
    kind        => JSON_TYPE_STRING,
    popularity  => undef,
    stock       => undef,
};

use constant ChairSearchResponse => {
    count  => JSON_TYPE_INT,
    chairs => json_type_arrayof(Chair),
};

Goのstructを使って明示する書きっぷりに、雰囲気は似ている(?)と思います。JSONレスポンスの構造が把握しやすいメリットだけでなく、パフォーマンス面でもJSON::Typesより優位になります。通常のJSONエンコードは、値が文字列か数値かといった内部の状態を確認してエンコードしますが、この場合、値が何であれ型宣言の通りエンコードを試みます。JSON::Typesの実行コスト分、エンコード速度は優位になります。JSON::Typesは、簡単、簡潔に利用できるメリットがありましたが、今回は変更してみました。

2. 返り値でエラーも返してみる

返り値でエラーも返すようにしてみたのですが、効果は特に得られなかった趣味の話です。

例えば、普段であれば、system(@cmd) or die '..'と異常時はorで繋ぐところ、次のように書いていました。

my $err = system(@cmd);
if ($err) {
    ...
}

range_idからrangeを取り出すget_range関数も、エラーも返すようにしました。

my ($chair_price, $err) = get_range($CHAIR_SEARCH_CONDITION->{price}, $price_range_id);
if ($err) {
    ...
}

次のようにget_rangeを剥がすこともできましたが、参考実装のGoに寄せました。

my $chair_price = $CHAIR_SEARCH_CONDITION->{price}{ranges}{$price_range_id};
if (!$chair_price) {
    ...
}

ただPerlの場合、例外で処理するケースがどうしても混ざるので、一貫性が出せず、中途半端でした。趣味でした。

余談

移植は、予定スケジュールよりも早く参考実装を運営チームが用意してくれたので、かなり前持って作業を始めることができました。ただ、悩んだ所、躓いた所もあったので、早めに始められて助かりました。特に次のような所で悩みました。

perlのビルドにuselongdoubleオプションを利用

perlのビルドオプションにuselongdoubleを利用しました。uselongdoubleは、拡張倍精度浮動小数点を扱うためのビルドオプションで、perl -V:nvsizeが8でなく16になります。変更した理由は、緯度経度で桁が足りなくなる為です。例えば、緯度:34.560727610897644 が、緯度:34.5607276108976 となってしまっていました。他の選択肢として、任意桁を扱うモジュール(Math::BigFloatなど)を考えましたが、Go実装と乖離が大きくなりそうな為、不採用にしました。オチとして、PHPでも同様の問題を抱え、結局ベンチマーカーのチェックは緩くなりました。今回の移植で一番悩み、不安だった所です。

isa operator

せっかく、perl5.32を採用したので、本筋でないところにしれっと利用しました。

eval {
    if ($err) {
        die $self->res_no_content($c, HTTP_NOT_FOUND);
    }
}
if ($@) {
    # $@が blessされているかどうかとかいちいち確認しなくて済んで便利
    return $@ if $@ isa Plack::Response;
}

isa operatorはさらっと書けていいですね。ただ、そもそも、evalでなく、try/catchを使う方が直感的で実務の場面に近いと感じるので、evalを無くすかどうか迷いました。Syntax::Keyword::Tryを採用すれば、次のように簡潔に書けるのですが、Try::Tinyを普段利用している人にとっては逆にハマりどころになると思い、今回は見送りました。Syntax::Keyword::Tryの魅力については、papix氏の記事を参照ください。

use Syntax::Keyword::Try;

try {
    if ($err) {
        # ここでreturnしても、直感通り動く。(普通のことなんだけど・・!)
        return $self->res_no_content($c, HTTP_NOT_FOUND);
    }
}
catch { ... }

cpmを採用

CPANインストーラーに、cpmを利用しました。速いですね。Perlの公式系でもcpmを利用されている例があり、採用しても受け入れてもらえると思い、入れさせてもらいました!

秘密のアレ

ISUCONの恒例(?)でしょうか。気づいてもらえたみたいで、よかったです。

おわりに

初めての移植でしたが、本番一発勝負で受け入れてもらえるか、きちんと動作するか緊張感がありましたが、その分充実感も大きかったです。楽しかったです!ありがとうございました!

*1:ISUCON」は、LINE株式会社の商標または登録商標です。

Vue.js で疑似要素に動的なスタイルを適用する

ブロックチェーンチームのソフトウェアエンジニアの id:odan3240 です。
Vue.js で、input タグの ::placeholder 疑似要素に動的なスタイルを当てたい場面がありました。 この記事では、その際の問題と解決方法について紹介します。

この記事のゴール

Vue.js では HTML クラスのバインディングインラインスタイルのバインディング を用いて、動的にクラスやインラインスタイルを割り当てることで CSS を動的に変更ができます。 例えば、<input :style="{ paddingLeft: myPadding }">で、input タグの padding-leftmyPadding を更新することで動的に変更されます。 しかし、これらの構文では ::placeholder の疑似要素だけを指定できず、結果スタイルを変更できません。

CSS カスタムプロパティによる解決

CSS カスタムプロパティ とは、JS などのプログラミング言語の変数と同じように、CSS で変数が使える機能です1。使用例は以下の通りです。

.class {
  /* 変数の宣言。`--` が先頭に付く */
  --main-bg-color: brown;
  /* `var` を使って変数を使用する */
  background-color: var(--main-bg-color);
}

SCSS などの CSS プリプロセッサの変数は、プリプロセス時に変数が置換されて定数として CSS に埋め込まれます。それに対して CSS カスタムプロパティは CSS の変数としての機能を持つので、JS から --main-bg-color を書き換えれば、動的に背景色を変更することが可能です。

このCSS カスタムプロパティを用いて、疑似要素の動的にスタイル変更できます。次の codepen は input タグの ::placeholder 疑似要素を動的に右にずらすサンプルです。

See the Pen abNpQmx by odanado (@odanado) on CodePen.

インラインスタイルのバインディングを使って CSS カスタムプロパティを定義しています。このカスタムプロパティを padding-left に渡しています。
これにより、Vue.js 側で変更された値が CSS カスタムプロパティとして CSS に伝わり、動的に padding を変更することができます。

まとめ

CSS カスタムプロパティをインラインスタイルのバインディングを用いて設定することで、疑似要素に動的なスタイルを適用する方法を紹介しました。