OpenIDでテキストを共有できます
- 新規作成
- OpenIDで認証してエントリーを新規作成します
- 共有
- エントリーにはOpenIDで閲覧と編集に制限かけることができます
- 変更履歴
- 編集履歴もあるので、コラボレーションにも活用できます
新着エントリー
モブストライクについて全裸で考えてみた
課金による無限レベルアップ効率についてちょっと計算
ヤマモトさんがついったーでつぶやいてらっしゃったことを僕なりにまとめてみました。
レベルアップごとに上昇する必要経験値
1レベルごとに上昇する必要経験値 = 12.5exp
母国に旅行の経験値効率 = 95exp / 46体力 = 2.06
無限レベルアップ状態を維持するために上げなければならない体力 = 12.5exp / 2.06 = 6.07sp
レベルアップボーナスのみでの不足分 = 6.07 - 5 = 1.07
リワードポイントをスキルポイントと引き換えて無限レベルアップ
不足分1.07spを、課金でのスキルポイントブーストによって補う
RPのスキルポイント効率 = 14rp / 4sp = 3.5
不足分1.07spを補うのに必要なRP = 1.07sp * 3.5 = 3.75rp
レベルアップすると1rpボーナスがあるので、課金によって必要なRP = 3.75rp - 1rp = 2.75rp
リワードポイントを体力回復に使用して無限レベルアップ
同様に必要経験値量/2の体力で、RPによる体力回復を利用して無限LvUpを行うと
レベルアップごとに 10rp - 1rp = 9rp 必要になる。
また、連続レベルアップを止めた時点で保有する体力に大きな差もでる。
レベルアップの効率に焦点を当てると、ステータスアップのほうが効率が良い
RP購入による無限レベルアップの、日本円効率は、ステータスアップのほうが3倍以上良い。
15,000円課金すると 700 / 2.75 = 254.5レベルのスパイラルが可能になる
これにより上昇する体力は約1500となる
ヤマモトさんのご発言
http://twitter.com/ymtkyk/status/6643610318
http://twitter.com/ymtkyk/status/6643627785
LWP::UserAgent::parse_headerのベンチマーク
titleタグとか勝手にparseするやつね
結果
% perl parse_header.pl
Rate lwp lwp-header
lwp 543/s -- -19%
lwp-header 673/s 24% --
script
use Benchmark; use LWP::UserAgent; my $count = 3000; Benchmark::cmpthese( $count , { 'lwp' => sub { my $ua = LWP::UserAgent->new; my $res = $ua->get('http://localhost/') }, 'lwp-header' => sub { my $ua = LWP::UserAgent->new; $ua->parse_head(0); my $res = $ua->get('http://localhost/'); }, });
LWP vs AnyEvent::HTTP (+ Coro)
AnyEvent::HTTPで同時にアクセスしないようにしてテスト
% perl -I. anyevnet_http.pl
Benchmark: timing 3000 iterations of anyevent, lwp...
anyevent: 3 wallclock secs ( 1.57 usr + 1.27 sys = 2.84 CPU) @ 1056.34/s (n=3000)
lwp: 6 wallclock secs ( 4.25 usr + 1.22 sys = 5.47 CPU) @ 548.45/s (n=3000)
script
use Benchmark; use LWP::UserAgent; use AnyEvent; use AnyEvent::HTTP qw//; $AnyEvent::HTTP::MAX_PER_HOST = 1; my $count = 3000; timethese( $count , { 'lwp' => sub { my $ua = LWP::UserAgent->new; my $res = $ua->get('http://localhost/') }, 'anyevent' => sub { my $cv = AE::cv; AnyEvent::HTTP::http_get 'http://localhost/', sub { $cv->send('1'); }; $cv->recv; }, });
LWPは速くない
同じようにCoro::LWP
若干遅くなる
Benchmark: timing 3000 iterations of coro...
coro: 5 wallclock secs ( 1.75 usr + 3.83 sys = 5.58 CPU) @ 537.63/s (n=3000)
use Benchmark; use Coro; use Coro::LWP; use LWP::UserAgent; my $count = 3000; async { timethese( $count , { 'coro' => sub { my $ua = LWP::UserAgent->new; my $res = $ua->get('http://localhost/') }, }); exit; }; schedule;
Coro::Channelとtimeoutを組み合わせる
use Plack::Request; use HTTP::Request; use Time::HiRes; use Coro; use Coro::Channel; use Coro::AnyEvent; use Coro::LWP; use LWP::UserAgent; my $UA_WORKER_CORO_DESC_IN_WORK = "ua_worker_coro_desc_in_work"; my $UA_WORKER_CORO_DESC_NOT_WORK = "ua_worker_coro_desc_not_work"; my $UA_WORKER_CORO_TIMEOUT_MESSAGE = "ua_worker_coro_timeout"; my $MAX_UA_WORKER = 100; sub coro_timeout { my $timeout = shift; my $cb = shift; my $coro = $Coro::current; $coro->{timeout_at} = Time::HiRes::time() + $timeout; my $installed_destroy = $coro->{on_destroy_once} ? 1 : 0; $coro->{on_destroy_once} = $cb; if ( !$installed_destroy ) { $coro->on_destroy( sub { my $message = shift; return if $message ne $UA_WORKER_CORO_TIMEOUT_MESSAGE; my $cb = delete $coro->{on_destroy_once}; return unless $cb; $cb->($message); }); } } sub ua_worker { my $channel = shift; async { $Coro::current->desc($UA_WORKER_CORO_DESC_NOT_WORK); while(1) { my $req = $channel->get(); $Coro::current->desc($UA_WORKER_CORO_DESC_IN_WORK); my $ua = LWP::UserAgent->new( timeout => $req->[1] ); #timeout is ignored coro_timeout( $req->[1], sub { $req->[2]->send( LWP::UserAgent::_new_response( $req->[0], &HTTP::Status::RC_INTERNAL_SERVER_ERROR, "request timeout" ) ); }); my $res = $ua->request($req->[0]); $Coro::current->desc($UA_WORKER_CORO_DESC_NOT_WORK); $req->[2]->send($res); } } } # build worker threads my $worker_timer; sub build_ua_channel { warn "[$$] build channel"; my $channel = Coro::Channel->new(); for ( my $i=0; $i < $MAX_UA_WORKER; $i++ ) { ua_worker($channel); } $worker_timer = AnyEvent->timer( after => 0.5, interval => 0.5, cb => sub { #timeout my $now = Time::HiRes::time; my @lwp_coro = grep { $_->desc eq $UA_WORKER_CORO_DESC_IN_WORK } Coro::State::list; for my $coro (@lwp_coro) { if ($now > $coro->{timeout_at}) { $coro->cancel($UA_WORKER_CORO_TIMEOUT_MESSAGE); } } #spawn worker my $dead_worker = $MAX_UA_WORKER - scalar( grep { $_->desc eq $UA_WORKER_CORO_DESC_IN_WORK || $_->desc eq $UA_WORKER_CORO_DESC_NOT_WORK } Coro::State::list ); for ( my $i=0; $i < $dead_worker; $i++ ) { warn "[$$] respwan"; ua_worker($channel); } } ); return $channel; } my $ua_channel; sub async_ua_request { my $request = shift; my %args = @_; my $timeout = $args{timeout} || 180; my $cb = $args{cb} || sub {}; $ua_channel ||= build_ua_channel(); my $cv = AE::cv; $ua_channel->put( [$request, $timeout, $cv ] ); $cv->cb( sub { $cb->(shift->recv) } ); } my $hanlder = sub { my $env = shift; my $req = Plack::Request->new($env); my $cv = AE::cv; my $request = HTTP::Request->new( $env->{REQUEST_METHOD}, URI->new_abs($env->{REQUEST_URI},'http://localhost/'), [ map { (my $field = $_) =~ s/^HTTPS?_//; ( $field => $env->{$_} ); } grep { /^(?:HTTP|CONTENT|COOKIE)/i } keys %$env ], $req->raw_body ); async_ua_request( $request, timeout => 1, cb => sub { my $res = shift; my @res_header; $res->headers->scan(sub{ push @res_header, @_; }); $cv->send([ $res->code, \@res_header, [$res->content] ]); }); return sub { my $start_response = shift; $cv->cb( sub { $start_response->( shift->recv ); } ); }; }
TODO: global変数の部分をpackageにしてオブジェクトの中に納める
Coro::Channelをつかってみた
use Plack::Request; use HTTP::Request; use Data::Dumper; use Coro; use Coro::Channel; use Coro::AnyEvent; use Coro::LWP; use LWP::UserAgent; # worker threads sub build_channel { warn "[$$] build channel"; my $channel = Coro::Channel->new(); for my $i (0..100) { async { while(1) { my $req = $channel->get(); my $ua = LWP::UserAgent->new(); my $res = $ua->request($req->[0]); my @res_header; $res->headers->scan(sub{ push @res_header, @_; }); $req->[1]->send([ $res->code, \@res_header, [$res->content] ]); } } } return $channel; } my $channel; my $hanlder = sub { my $env = shift; my $req = Plack::Request->new($env); $channel ||= build_channel(); my $request = HTTP::Request->new( $env->{REQUEST_METHOD}, URI->new_abs($env->{REQUEST_URI},'http://localhost/'), [ map { (my $field = $_) =~ s/^HTTPS?_//; ( $field => $env->{$_} ); } grep { /^(?:HTTP|CONTENT|COOKIE)/i } keys %$env ], $req->raw_body ); my $cv = AE::cv; $channel->put( [$request,$cv ] ); return sub { my $start_response = shift; $cv->cb( sub { $start_response->( shift->recv ); } ); }; }
mobstrikeに於ける他攻略要素についての考察等
mobstrikeの攻略とか
自由に編集して下さって結構です。
各階層に於けるクリアボーナス
- ごろつき(lvls 1 - 4)
- 体力 -50秒短縮
- ちんぴら(lvls 5 - 8)
- スタミナ -50秒短縮
- ソルジャー(lvls 9 - 12)
- 体力 -40秒短縮
- 用心棒(lvls 13 - 17)
- スタミナ -40秒短縮
- ヒットマン(lvls 18 - 24)
- 体力 -30秒短縮
- 支部長(lvls 25 - 34)
- スタミナ -30秒短縮
- アンダーボス(lvls 60 - 99)
- 体力 -20秒短縮
- ボス(lvls 99 ~)
- スタミナ -20秒短縮
各々をクリア(全ての仕事をレベル3の習熟度100%)することでボーナスが享受出来る。勿論、ごろつき->ソルジャーの様な順にクリアしても問題無く効果を享受出来る様だ。
懸賞金TIPS
- 気になったマフィアに掛ける事で、現状のそいつを倒せるマフィアを探す事が出来る。
上限
- ファミリ
- 2510#(ただし、URLによる追加は可能な為、更なる高見は目指す事が可能。
Coro::LWPでtimeout + timeoutきりかえ
Coro::LWPでタイムアウトさせるに加えて、1つのasyncの中で複数回LWPを行ない、それぞれのtimeout、on_destory時の挙動を変更するtest
何がやりたいかというと
A->B->C
というリクエスと順は保ちつつ、それぞれを非同期でアクセスするものです
proxyっぽいものを実装する際に、Aで認証、Bで実際のアクセス、Cでフィルタ動作
を狙ってます。
use Time::HiRes; use Coro; use Coro::LWP; use LWP::UserAgent; use Data::Dumper; sub coro_timeout { my $timeout = shift; my $cb = shift; my $coro = $Coro::current; $coro->{timeout_at} = Time::HiRes::time() + $timeout; my $installed_destroy = $coro->{on_destroy_once} ? 1 : 0; $coro->{on_destroy_once} = $cb; if ( !$installed_destroy ) { $coro->on_destroy( sub { my $message = shift; my $cb = delete $coro->{on_destroy_once}; return unless $cb; return if $message ne 'timeout'; $cb->($message); }); } } my $parent_loop = 0; for my $timelimits ( [10,10,10],[1,10,10],[10,1,10],[10,10,1] ) { $parent_loop++; my $parentloop = $parent_loop; async { $Coro::current->desc("LWP"); my $childloop = 0; for my $timelimit ( @$timelimits ) { $childloop++; my $ua = LWP::UserAgent->new; coro_timeout($timelimit, sub { my $message = shift; warn sprintf "parent: %s, child: %s cancel because %s", $parentloop, $childloop, $message; }); my $res = $ua->get("http://localhost/tmp/sleep3.cgi"); warn "parent: $parentloop, child: $childloop done!"; } warn "parent: $parentloop done all!"; }; } my $w; $w = AnyEvent->timer ( after => 0.5, interval => 1, cb => sub { my $now = Time::HiRes::time; my @lwp_coro = grep { $_->desc eq "LWP" } Coro::State::list; for my $coro (@lwp_coro) { if ($now > $coro->{timeout_at}) { $coro->cancel("timeout"); } } if (@lwp_coro == 0) { exit; } } ); schedule;
実行結果
% perl coro_lwp.pl parent: 2, child: 1 cancel because timeout at coro_lwp.pl line 41. parent: 4, child: 1 done! at coro_lwp.pl line 44. parent: 3, child: 1 done! at coro_lwp.pl line 44. parent: 1, child: 1 done! at coro_lwp.pl line 44. parent: 3, child: 2 cancel because timeout at coro_lwp.pl line 41. parent: 4, child: 2 done! at coro_lwp.pl line 44. parent: 1, child: 2 done! at coro_lwp.pl line 44. parent: 4, child: 3 cancel because timeout at coro_lwp.pl line 41. parent: 1, child: 3 done! at coro_lwp.pl line 44. parent: 1 done all! at coro_lwp.pl line 46.
mobstrikeに於けるファミリーの追加規則
モブストライク( http://mfga.jp/ )に於ける、ファミリの追加規則等について。
MobStrike登録可能アカウントについて
※ログイン画面に表示されるAOLmailとAIMは同じAOL Accountによって機能する物であるので、同様の物と判断し、AOLMailに統一した。
MobStrike参加前
- mixi
- 登録録する時のマイミクシィの中のMobStrike参加者分のファミリ数となる。
- 登録する時のフォローしている最新から100人目迄の人の中からMobStrike参加者が居るとその人数分のファミリ数となる。
- 各種メール
- 登録する時の相互アドレス登録しているMobStrike参加者分のファミリ数となる。
MobStrike参加後
- mixi
- 登録録する時のマイミクシィの中の誰かがMobStrikeに参加するとファミリが増える。
- 登録する時のフォローしている最新から100人目迄の人の中からMobStrikeに参加するとファミリが増える。
- 各種メール
- 登録する時の相互アドレス登録の中の誰かがMobStrikeに参加するとファミリが増える。
新たにファミリを増やしたい場合
- ファミリーにて取得出来るURLから登録して貰うと各種条件に関わらずファミリが1増える。
- ボスにリワードポイント20ポイントを払ってファミリーを2×n人追加出来る。
ポイント
- 初期ファミリ数や随時追加ファミリ数は登録時のフォロワやマイミクシィなどの相互登録の数による固定数である事。
- twitterはAPIに取得制限があるため、登録時の最新フォロワの100人目までしか取れない為、フォローしまくってフォローして貰いやすくするという方法によって妨害か可能である事。
- mixiや各種メールは取得の制限が無い為、幾らでも増やせる事。
参考資料
注意
尚、結構な情報が推測した物なので間違っていたらコメントや編集などをして頂けると幸いです。
又、加筆修正は随時受け付けております。
AnyEvent::MPRPCでMessagePack RPC
MessagePack RPCは全てのメッセージにシーケンス番号をいれることで、1つのTCP接続でpipeliningのような遅延リターンができる。
Gearmanでやりたかったいめーじはこちらのほうがちかい。
server側
use AnyEvent; use AnyEvent::MPRPC::Server; my $server = AnyEvent::MPRPC::Server->new( port => 4423 ); $server->reg_cb( 'sleep' => sub { my ( $res_cv, $sleep ) = @_; warn $sleep; my $t; $t = AE::timer $sleep, 0 , sub { undef $t; $res_cv->result("$sleep done!"); }; }, ); AE::cv->recv;
client側
my $work_count = 0; my @sleeps = qw/1 5 2 4 3 2 1/; my $worked = 0; my $client = AnyEvent::MPRPC::Client->new( host => '127.0.0.1', port => 4423, ); for my $sleep ( @sleeps ) { my $n = $work_count++; $client->call( 'sleep' => $sleep )->cb( sub { my $res = shift->recv; warn "$n: $sleep: $res"; $worked++; }); }; my $w; $w = AnyEvent->timer( after => 0, interval => 1, cb => sub { if ($worked == @sleeps) { exit; } } ); AE::cv->recv;
実行結果
% perl anyevent_mprpc_client.pl0: 1: 1 done! at anyevent_mprpc_client.pl line 20. 6: 1: 1 done! at anyevent_mprpc_client.pl line 20. 2: 2: 2 done! at anyevent_mprpc_client.pl line 20. 5: 2: 2 done! at anyevent_mprpc_client.pl line 20. 4: 3: 3 done! at anyevent_mprpc_client.pl line 20. 3: 4: 4 done! at anyevent_mprpc_client.pl line 20. 1: 5: 5 done! at anyevent_mprpc_client.pl line 20.
AnyEvent::Gearman::Worker
ちょっと勘違いしていた。pipeliningのようなことができるわけじゃないから、1つの接続で
非同期に処理をしてくれるわけじゃない。
これは結果、普通のworkerとほぼ同じになる
use AnyEvent::Gearman::Worker; my $worker = AnyEvent::Gearman::Worker->new( job_servers => ['127.0.0.1:4730'], ); $worker->register_function( 'sleep' => sub { my $job = shift; my $sleep = $job->workload; warn $sleep; my $t; $t = AnyEvent->timer( after => $sleep, cb => sub { warn "$sleep done!"; undef $t; $job->complete("$sleep done!"); } ); } ); AE::cv->recv;
非同期/パラレルを狙うならこうする(力技
my @workers; for ( 1 .. 4 ) { my $worker = AnyEvent::Gearman::Worker->new( job_servers => ['127.0.0.1:4730'], ); push @workers, $worker; $worker->register_function( 'sleep' => sub { my $job = shift; my $sleep = $job->workload; warn $sleep; my $t; $t = AnyEvent->timer( after => $sleep, cb => sub { warn "$sleep done!"; undef $t; $job->complete("$sleep done!"); } ); } ); } AE::cv->recv;