Skip to content

Conversation

@kfly8
Copy link
Contributor

@kfly8 kfly8 commented Aug 12, 2021

やったこと

初期実装の Perlへの移植。

対応issue

セルフチェック

  • 静的解析 割愛
  • ビルドが通る
  • 動作確認

備考

@kfly8 kfly8 added the transplant 言語移植に関するタスク label Aug 12, 2021
@kfly8
Copy link
Contributor Author

kfly8 commented Aug 15, 2021

現在の進捗

- [x] POST "/initialize"
- [x] POST "/api/auth"
- [x] POST "/api/signout"
- [x] GET "/api/user/me"
- [ ] GET "/api/isu"
- [x] POST "/api/isu"
- [ ] GET "/api/isu/{jia_isu_uuid}/icon"
- [ ] GET "/api/isu/{jia_isu_uuid}/graph"
- [ ] GET "/api/isu/{jia_isu_uuid}"
- [ ] GET "/api/condition/{jia_isu_uuid}"
- [ ] GET "/api/trend"
- [ ] POST "/api/condition/{jia_isu_uuid}"
- [x] GET "/" 
- [x] GET "/isu/{jia_isu_uuid}/condition"
- [x] GET "/isu/{jia_isu_uuid}/graph"
- [x] GET "/isu/{jia_isu_uuid}"
- [x] GET "/register"
- [ ] CI動かす
- [ ] ベンチマーカーで動かす

@kfly8
Copy link
Contributor Author

kfly8 commented Aug 17, 2021

apitest_1 | 18:04:53.227999 ERR: prepare: Invalid JSON, wrong char '' found at position 3349
apitest_1 | 18:04:53.468372 ERR: prepare: critical: アプリケーション互換性チェックに失敗しました

https://github.com/isucon/isucon11-qualify/pull/1099/checks?check_run_id=3348738825#step:7:143

ふむー。

@kfly8
Copy link
Contributor Author

kfly8 commented Aug 17, 2021

環境まっさら(docker system prune -a)にしたら、ベンチ通り始めた。

1回目

00:41:21.070112 <=== LOAD END
00:41:21.070207 SCORE: 00.StartBenchmark       : 1
00:41:21.070233 SCORE: 01.GraphExcellent       : 0
00:41:21.070237 SCORE: 02.GraphGood            : 0
00:41:21.070239 SCORE: 03.GraphNormal          : 0
00:41:21.070241 SCORE: 04.GraphBad             : 0
00:41:21.070251 SCORE: 05.GraphWorst           : 0
00:41:21.070256 SCORE: 06.TodayGraphExcellent  : 0
00:41:21.070261 SCORE: 07.TodayGraphGood       : 0
00:41:21.070266 SCORE: 08.TodayGraphNormal     : 0
00:41:21.070271 SCORE: 09.TodayGraphBad        : 0
00:41:21.070275 SCORE: 10.TodayGraphWorst      : 0
00:41:21.070280 SCORE: 11.ReadInfoCondition    : 0
00:41:21.070284 SCORE: 12.ReadWarningCondition : 0
00:41:21.070289 SCORE: 13.ReadCriticalCondition: 0
00:41:21.070293 SCORE: _1.IsuInitialize        : 48
00:41:21.070298 SCORE: _2.NormalUserInitialize : 7
00:41:21.070302 SCORE: _3.ViewerInitialize     : 0
00:41:21.070308 SCORE: _4.ViewerDropout        : 0
00:41:21.070313 SCORE: _5.RepairIsu            : 0
00:41:21.070319 SCORE: _6.PostInfoCondition    : 283
00:41:21.070324 SCORE: _7.PostWarningCondition : 1165
00:41:21.070328 SCORE: _8.PostCriticalCondition: 4129
00:41:21.070333 SCORE: 00.StartBenchmark       : 1
00:41:21.070338 SCORE: 01.GraphExcellent       : 0
00:41:21.070342 SCORE: 02.GraphGood            : 0
00:41:21.070346 SCORE: 03.GraphNormal          : 0
00:41:21.070350 SCORE: 04.GraphBad             : 0
00:41:21.070354 SCORE: 05.GraphWorst           : 0
00:41:21.070358 SCORE: 06.TodayGraphExcellent  : 0
00:41:21.070362 SCORE: 07.TodayGraphGood       : 0
00:41:21.070366 SCORE: 08.TodayGraphNormal     : 0
00:41:21.070370 SCORE: 09.TodayGraphBad        : 0
00:41:21.070374 SCORE: 10.TodayGraphWorst      : 0
00:41:21.070378 SCORE: 11.ReadInfoCondition    : 0
00:41:21.070382 SCORE: 12.ReadWarningCondition : 0
00:41:21.070386 SCORE: 13.ReadCriticalCondition: 0
00:41:21.094389 score: 903(1000 - 97) : pass
00:41:21.094881 deduction: 0 / timeout: 978
00:41:21.095991 <=== sendResult finish

