3
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

[競プロ]AtCoderでの削除不要で高速なデバッグ方法

Posted at

結論

環境変数で条件分岐させることで、提出時に削除不要かつ速度のオーバーヘッドを低く抑えることが可能です

import os

IS_ATCODER = bool(int(os.getenv("ATCODER", "0")))

def debug(*args, **kwargs) -> None:
    if not IS_ATCODER:
        print(*args, **kwargs)

for i in range(10 ** 7):
    debug(i)
    ...

はじめに

競技プログラミングのデバッグはどのように行なっていますか?

最も簡単な方法はprintデバッグでしょう

for i in range(10 ** 7):
    print(i)
    ...

しかしこの方法には大きな欠点があります。

それは、AtCoderに提出する際にprint文を削除する必要があり、削除を忘れるとWAを食らってしまいます。

image.png
私自身、何回かやらかしてます、、、

stderr(標準エラー出力)

削除する必要がないデバッグ方法として、stderrも候補です

import sys

def debug(*args, **kwargs) -> None:
    print(*args, **kwargs, file=sys.stderr)

for i in range(10 ** 7):
    debug(i)
    ...

しかし、この方法も標準エラー出力にかかる時間がオーバーヘッドになるため、TLEになるリスクがあります。

環境変数で実行環境を判別して条件分岐

さて、AtCoderの環境には以下の環境変数が指定されています(言語には依らないと思いますが、Python (PyPy 3.10-v7.3.12)で調査しています。なお公式で明言されているわけではないと思うので、利用は自己責任でお願いします)

環境変数名
PATH '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'
LANG 'C.UTF-8'
'ATCODER' '1'
'HOME' '/home/runner'

ちなみに、以下のコードで調べることができます

import os
print(os.environ)

この内、ATCODERの値を取得することで、環境を判別することが可能です

import os

IS_ATCODER = bool(int(os.getenv("ATCODER", "0")))

def debug(*args, **kwargs) -> None:
    if not IS_ATCODER:
        print(*args, **kwargs)

for i in range(10 ** 7):
    debug(i)
    ...

そして、この方法では標準エラー出力のようなI/Oが発生しないため、高速であることが期待できます

速度比較

実際に幾つかのコードで速度を比較してみました

1. デバッグなし

for i in range(10 ** 8):
    a = i  # ダミーの処理

image.png

2. stderrでデバッグ

import sys

def debug(*args, **kwargs) -> None:
    print(*args, **kwargs, file=sys.stderr)

for i in range(10 ** 8):
    a = i  # ダミーの処理
    debug("hello", a)

image.png

1より明らかに遅くなったことがわかります

3. 環境変数で条件分岐してデバッグ

import os

IS_ATCODER = bool(int(os.getenv("ATCODER", "0")))

def debug(*args, **kwargs) -> None:
    if not IS_ATCODER:
        print(*args, **kwargs)

for i in range(10 ** 8):
    a = i  # ダミーの処理
    debug("hello", a)

image.png

2より圧倒的に高速で、1とほぼ同じ速度(+33ms)で処理することができました

4. 環境変数で条件分岐してデバッグ(改良)

ちなみに、lambda式にすると+2msまでオーバーヘッドを抑えることができました

import os

IS_ATCODER = bool(int(os.getenv("ATCODER", "0")))

debug =  (lambda *args, **kwargs: None) if IS_ATCODER else print

for i in range(10 ** 8):
    a = i  # ダミーの処理
    debug("hello", a)

image.png

個人的には、可読性を優先した3の書き方で良いと思いますが、とにかく速度を求めたい方はこちらでも良いでしょう

まとめ

ぜひ、ご活用ください。
それでは、よい競プロライフを!

3
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?