今年もISUCON4予選に参加した→予選通過できた!

去年のISUCON3にも参加してダメだったんですが、今年はISUCON夏期講習に参加してから、ISUCON4の予選に参加しました。

学生枠「railsへの執着はもはや煩悩の域であり、開発者一同は瞑想したほうがいいと思います。」というチーム名で、id:cnosukeさんとid:k0kubunさんの3人で出場しました。

k0kubunさんの記事→http://k0kubun.hatenablog.com/entry/2014/09/28/202828

学生枠は上位5チームが本戦に出場できるのですが、予選1日目の時点で4位だったので本戦に行けるかはびみょいです(;´Д`)


ちなみに、使った言語はRubyでした。

開発環境まわりでやったこと

各自で自分専用のインスタンスを立ち上げる

m3.xlargeインスタンスが$0.4/1hくらいだったので、各自でインスタンス立ち上げてそこで開発/ベンチマークが終わったら、GitHubにプルリ投げて本番用インスタンスベンチマーク走らせるってフローでやってました。

GitHub

去年、開発中にベンチとったらいつの間にかFailしまくってしまい、どこで間違えたのかわからなくなってしまったので、今年は GitHubのプライベートリポジトリを立ててそこでプルリ投げ合いながら開発することにしました。

事前に3人で集まって練習した時は1つのインスタンスを3人で触っていたので、どこを変更したのかよくわからなくなってました。 ですが、この方法で割と安全に開発が進められたと思いました。

実装まわりでやったこと

erbをslimに置き換えた

事前にISUCON2の問題を練習していたときに、erbよりもslimのほうが速かったのでそうしました。

'/'はnginxで静的HTMLを返すようにした

トップページもslimで描画でしていたのですが、出力HTMLは変わってなかったのでnginxで生HTMLを返すようにしました。

これと、他の2人がやってくれたvanishやMySQLのインデックスなどでだいたいスコアが8000点前後でした。


反省点

プルリ体制にしてたのに自分がミスった

プルリを投げるときは手元で動かしたベンチマークの結果も一緒に貼って、ちゃんとベンチマークが落ちないことを確かめていたのですが、自分が/reportをいじった時のプルリでベンチマークが落ちていたのにメッセージに気づくことができなかったです。 そのため、後半で「実はベンチマークとおってない」ことに気づいてから、ロールバックするのに時間がかかってしまいました(;´Д`)

ルール(というかツール)をちゃんと把握しておくべきだった

ベンチを走らせるときに、編集したinit.shが何故か適用されていなくて困惑してたら、土壇場になって--helpつけたらちゃんとオプションがあることに気づきました。 また、その時にworkloadについても気づけました(;´Д`) モッタイナカッタ…

m(_ _)m


本戦に行けるかどうかはまだ分かってないですが、今年のISUCON予選もとても楽しかったです!!!

10/06追記: 学生枠4位通過で、本戦に参加できることになりました!!!やった!!!

運営をしてくださった方々にはとてもお世話になりました。

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

ISUCON夏期講習に参加した

ISUCON

去年のISUCON3の予選にも出たんだけど、FAILしまくってダメだった( 第三回ISUCONの予選に出たけどダメだった - 明日から本気だす )ので、今年こそ予選突破するため(?)に夏期講習に参加してみました。

会場は、LINE社のセミナールーム的な部屋でした。

入り口がめっちゃ緑色だったり窓口?がすごく綺麗でした。

お話

LINE社の採用の話とか、ISUCONについてなどのお話がありました。

次に過去問にチャレンジがあったのですが、その途中でもヒントとかテクニックの解説を聞けました。

過去問にチャレンジ

ISUCON3の予選問題に似ている感じの過去問を、その場で解くこともしました。

自分ができたこと(&スコアに反映できたっぽいこと)は、

  • apacheをnginxに置き換える
  • unicornのワーカ数を変える
  • mysqlにindexを貼る
  • Webアプリ(sinatra)の実装を修正する
    • 使っていないところでSELECTを呼んでいたのを消す
    • アイテム数を表示するためにCOUNTを呼んでいたのをMAX(id)に置き換える

くらいでした。

途中途中で解説があって、めちゃ面白そうな話っぽかったのですが自分の作業が中途半端だったせいで話半分にしか聞けませんでした、残念。

初期状態では1700点くらいのスコアが最終的に2500点位になったのですが、中の人は9000点超(!?)したなどを聞いてびっくりしてました。

懇親会

渋谷ヒカリエの裏らへんにあった、オサレなとこで懇親会までありました。 久しぶりにピザとか食べまくりました。

他にも、LINE社の中の方のお話を色々聞けて楽しかったです。

Effective Java

Effective Javaを読み始めたのでメモなど

項目1 コンストラクタの代わりにstaticファクトリーメソッドを検討する

普通にクラスのインスタンスを生成するならばコンストラクタを使用するけれど、コンストラクタよりもstaticファクトリーメソッドを使ったほうが良いケースがある。

staticファクトリーメソッドの利点

  • staticファクトリーメソッドは、コンストラクタと違って名前を持っているため、仮引数の名前とかコメントだけに頼らずにわかりやすい。
public class Hoge {
  // コンストラクタ
  // "Hoge"クラスなのでコンストラクタの名前はHogeになってしまう
  public Hoge(int i, int j, String str) { }

  // staticファクトリーメソッド
  // 好きなメソッド名を付けることができたり、同じ引数のメソッドだけど違う結果がほしい時にも便利
  public static factoryMethod1(int i, int j, String str) { return XXX; }
  public static factoryMethod2(int i, int j, String str) { return XXX; }
  • staticファクトリーメソッドなら、新たなオブジェクトを生成することが必要ない。

    • immutableなオブジェクトを生成したいときにも良い
    • パフォーマンスが大幅に向上することも
  • コンストラクタと異なり、メソッドの戻り値型の任意のサブタイプのオブジェクトも戻り値に使える。

  • パラメータ化された型のインスタンス生成の面倒さを低減できる。

    • Java 7からはダイアモンド構文があるので、staticファクトリーメソッドじゃなくていいかも。
// "<String, List<String>>"って書くのがめんどい
Map<String, List<String>> map = new HashMap<String, List<String>>();

// Java 7ならダイアモンド構文がある!
Map<String, List<String>> map =  new HashMap<>();

// staticファクトリーメソッドを使えば、上の冗長だった文が簡潔に書ける
public static <K, V> HashMap<K, V> newInstance() {
  return new HashMap<K, V>();
}
Map<String, List<String>> map = HashMap.newInstance();

staticファクトリーメソッドの欠点

  • public or protectedのコンストラクタを持たないクラスのサブクラスを作れない。

  • 他の普通のstaticメソッドと区別がつきづらい。

    • staticファクトリーメソッドの一般的な命名・使用用途は次
      • valueOf / of:
      • getInstance
      • newInstance
      • getType
      • newType

newInstance()ってのはよく見る気がするし、メソッドチェーンぽくインスタンス生成できるのは良さげだと思った。

項目2 数多くのコンストラクタパラメータに直面した時にはビルダーを検討する

  • テレスコーピングコンストラクタ・パターンだと、引数の種類が多すぎると対処するのが大変。
public class Facts {
  private final int calories; // 必須
  private final int fat;      // オプション
  private final int sodium;   // オプション

  // コンストラクタがパターンごとに必要になってしまう
  // また、引数の順序が難しい
  public Facts(int calories) {
    this(calories, 0, 0);
  }
  public Facts(int calories, int fat) {
    this(calories, fat, 0);
  }

  public Facts(int calories, int fat, int sodium) {
    this.calories = calories;
    this.fat = fat;
    this.sodium = sodium;
  }
}

// colaインスタンスの生成
Facts cola1 = new Facts(100, 50, 20);  // {calories, fat, sodium} = {100, 50, 20}
Facts cola2 = new Facts(100, 50);      // {calories, fat, sodium} = {100, 50, 0}
Facts cola3 = new Facts(100);          // {calories, fat, sodium} = {100, 0, 0}
...
  • Java Beansパターンだと、生成の過程で不整合な状態ができてしまうことがある。
public class Facts {
  private int calories = 0;
  private int fat = 0;
  private int sodium = 0;

  // コンストラクタは空で引数もなし
  public Facts() {}

  // Setterで処理
  public void setCalories(int val) { this.calories = val; }
  public void setFat(int val) { this.fat = val; }
  public void setSodium(int val) { this.sodium = val; }
}

// colaインスタンスを生成し、その後Setterで状態を与える
Facts cola = new Facts();
cola.setCalories(100);
cola.setFat(40);
...
  • ビルダーパターンで書くと
public class Facts {
  private final int calories; // 必須
  private final int fat;      // オプション
  private final int sodium;   // オプション

  public static class Builder {
    // 必須
    private final int calories;

    // オプションはデフォルト値で初期化しておく
    private int fat = 0;
    private int sodium = 0;

    // 必須パラメータはコンストラクタで代入
    public Builder(int calories) {
      this.calories = calories;
    }

    // オプションパラメータは好きなだけ
    public Builder fat(int val) {
      this.calories = val; return this;
    }
    public Builder sodium(int val) {
      this.sodium = val; return this;
    }

    public Facts build() {
      return new Facts(this);
    }
  }

  private Facts(Builder builder) {
    this.calories = builder.calories;
    this.fat = builder.fat;
    this.sodium = builder.sodium;
  }
}

// colaインスタンスの生成
Facts cola1 = new Facts.builder(100).fat(50).sodium(20).build();
Facts cola2 = new Facts.builder(100).fat(50).build();

ビルダーパターンはいい感じだけど、記述量が結構増えてしまうので引数が多い状況に使うのが良さそう

テレスコーピングコンストラクタ・パターンやJavaBeansパターンよりはかなり安全。


  • staticファクトリーメソッドは、たまに作っていたけど役割とか利点を理解していたわけではなかったので勉強になった。
  • ビルダーパターンはRubyとかPythonメソッドチェーンみたいに書けて、個人的にはめちゃ良いと思った。

Minicondaを試そうとしたが

普段、PythonをHomebrewでインストールしたpyenvで使っているのですが、ふとpyenv install -lを見てみたら色々なパッケージがありました。

$ pyenv install -l
Available versions:
  2.4.0
...           # ふつうのPython2系
  2.7.7

  3.0.1
...           # ふつうのPython3系
  3.4.1

  anaconda-1.4.0
...           # 数値計算ライブラリが最初から入ってるPython
  anaconda-1.8.0

...

  miniconda-2.2.2
...           # なんだこれ?
  miniconda3-3.4.2

...

どうやら、MinicondaというのはPython数値計算のライブラリを使うためのパッケージのようですが、Anacondaと違って、あとからライブラリを選んで入れるようでした。

http://conda.pydata.org/miniconda.html

そこでせっかくなので、試しに入れてみました。


まず、pyenvでMinicondaの最新版を入れます

$ pyenv install miniconda-3.4.2
$ pyenv global miniconda-3.4.2
$ pyenv rehash

(一度シェルを開き直す)

$ python --version
Python 2.7.7 :: Continuum Analytics, Inc.


$ conda list
# packages in environment at /Users/rkmathi/.pyenv/versions/miniconda-3.4.2:
#
conda                     3.5.5                    py27_0
openssl                   1.0.1h                        0
pip                       1.5.6                    py27_0
pycosat                   0.6.1                    py27_0
python                    2.7.7                         0
pyyaml                    3.11                     py27_0
readline                  6.2                           2
requests                  2.3.0                    py27_0
setuptools                3.6                      py27_0
sqlite                    3.8.4.1                       0
tk                        8.5.15                        0
wsgiref                   0.1.2                     <pip>
yaml                      0.1.4                         1
zlib                      1.2.7                         1


$ conda install numpy
Fetching package metadata: ..
Solving package specifications: .
Package plan for installation in environment /Users/rkmathi/.pyenv/versions/miniconda-3.4.2:

The following packages will be downloaded:

    package                    |            build
    ---------------------------|-----------------
    numpy-1.8.1                |           py27_0         3.0 MB

The following packages will be linked:

    package                    |            build
    ---------------------------|-----------------
    numpy-1.8.1                |           py27_0   hard-link

Proceed ([y]/n)? yes

Fetching packages ...
numpy-1.8.1-py 100% |#######| Time: 0:00:46  67.81 kB/s
Extracting packages ...
[      COMPLETE      ] |#######| 100%
Linking packages ...
[      COMPLETE      ] |#######| 100


$ conda list
# packages in environment at /Users/rkmathi/.pyenv/versions/miniconda-3.4.2:
#
...
numpy                     1.8.1                    py27_0
...

こんなかんじで、とりあえずNumPyを入れるところまで動かしました。

次に、インストールしたパッケージを使って環境を作ります。

virtualenvwrapperみたいなかんじですが、環境を作るときの入れたいパッケージを自分で選び、足りない場合はダウンロードからしてくれます。

例として、Numpy 2.1.0とIPythonをインストールしたnumpy21という名前の環境を作ってみます。

$ conda create -n numpy21 ipython numpy=2.1.0
Fetching package metadata: ..
Solving package specifications: .
Package plan for installation in environment /Users/rkmathi/.pyenv/versions/miniconda-3.4.2/envs/numpy21:

The following packages will be linked:

    package                    |            build
    ---------------------------|-----------------
    ipython-2.1.0              |           py27_2   hard-link
    numpy-1.8.1                |           py27_0   hard-link
    openssl-1.0.1h             |                0   hard-link
    python-2.7.7               |                0   hard-link
    python.app-1.2             |           py27_2   hard-link
    readline-6.2               |                2   hard-link
    sqlite-3.8.4.1             |                0   hard-link
    tk-8.5.15                  |                0   hard-link
    zlib-1.2.7                 |                1   hard-link

Proceed ([y]/n)? yes

Linking packages ...
[      COMPLETE      ] |######| 100%
#
# To activate this environment, use:
# $ source activate numpy21
#
# To deactivate this environment, use:
# $ source deactivate
#


$ source activate numpy21

しかし、何故か自分の環境だとactivateした瞬間にシェルごと落ちました(;´Д`)

ナンデー