2回目

00:46:02.535265 score: 908(1000 - 92) : pass
00:46:02.535322 deduction: 0 / timeout: 921
00:46:02.535334 <=== sendResult finish
00:46:02.535888 --- ISU協会サービス END

3回目

00:47:56.813944 score: 921(1000 - 79) : pass
00:47:56.813981 deduction: 2 / timeout: 777
00:47:56.813991 <=== sendResult finish

@kfly8
Copy link
Contributor Author

kfly8 commented Aug 17, 2021

こけるときもある。謎

➜ make run-bench
target=perl docker compose -f docker-compose-bench.yml exec apitest go run ./main.go
00:58:11.221412 ISUCON11 benchmarker
00:58:11.221910 ===> PREPARE
00:58:11.221996 start: load initial data
00:58:11.233753 finish: load initial data
00:58:17.834563 ERR: prepare: status code: 期待する HTTP ステータスコード以外が返却されました (expected: 200,304): 401 (GET: /api/isu)
00:58:17.871998 ERR: prepare: status code: 期待する HTTP ステータスコード以外が返却されました (expected: 404): 401 (GET: /api/isu/8469e6f6-0d29-4052-87cf-dd9bfde45014)
00:58:17.908252 ERR: prepare: status code: 期待する HTTP ステータスコード以外が返却されました (expected: 404): 401 (GET: /api/isu/8469e6f6-0d29-4052-87cf-dd9bfde45014/icon)
00:58:17.961722 ERR: prepare: status code: 期待する HTTP ステータスコード以外が返却されました (expected: 404): 401 (GET: /api/isu/eb104b95-3532-4828-8550-89da717b9667/graph)
00:58:18.000163 ERR: prepare: status code: 期待する HTTP ステータスコード以外が返却されました (expected: 404): 401 (GET: /api/condition/15063e10-26fc-4dac-ae3a-1f195e0c6c4a)
00:58:18.241442 ERR: prepare: status code: 期待する HTTP ステータスコード以外が返却されました (expected: 409): 401 (POST: /api/isu)

@kfly8
Copy link
Contributor Author

kfly8 commented Aug 17, 2021

image

image

@kfly8 kfly8 changed the title [WIP] feat(perl) initial implementation feat(perl) initial implementation Aug 17, 2021

