スペースモラトリアムノカミサマ

日記+コメント付きブックマーク+他人にも役に立つかもしれない情報など。
(更新情報: RSS(ツッコミ付き) / RSS(ツッコミ抜き) / LIRS)

最近の TrackBack:
2004|01|02|03|04|05|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|03|04|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|06|07|08|09|10|
2011|01|02|03|07|10|11|
2012|02|03|04|07|08|09|
2013|01|06|07|
2014|02|08|09|11|
2015|09|
2016|01|05|
2017|07|
2018|05|

2009/07/08

_ [システム運用][ベンチマーク] Windows 版サイボウズガルーンは Linux 版より2倍重い

某環境にて、サイボウズガルーン1.5を Linux 版から Windows 版に乗り換えたところ、アクセス要求集中時などに度々レスポンスが激しく悪化しサービスに支障を来すという状況に陥るようになった。

また、サイボウズ ガルーン 2 をインストールするサーバーの環境構築例によれば、「クアッドコア インテル Xeon プロセッサー X5460 3.16GHz × 2」という環境下で、Linux 版は1000ユーザ、Windows 版は500ユーザでの利用を想定しているという。

Windows 版は Linux 版より倍近くパフォーマンスが悪いのだろうか? …ということで、サイボウズガルーン2体験版でベンチマークした結果が下記のデータだ。

テスト方法

  • WWW::Mechanize を使用し、ログイン後のトップページを GET、レスポンス完了するまでの時間を計測、30秒間で何回成功するか測る
  • …という処理を ithreads で同時接続数を1〜8と変えながらテスト

テストサーバ環境

  • CPU: Xeon X3350
  • MEM: 8GB
  • OS: ESXi4

上に作られた、

  • CPU: 2コアまたは4コア
  • MEM: 2GB
  • OS: CentOS 5 または Windows Web Server 2008 (ともに 32bit)

という仮想マシン。(thanks to ○△)

結果

CentOS 5
CPU2コア4コア
同時接続数成功回数平均応答時間成功回数平均応答時間
155545ms53566ms
2106573ms86699ms
4127962ms1201,015ms
81341,842ms1291,896ms
Windows Web Server 2008
CPU2コア4コア
同時接続数成功回数平均応答時間成功回数平均応答時間
1281,097ms291,048ms
2561,098ms541,126ms
4592,079ms981,241ms
8633,974ms1082,269ms

デュアルコア環境では Windows 版は Linux 版と比べてほぼ約2倍遅いということがベンチマークからも確認できた。(ただしこの数値だと、クアッドコア環境だとその差が縮まっていたり、Linux 版は4コアの方が2コアよりスコアが悪かったりする。これはバックグラウンドで余計なタスクを動かしてしまった影響と考えられるが、面倒なので追試はしない)

悪いのが OS なのかサイボウズなのかはわからないけど、とにかくサイボウズ動かすなら Windows 版という選択肢はないな…。

それにしても、仮想環境とはいえこの遅さは (Linux 版含めて) 酷い…

そりゃ高々1000ユーザ程度で「Xeon X5460 × 2」なんて豪勢な環境を要求するわけですよ…

関連:サイボウズコミュニティ - 過去のエントリー - 【ガルーン2】Windows版の動作環境

(使用したスクリプト)

#!/usr/bin/perl
use strict;
use warnings;
use threads;
use Getopt::Long;
use POSIX;
use Time::HiRes qw(gettimeofday tv_interval);
use WWW::Mechanize;
 
my $user; # ex: 'username'
my $pass; # ex: 'password'
my $url; # ex: 'http://example.com/cgi-bin/cbgrn/grn.cgi'
my $threads = 1;
my $span = 60;
 
GetOptions(
  '--user=s' => \$user,
  '--pass=s' => \$pass,
  '--url=s' => \$url,
  '--threads=i' => \$threads,
  '--span=i' => \$span,
);
 
if (!defined $user or !defined $pass or !defined $url) {
  print <<"EOD";
$0 [options]
  -us, --user=username
  -p, --pass=password
  -ur, --url=url
  -t, --threads=threads [1]
  -s, --span=seconds [60]
EOD
  exit;
}
 
my @threads;
for (1..$threads) {
  my $th = threads->new(\&stress, $_);
  push @threads, $th;
}
my ($success, $successtime, $failed) = (0, 0, 0);
 
foreach (@threads) {
  my $r = $_->join;
  $success += $r->[0];
  $successtime += $r->[1];
  $failed += $r->[2];
}
 
my $avg = $success ? $successtime / $success : 0;
printf "Total: Success: $success (Avg: %.2dms), Failed: $failed\n", $avg;
 
# 負荷テストスレッド
sub stress
{
  my $tid = shift;
  my $mech;
  my $count = -1;
  my ($success, $successtime, $failed) = (0, 0, 0);
  my $st;
  while (1) {
    my $result = undef;
    my $login = '';
    if ($count < 0 or ++$count >= 600) { # 定期的に mech 再構築
      $count = 0;
      $mech = WWW::Mechanize->new;
    }
    my $t0 = [gettimeofday];
    eval {
      if (my $res = $mech->get($url)) {
        if ($res->content =~ /<form name="?login"?/) {
          $mech->set_fields('_account' => $user, '_password' => $pass);
        #↓ガルーン1.5の場合
        #  $mech->set_fields('_Account' => $user, 'Password' => $pass);
          $mech->submit;
          die unless $mech->success;
          $login = ' login';
        }
        $result = int(tv_interval($t0) * 1000 + 0.5);
        if ($login eq ' login') {
          sleep 1;
          $st = [gettimeofday];
        }
        else {
          $success++;
          $successtime += $result;
        }
        $result .= ' ' . '*' x ($result / 1000) if $result >= 1000;
      }
      else {
        $result = 'failed';
        $failed++;
      }
    };
    if ($@) {
      $result = 'failed';
      $failed++;
    }
    print strftime('%Y-%m-%d %H:%M:%S', localtime) . ": T$tid: $result$login\n";
    last if tv_interval($st) > $span;
  }
  my $avg = $success ? $successtime / $success : 0;
  printf "T$tid: Success: $success (Avg: %dms), Failed: $failed\n", $avg;
  return [$success, $successtime, $failed];
}