ラベル プログラミング の投稿を表示しています。 すべての投稿を表示
ラベル プログラミング の投稿を表示しています。 すべての投稿を表示

2014年7月21日月曜日

成長したいので本棚を載せてみる

成長したいエンジニアは良いエンジニアの本棚を真似るといいんじゃない? - Lean Baseballという記事があって、その中で

「本棚を真似る」エンジニアから「本棚を真似られる」エンジニアへ

というお話がありました。確かに本棚を共有するのは、私がどんなことに興味があるのかわかってもらうとても良い方法だと思うので、家の本棚写真を一挙公開です!

なお、ここにある本の他に、実家にあるもの、会社の本棚にあるもの、Kindleで読んでいるものなどあります。これでおすすめ&必読本すべてを網羅しているわけではないですが、比較的最近購入した、またはいただいた本が並んでいるので、興味の反映としてはかなり正確です。マンガ棚は省略しています。

みなさまはどんな本が本棚に並んでいるでしょうか。見てみたいです!

なお、元記事で触れられている「リーン・スタートアップ」は、amazonのwishlistに入ったまま購入には至っておりません...

2013年10月7日月曜日

ISUCON3 に参加して予選敗退したので反省してみる

ISUCON3に会社の方と3人でチームを作って参加しました。結果は... 予選敗退! スコアは初期状態のGoで測ったものが最終スコアです...

当日の流れ

今年初参加で、全く予習をしていなかったので勘所もなにもつかめず、まずWebアプリケーションがいくつか転がっていることを認識。言語は議論の末皆がやったことのある Perl に決定。 Perl の会社に務めているのに私だけ Perl がろくに書けません。

作戦は、まずコードを読んでボトルネックになっていそうなところを修正しようとして、コード分析から始めました。具体的には以下の様な点があがりました。

  • markdown を毎回 fork して変換しているのは遅そう
  • ログインしていない状態のページはキャッシュできそう
  • pager は怪しいけど後回しかな
  • 静的ファイルも後ろに投げているのでリバースプロキシで処理しよう
  • SQLにインデックスがなかったり、MySQLがデフォルトで動いているのをなんとかする?

それで、作戦としては基本的にはコンテンツから静的ファイルを作成して nginx の rewrite でがんばろうという話になりました。

最初に想定したアーキテクチャ

初期段階で「このアーキテクチャで行こう」となったものは次のような感じです。

フロント nginx
静的ファイルをサービスする
Webアプリケーション
markdown への変換と動的コンテンツの提供
バックグラウンドWorker
Webアプリケーションから受け取ったデータで静的ファイル生成&DB更新
memcached
アプリケーションとworkerから参照するキャッシュ
RabbitMQ
更新があった時にworkerに仕事を投げるqueue

いま考えると少し大掛かりすぎるわりには全体をカバーできていないアーキテクチャでした。

つまづいたところ

