はじめに
AtCoderでコンパイラコマンドを変更できたらなあ,ということはありませんか?
そんなときのための裏技です.
フォーマット
以下をPythonで実行することで,自由なコンパイラコマンドでコードをコンパイルできます.
import sys
import os
def run():
# 実行前であることを判定
if sys.argv[-1] == 'ONLINE_JUDGE':
import subprocess
# ファイルの名前
filename = 'mycode.cpp'
# コンパイラコマンド
command = 'g++ -std=gnu++17 mycode.cpp'
# コード
mycode = r'''
#include <bits/stdc++.h>
using namespace std;
int main() {
cout << "Hello world!" << endl;
}
'''
# ファイルを作成し,コードを記述
with open(filename, 'w') as f:
f.write(mycode)
# コンパイルエラーがerror.txtに出力されるようにコンパイルする
with open('error.txt', 'w') as f:
exit_code = subprocess.run(command.split(), stderr = f).returncode
# exit codeが0以外ならfailed.txtを作成
if exit_code:
with open('failed.txt', 'w') as f:
pass
exit()
# failed.txtが存在したら標準エラー出力にerror.txtを記述
if os.path.exists('failed.txt'):
with open('error.txt') as f:
sys.stderr.write(f.read())
exit(1)
# 実行コマンド
os.system('./a.out')
run()
使い方
変更する必要のある場所は4つです.
-
filename
:ファイルの名前 -
command
:コンパイラコマンド -
mycode
:コードの本体 - 最後の
os.system
の引数:実行コマンド
これさえ用意すればあとはコンパイルしてくれます.
仕組み
Language Test 202001 によれば,AtCoderのPythonではテストケース実行前に次のようなコマンドでコードが実行されます.
python3.8 {dirname}/{basename} ONLINE_JUDGE 2>/dev/null
そして,この ONLINE_JUDGE
コマンドライン引数は,テストケース実行時には無くなります.
これを利用すれば,ONLINE_JUDGE
の有無でテストケース実行前かを判定できます!!(おそらくこれはPython限定です)
フォーマットでは,if sys.argv[-1] == 'ONLINE_JUDGE':
として実行前にコンパイルしています.
また,コンパイルエラーが出た場合もerror.txt
に保存しておいてちゃんと出力されるようになっています.
応用例
Clangで bits/stdc++.h
を使ったり最適化したりする
AtCoderでGCCよりClangが劣っていると言われやすい点を解消できます.
AtCoderのClangは libc++
という標準ライブラリを使っているので,bits/stdc++.h
がありません.
そこで,AtCoderのコンパイラコマンド(Language Test 202001)から stdlib=libc++
を取り除けば,GCCと同じ libstdc++
となり,bits/stdc++.h
を使えるようになります.
同様に,Clangではコードからは #pragma clang optimize on
という最適化しかできませんでしたが,コンパイラコマンドをいじることで詳細まで最適化できるようになります.
スタックサイズを変更
稀にスタックオーバーフローを起こす場合は,先に次のコードを実行してからコンパイルさせれば通るかもしれません.
os.system('ulimit -s unlimited')
ただし,AtCoderではスタックサイズが既にギリギリなので,colun法 の方が無難です.
Cythonのコンパイル
AtCoderのCythonは,Cにコンパイルされるので,C++のSTLが使えなくなってしまいます.
そこで,自分でコンパイルします.
Cythonのコンパイルやその他については以下にまとめました:
NumbaAOT
Numbaは、通常ではコンパイルに400msほどかかり,メモリ消費もそこそこ多いため,JuliaやPyPyと比較して見劣りします.
そこでNumbaAOT(事前コンパイル)を使うと,メモリ消費がPyPyより少なく,起動時間もNumpyのimportの100msだけとなり魅力的になります.
以下の記事に使い方が載っています.
その他
- C++で
-fconstexpr-loop-limit=10000000000
などのオプションの追加 - Python/PyPyの事前コンパイル(
py_compile
) - Pythonのコンパイル時計算(ファイルの読み込みが重いので実用的でない).
おわりに
実行前に処理を行えることは色々と応用が効くので,新しいアイディアがあるかもしれません.
最後まで読んでいただきありがとうございました.
参考
AtCoderでCythonの力を開放する魔術詠唱 を参考に作成しました.