# ISUのコンディションの文字列からコンディションレベルを計算
sub calculate_condition_level($condition) {
my $warn_count = () = $condition =~ m!=true!g;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NodeJSでも議論になっていたんですけど、ここの処理で正規表現つかうのはパフォーマンス的にどうなんだろう?という話があります。 Perlの場合はどうですかね…?やっぱり index 関数使った方が早そうな気はするけど…
#452 (comment)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ベンチとってみます・・!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ベンチとってみました。
この差であれば、正規表現ままで良いと思ったのですが、どうですかね。

ベンチ結果

conditionにfalseが多いとregexの勝利
逆にtrueが多いとindexの処理

CASE1: is_dirty=false,is_overweight=false,is_broken=false
           Rate index regex
index 3308308/s    --  -25%
regex 4411077/s   33%    --
----------------
CASE2: is_dirty=false,is_overweight=true,is_broken=false
           Rate index regex
index 2477508/s    --   -5%
regex 2621439/s    6%    --
----------------
CASE3: is_dirty=true,is_overweight=false,is_broken=true
           Rate regex index
regex 1816839/s    --   -7%
index 1946613/s    7%    --
----------------
CASE4: is_dirty=true,is_overweight=true,is_broken=true
           Rate regex index
regex 1336170/s    --  -15%
index 1571331/s   18%    --
----------------
MIX: $case1,$case2,$case3,$case4
          Rate regex index
regex 481881/s    --   -4%
index 504123/s    5%    --

ベンチスクリプト

use strict;
use warnings;

use v5.34.0;
use Test2::V0;
use Benchmark qw(cmpthese);

my $case1 = 'is_dirty=false,is_overweight=false,is_broken=false';
my $case2 = 'is_dirty=false,is_overweight=true,is_broken=false';
my $case3 = 'is_dirty=true,is_overweight=false,is_broken=true';
my $case4 = 'is_dirty=true,is_overweight=true,is_broken=true';

sub warn_count_by_regex {
    my $condition = shift;
    my $warn_count = () = $condition =~ m!=true!g;
}

sub warn_count_by_index {
    my $condition = shift;

    my $count = 0;
    my $pos = 0;
    while ($pos != -1) {
        $pos = index($condition, "=true", $pos);
        if ($pos >= 0) {
            $count += 1;
            $pos += 5; # length "=true"
        }
    }
    return $count;
}

is warn_count_by_regex($case1), 0;
is warn_count_by_regex($case2), 1;
is warn_count_by_regex($case3), 2;
is warn_count_by_regex($case4), 3;

is warn_count_by_index($case1), 0;
is warn_count_by_index($case2), 1;
is warn_count_by_index($case3), 2;
is warn_count_by_index($case4), 3;

done_testing;


say "CASE1: $case1";
cmpthese(-1, {
    regex => sub {
        warn_count_by_regex($case1);
    },
    index => sub {
        warn_count_by_index($case1);
    },
});

say '----------------';
say "CASE2: $case2";
cmpthese(-1, {
    regex => sub {
        warn_count_by_regex($case2);
    },
    index => sub {
        warn_count_by_index($case2);
    },
});

say '----------------';
say "CASE3: $case3";
cmpthese(-1, {
    regex => sub {
        warn_count_by_regex($case3);
    },
    index => sub {
        warn_count_by_index($case3);
    },
});

say '----------------';
say "CASE4: $case4";
cmpthese(-1, {
    regex => sub {
        warn_count_by_regex($case4);
    },
    index => sub {
        warn_count_by_index($case4);
    },
});

say '----------------';
say 'MIX: $case1,$case2,$case3,$case4';
cmpthese(-1, {
    regex => sub {
        warn_count_by_regex($case1);
        warn_count_by_regex($case2);
        warn_count_by_regex($case3);
        warn_count_by_regex($case4);
    },
    index => sub {
        warn_count_by_index($case1);
        warn_count_by_index($case2);
        warn_count_by_index($case3);
        warn_count_by_index($case4);
    },
});

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ちょっと意外な気はしましたが、たしかにこれだけ差がないんであればこのままでも良いかもですね…

for (my $idx_keys = 0; $idx_keys < $keys->@*; $idx_keys++) {
my $key = $keys->[$idx_keys];

if (substr($condition_str, $idx_cond_str) !~ m!^$key!) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

このあたりも正規表現の方がスッキリしていてそれらしいとは思うのですが index と比較して遅すぎたりしないか心配です

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

こちらに関しては、indexだけの実装にした方がパフォーマンスが明らかに良さそうなので、変更しました!
fd1365e

ベンチ結果

          Rate  regex substr  index
regex  15459/s     --   -84%   -84%
substr 96376/s   523%     --    -1%
index  97745/s   532%     1%     --

ベンチスクリプト

use strict;
use warnings;

use v5.34;
use Test2::V0;
use Benchmark qw(cmpthese);

# ISUのコンディションの文字列がcsv形式になっているか検証
sub is_valid_condition_format_by_index {
    my $condition_str = shift;

    my $keys = ["is_dirty=", "is_overweight=", "is_broken="];
    my $value_true = "true";
    my $value_false = "false";

    my $idx_cond_str = 0;

    for (my $idx_keys = 0; $idx_keys < $keys->@*; $idx_keys++) {
        my $key = $keys->[$idx_keys];

        if (index($condition_str, $key, $idx_cond_str) != $idx_cond_str) {
            return !!0;
        }
        $idx_cond_str += length $key;

        if (index($condition_str, $value_true, $idx_cond_str) == $idx_cond_str) {
            $idx_cond_str += length $value_true;
        }
        elsif (index($condition_str, $value_false, $idx_cond_str) == $idx_cond_str) {
            $idx_cond_str += length $value_false;
        }
        else {
            return !!0;
        }

        if ($idx_keys < $keys->@* - 1) {
            if (index($condition_str, ",", $idx_cond_str) != $idx_cond_str) {
                return !!0;
            }
            $idx_cond_str++;
        }
    }

    return $idx_cond_str == length $condition_str;
}


sub is_valid_condition_format_by_substr {
    my $condition_str = shift;

    my $keys = ["is_dirty=", "is_overweight=", "is_broken="];
    my $value_true = "true";
    my $value_false = "false";

    my $idx_cond_str = 0;

    for (my $idx_keys = 0; $idx_keys < $keys->@*; $idx_keys++) {
        my $key = $keys->[$idx_keys];

        if (substr($condition_str, $idx_cond_str, length $key) ne $key) {
            return !!0;
        }
        $idx_cond_str += length $key;

        if (substr($condition_str, $idx_cond_str, length $value_true) eq $value_true) {
            $idx_cond_str += length $value_true;
        }
        elsif (substr($condition_str, $idx_cond_str, length $value_false) eq $value_false) {
            $idx_cond_str += length $value_false;
        }
        else {
            return !!0;
        }

        if ($idx_keys < $keys->@* - 1) {
            if (substr($condition_str, $idx_cond_str, 1) ne ",") {
                return !!0;
            }
            $idx_cond_str++;
        }
    }

    return $idx_cond_str == length $condition_str;
}

sub is_valid_condition_format_by_regex {
    my $condition_str = shift;

    my $keys = ["is_dirty=", "is_overweight=", "is_broken="];
    my $value_true = "true";
    my $value_false = "false";

    my $idx_cond_str = 0;

    for (my $idx_keys = 0; $idx_keys < $keys->@*; $idx_keys++) {
        my $key = $keys->[$idx_keys];

        if (substr($condition_str, $idx_cond_str) !~ m!^$key!) {
            return !!0;
        }
        $idx_cond_str += length $key;

        if (substr($condition_str, $idx_cond_str) =~ m!^$value_true!) {
            $idx_cond_str += length $value_true;
        }
        elsif (substr($condition_str, $idx_cond_str) =~ m!^$value_false!) {
            $idx_cond_str += length $value_false;
        }
        else {
            return !!0;
        }

        if ($idx_keys < $keys->@* - 1) {
            if (substr($condition_str, $idx_cond_str, 1) ne ",") {
                return !!0;
            }
            $idx_cond_str++;
        }
    }

    return $idx_cond_str == length $condition_str;
}


my @case = (
    'is_dirty=false,is_overweight=false,is_broken=false',
    'is_dirty=true,is_overweight=false,is_broken=false',
    'is_dirty=true,is_overweight=false,is_broken=true',
    'is_dirty=true,is_overweight=true,is_broken=true',
);

my @ng = (
    'is_dirty=1,is_overweight=0,is_broken=0',
    'is_dirty=1,is_overweight=0,is_broken=0,aaa',
    'is_overweight=true,is_broken=true,is_dirty=true',
);

for (@case) {
    note $_;
    ok is_valid_condition_format_by_index($_);
    ok is_valid_condition_format_by_substr($_);
    ok is_valid_condition_format_by_regex($_);
}

for (@ng) {
    note $_;
    ok !is_valid_condition_format_by_index($_);
    ok !is_valid_condition_format_by_substr($_);
    ok !is_valid_condition_format_by_regex($_);
}
done_testing;

cmpthese(-1, {
    index => sub {
        is_valid_condition_format_by_index($_) for @case;
        is_valid_condition_format_by_index($_) for @ng;
    },
    substr => sub {
        is_valid_condition_format_by_substr($_) for @case;
        is_valid_condition_format_by_substr($_) for @ng;
    },
    regex => sub {
        is_valid_condition_format_by_regex($_) for @case;
        is_valid_condition_format_by_regex($_) for @ng;
    },
});

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

こちらはこれだけ差がつくのに… warn_count との差は一体…

@sugyan sugyan self-requested a review August 18, 2021 02:04
kfly8 added 5 commits August 18, 2021 11:13
> バージョン14.1.2 (15611.3.10.1.5, 15611)
でみるとバグっていた

:memo: Kossyもこのコード入っているので抜いた方が良さそう
@sugyan sugyan self-requested a review August 18, 2021 04:41
Copy link
Contributor

@sugyan sugyan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

手元で負荷かけてみるとちょいちょい接続切れ (timeout?) るけど、検証失敗しているということはなさそうです。StarmanとかStarletとかかませば安定するかな…?

アプリケーションコードとしてはLGTMです

@kfly8
Copy link
Contributor Author

kfly8 commented Aug 18, 2021

ありがとうございます!

負荷かけ自分でもやってみます・・!

@kfly8 kfly8 merged commit 176ae39 into main Aug 18, 2021
@kfly8 kfly8 deleted the perl/init branch August 18, 2021 08:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

transplant 言語移植に関するタスク

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants