ISUCON4本戦に参加したけどダメでした

ISUCON4の本戦に@cnosukeさんと@k0kubunさんの3人で、「railsへの執着はもはや煩悩」というチームで出てきました。

cnosukeさんのブログ( http://cnosuke.hatenablog.com/entry/2014/11/11/141321 )やk0kubunさんのブログ( http://k0kubun.hatenablog.com/entry/2014/11/09/003455 )に状況とか詳しく書いてあるのであんまり書くことないですが、とりあえず最終結果は6,000点も行かずにダメダメな感じでした(ヽ´ω`)

戦略

f:id:rkmathii:20141109151912p:plain

動画広告の配信だし、多分ベンチマークから攻撃のようなアクセスをされるだろうと思い、3台与えられたサーバ全部に対してアクセスさせるようにしました。

ただし、各サーバともメモリが1GBしか積んでいなかったためスワップしまくりだし、OOM Killerにunicornだのvarnishだのを殺されて辛い感じでした。

反省とか

ISUCONの敵であるbenchmarkerの挙動をもっとちゃんと把握しておくべきでした。

benchmarkerからのアクセスログを覗いていたら、"Galaxy Nexus"とかいろんな端末のUAがあって「なんかbenchmarkerめちゃ凝ってるぽいなー」とは思ったのですが、まさかブラウザの挙動を模してキャッシュ機能とかまで備えているとは考えられませんでした。

何か問題があってそれに対して高速化を図るのに、その問題をきちんと把握せずに取り組むのは今更ですが良くなかったです(ヽ´ω`)

(でも競技時間が8時間しかないので焦ってしまう…)

おわり

今年で学生は終わりなので最後の学生枠だったのですが、本戦まで行けて(一応)最終結果をFailせずに終われたのはよかったです。

来年も出場したいので、社会人枠で戦えるようにちゃんと勉強していきます。

ISUCONは競技中はずっと緊張感があり、とても楽しかったです。

運営をしてくださった皆様に感謝しております。本当にありがとうございました。

今年も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メソッドチェーンみたいに書けて、個人的にはめちゃ良いと思った。