私の担当はworkerだったので、Queueからfetchして静的ファイルを生成するところを作成しだしました。 RabbitMQ との接続には Net::RabbitFoot を使いました。まずはトップページだけ... と思ってやって、ボチボチ出来かけたところで...

  • uri_for で URI 生成しているけど裏workerだと使えない
  • ページにユーザー情報埋め込んでるけど取ってこれない
  • っていうかセッション情報入ってる... 静的にして大丈夫?(いまさら

のようなところで悩み、うまく行かなくなって「ごめん、やっぱり無理...」と断念しました。

その後はいろんなページをmemcachedでキャッシュする方向に進みましたが、なかなか思うように成果が出ず時間切れで終了しました。

こうしたらよかったかな

  • チームで得意言語が違うとそれなりに不利なので、得意言語は合わせるorちゃんと予習したほうがいい
  • 全部を一度に作りなおす方法だとハイリスクなので、少しずつチューニングするほうが失敗した時のダメージが少ない
  • もっと積極的にbenchmark回せばよかった。つねにgit pullしてbenchmarkだけ回し続けるようにしてもいいくらい
  • プロファイリングしたのが午後4時ぐらいで、遅すぎた。
  • いろんな罠が仕掛けられている可能性を考慮すべきだった。
  • ベンチツールがどんなクエリを投げているのか統計とるべきだった

罠というのは、たとえば memcached が 11212 で上がっている問題がありますが、これは自分でmemcachedの設定を書き換えていて、「あれー、反映されない!」となっているのに気付かず、 stats とった時の値がおかしいのに気づかず... というので、自分の設定ミスばかり疑っていました。また、「DOMを変更しないこと」とあったので、スタイルシートの指定にトップページがあるのは気づいていましたが、変更できない... と諦めていました。

memcached 作戦はそこそこ動いて、testでスコア5000ぐらいまで行ったのですが「testは通るけどこの場合にうまくいかない」みたいなケースを見つけてしまいやり直して泥沼にハマってしまいました。多少FAILしても積極的にbenchmarkをとる作戦のほうがよかったと思います。

でも最大の失敗は、コードを中途半端に読んでボトルネックを「推測」するだけに終わり、最初からプロファイリングしたりシステムメトリックスを取得してチューニングに挑まなかったことだと思います。こんなですが会社ではチューニングもやってます...

次回があるとしたら、すぐにgangliaを入れれるようにしておいて基本的な数値は常に見ながらやるようにしようかなぁ、と思いました。あとNYTProfのようなツールの使い方をもっと覚えないといけませんね。

あと一人一インスタンスだったのでChefで構築しようとして途中で諦めてさらに時間をロスしていました。

くやしい

くやしいです...

2011年11月24日木曜日

memtest64 開発メモ

memtest86 という有名なメモリチェッカがあるんですが、これ 64bit に対応していません。

PAE には対応しているので、64GB までのメモリチェックは出来るんですが、お陰で結構複雑なコードになってるし、メモリアクセスは 32bit なので遅い... ということで、64bit 移植に挑戦してみようと思いました。

ところがブートローダーってまともに書いたことが無いので、さっぱりわかりません。ある程度まとまってきたので、自分用に調査メモを残します。

全般的な情報

Intel® 64 and IA-32 Architectures Software Developer Manuals
リファレンスといえば Intel です。 AMD64 なんですが、ドキュメントは Intel のほうが親切な気がするので、こっちを読んでます。迷ったらこれ、です。

Linux i386 Boot Code HOWTO
i386 向けですが、Linux のブートコードの解説です。
memtest86 もブートローダーは Linux のを借りているので、勉強になります。

Linux のブートローダー
memtest86 のブートローダーの元です。
バージョンが変わってるのでかなり変わってますが、参考になります。

GRUB
GNU のブートローダーです。
ライセンスは GPLv3 なので、Linux 関係のコードと混ぜられませんが、勉強にはなります。

Entering Long Mode Directly
Long mode に入るには protected mode を経由しないとだめなんですが、手順を一部省略する方法が書かれています。
どれぐらい安全なのかはわかりませんが...

メモリ情報

メモリ情報は、OS が起動している状態だと OS がよしなにやってくれますが、起動した状態だとどの物理アドレスが使えるのかもわかりません。

Detecting Memory (x86)
メモリ情報は BIOS の E820 というコールを使うのがよさそうです。
2002年以降の BIOS だと使えるようなので、x64 対応の CPU が載ってるマシンだとほぼ問題ないでしょう。

x86-64 Page table structure
Wikipedia のページテーブル関係の記述です。プログラマ向けじゃないので情報量は少ないですが、あらすじはわかります。

Memory Map (x86)
典型的なメモリマップが載ってます。E820 で情報取るまでは決め打ちでメモリを使うしかないので、参考にします。

その他メモ

  • E820 は long mode からは呼べないので、それまでにマップを取得する必要がある。
  • startup_32 と startup_64 というエントリが結構キーポイントっぽい。

2011年8月4日木曜日

Rails はじめました

Rails ずっとやったことなくて、このままじゃいけない気がしたので勉強を始めました。

まずは 公式サイトのチュートリアル をやっています。コマンドを打つだけで色々ファイルが生成されて、魔法みたいですね。

本では Ruby on Rails 3 アプリケーションプログラミング が良さそう。チュートリアル終わったら欲しいな。

がんばるよー。

2011年7月28日木曜日

Ackermann function

アッカーマン関数という関数があります。
バカ正直に計算しようとするとすぐ計算量が膨大になり、計算できなくなるたぐいの関数です。

これをアセンブリでバカ正直に計算させるコードを書いてみました。階乗を計算させるコードのおまけ付きです。

実際に実行させたところ、ack(3, 12) あたりになるとスタックがあふれました。
スタックサイズを調整すればもうちょっといけると思います。

使用した高速化のテクニック
  • 末尾再帰を CALL から JMP に置き換え
  • TEST+JE でmacro fusion できる命令形式にした
  • INC / DEC を ADD / SUB に置き換えて、フラグレジスタの依存関係を解消
  • MOV RDX, 1 を MOV EDX, 1 にして命令長を短縮
    .code

ack PROC public

    ; RCX = m, RDX = n
    TEST    RCX, RCX
    JE      m_zero

    TEST    RDX, RDX
    JE      n_zero

    PUSH    RCX
    SUB     RDX, 1
    CALL    ack
    POP     RCX
    SUB     RCX, 1
    MOV     RDX, RAX
    JMP     ack

n_zero:
    MOV     EDX, 1
    SUB     RCX, 1
    JMP     ack

m_zero:
    MOV     RAX, RDX
    ADD     RAX, 1
    RET

    ack ENDP

factorial PROC public
    
    MOV     EAX, 1
    CMP     RCX, 1
    JLE     f_ret

f_loop:
    IMUL    RAX, RCX
    SUB     RCX, 1
    CMP     RCX, 1
    JNLE    f_loop

f_ret:

    RET

    factorial ENDP

    END

一応 C で書いた以下のコードよりは、3割ぐらい速いようです(Visual C++ 2010 で /O2 としてコンパイル)。階乗は微妙に速いだけでほとんど変わりませんでした。CPU は Penryn ベースの Core2 Duo です。

int64_t factorial_c(int64_t n)
{
    int64_t r = 1;
    while(n > 1) r *= n--;
    return r;
}

int64_t ack_c(int64_t m, int64_t n)
{
    return m == 0 ? n + 1 : n == 0 ? ack_c(m - 1, 1) : ack_c(m - 1, ack_c(m, n - 1));
}

2011年7月26日火曜日

Bogosort

世の中にはボゴソート というソートアルゴリズムがあります。ランダムにシャッフルして、順番に並んでいたらソート完了というものです。

魔が差したので実装してみました。

ところで、ボゴソートは完全な乱数を使用した場合にはアルゴリズムが完了することが保証されていますが、疑似乱数には周期があるため同じ状態が繰り返し、完了しない場合があります。メルセンヌツイスタ MT19937 を利用した場合、周期は 2^19937-1 で、およそ 10^6001 です。

一方ボゴソートの平均計算量は O(n * n!) らしいので、138 * 138! = 9.55 * 10^238、つまり 138 要素ぐらいまでならメルセンヌツイスタぐらいの周期を持つ乱数でそろう可能性が高い(50%を越える)ということになります。だいたい計算してみると 2080 * 2080! = 10^6004 ぐらいとのことなので、2079 要素ぐらいまでならメルセンヌツイスタぐらいの周期を持つ乱数でそろう可能性が高い(50%を越える)ということになります。

32bit の線形合同法(普通の rand() 関数)の場合は、最大周期が 2^32-1 なので、わずかに11要素ぐらいまでしかソートできず、それを越えると永久にソートできない可能性が高くなります。

メルセンヌツイスタのような、非常に周期が長いと言われる乱数を利用しても数百千要素までしかソートできないなんて、ボゴソートは恐ろしいですね。

ちなみに 138要素の場合でも、1秒間に1京回計算できる計算機を使っても、完了までに 10^215年以上かかることになり、宇宙の寿命が先に尽きてしまいます。
2000要素とかどんだけかかるのかな...

#include <iostream>
#include <boost/timer.hpp>
#include <boost/random.hpp>
#include <algorithm>
#include <ctime>
#include <boost/foreach.hpp>

class Random
{
public:
  boost::mt19937 gen;
  boost::uniform_int<int> dst;
  boost::variate_generator< boost::mt19937, boost::uniform_int<int> > rand;
  Random( int N ):// call instance:
    gen( static_cast<unsigned long>(std::time(nullptr)) ), dst( 0, N ), rand( gen, dst ) {
  }
  std::ptrdiff_t operator()( std::ptrdiff_t arg ) {
    return static_cast< std::ptrdiff_t >( rand() );
  }
};

template <class InputIter, class Rand>
void bogosort(InputIter first, InputIter last, Rand rand){
  while(!std::is_sorted(first, last))
    std::random_shuffle(first, last, rand);
}

template <class InputIter>
void bogosort(InputIter first, InputIter last){
  while(!std::is_sorted(first, last))
    std::random_shuffle(first, last);
}

int main(int argc, int argv[])
{
  std::vector<int> a;

  while(true){
    int i = 0;
    std::cin >> i;
    if(std::cin.eof() || std::cin.fail()) break;
    a.push_back(i);
  }
  Random rand(a.size());
  boost::timer timer;
  bogosort(a.begin(), a.end(), rand);
  std::cout << "time = " << timer.elapsed() << std::endl;
  BOOST_FOREACH(int i, a){
    std::cout << i << ", ";
  }
  std::cout << std::endl;
  return 0;
}

2011年6月12日日曜日

Boost の 32bit と 64bit ライブラリの両方を用意しておく方法

Boost のほとんどはヘッダファイルだけで動きますが、一部のライブラリはあらかじめビルドしておかなければなりません。

Windows だと x86 と x64 の両方ビルドしたいこともあって、私なりに共存策を探ってみました。Boost 1.46.1 で実行、確認した方法です。 コンパイラは Visual Studio 2010 を利用しました。

C:\usr\local\boost_1_46_1>bootstrap
#略
C:\usr\local\boost_1_46_1>bjam --build-dir=build\x86 --stagedir=stage\x86 address-model=32 -j 2
# 略
C:\usr\local\boost_1_46_1>bjam --build-dir=build\x64 --stagedir=stage\x64 address-model=64 -j 2
# 略

こうすると、c:\usr\local\boost_1_46_1\stage\x86\lib に 32bit バイナリが、c:\usr\local\boost_1_46_1\stage\x64\lib に 64bit バイナリができあがります。build ディレクトリは中間ファイルが入っているだけですので、消してもかまいません。

ヘッダファイルは共通ですので、c:\usr\local\boost_1_46_1 をインクルードパスに指定しておけば、32bit と 64bit 両方から利用できます。

以上自分用のメモもかねてまとめてみました。

2011年5月21日土曜日

Python スクリプトを実行ファイルに変換する方法

Python を新しく勉強してみようと思い立ち、誕生日プレゼントにもらった Learning Python を片手にPythonでプログラミングをしています。で、会社で小さなスクリプトを書くことがあるんですが、周りに使ってもらおうとしても「なんか変なプログラム入れないといけないんでしょ」と、Python のインタプリタが変なプログラム扱いされてしまい、そのままでは使ってもらえません。やはり、スクリプトはそのまま実行可能な形式に変換しないと、広くは使ってもらえないようです。

Python にはいくつか実行ファイルに変換するユーティリティがあるんですが、中でも今一番ホットなのは cx_freeze のようです。簡単に特徴を挙げますと、

  • クラスプラットフォーム。Python 動作可能な環境用には実行ファイルを作成可能
  • Python 2.5 から 3.2 まで対応
  • アクティブに開発が行われている

です。

Windows での使い方は簡単で、

C:\> cx_freeze script.py

とするだけで、その下の dist ディレクトリに実行ファイル他、必要なファイルが生成されます。後は生成された script.exe を実行するだけです。

Python などのスクリプト言語は、簡単なユーティリティを書くには適していると思いますが、広く使ってもらうためにはやはりこういうツールが欠かせません。