3
4

速いコードのプログラミングをアシストするAI。チャットで質問しながら、楽しくプログラミング。

Last updated at Posted at 2024-07-27

72ad17ba-1b72-4f7a-a9ff-2688057e6f78.png

ストーリー: AI支援によるプログラミングへの挑戦

プロローグ
エミリーは、小学校に通う10歳の少女だった。彼女の得意分野はPythonで、学校の授業や自宅で簡単なプログラムを作ったことがある程度だった。ある日、学校のプログラミングコンテストでC++を使った課題が出されることを知った時、エミリーは不安に襲われた。彼女はC++には全く触れたことがなく、どうすれば効率的なコードを書けるのか全く分からなかった。

新しいツールとの出会い
その時、エミリーは「AI支援プログラミングアシスタント」の存在を知った。このツールは、自然言語でプログラムの要件を入力するだけで、AIが自動的にコードを生成し、最適化までしてくれるというものだった。彼女は早速、このツールを試してみることにした。

初めての試行
エミリーは、「1から10000までのカウント処理を行うC++プログラムを書いてください。」というプロンプトを入力した。AIはすぐに彼女のためにコードを生成し、それを最適化する3つの異なる方法でコンパイルし、実行してくれた。彼女は結果を見て驚いた。AIが生成したコードはシンプルで、彼女自身が書くよりも遥かに効率的だった。さらに、最適化ありとなしの違いやAVX512を使用した最適化の効果を直感的に理解することができた。

AIの支援による成長
このツールを使い続けることで、エミリーはC++のコンパイルオプションや最適化に関する知識を少しずつ蓄えていった。AIが生成するコードを見て学び、次第に自分でも効率的なC++コードを書けるようになっていった。彼女は学校のプログラミングコンテストでも次第にC++の最適化に貢献できるようになり、クラスメートからの信頼も厚くなった。

エピローグ
数ヶ月後、エミリーは学校のプログラミングコンテストで見事に優勝を果たした。彼女はAI支援プログラミングアシスタントのおかげで、C++のプログラミングに対する自信を持つことができた。

前回のあらすじ。

プロンプトを入力すると速いコードを生成します。最適化して素早く実行します。

スクリーンショット 2024-07-28 052039.png

スクリーンショット 2024-07-28 051359.png

使用方法。

画面の右半分はプログラミングエディタ、左半分はチャットというインターフェースです。

Google corab (AVX-512が使用できます) の場合です。設定はGPUランタイムです。

チャットAIモデルは GPUランタイムで動きます。
C++コードは CPU で動きます。

C++コードの生成と最適化性能を評価するためのGradioインターフェースです。サンプルプロンプトを10個用意し、コンパイラの最適化設定(無し、最適化有り、AVX512)をラジオボタンで選択できるようにしました。

AVX-512は、Advanced Vector Extensions (AVX) の512ビット版で、特にデータ並列処理において高い性能を提供します。これを利用することで、さらに高速化が期待できますが、AVX-512がサポートされているCPUが必要です。

C++コードの生成と実行を支援するGradioインターフェースを提供します。ユーザーはプロンプトを入力するかサンプルプロンプトを選択し、言語モデルから生成されたコードを実行して最適化レベルごとの性能を評価することができます。

C++コードを生成して実行し、最適化設定に応じた性能を評価することができます。サンプルプロンプトはシンプルながら計算量が大きく、コンパイラの最適化効果を確認するのに適しています。

!pip install gradio transformers accelerate

from transformers import AutoModelForCausalLM, AutoTokenizer
import torch
import gradio as gr
import subprocess
import re
import time

# GPUが利用可能かどうかをチェック
device = "cuda" if torch.cuda.is_available() else "cpu"

# 言語モデルとトークナイザーの読み込み
language_model_name = "Qwen/Qwen2-1.5B-Instruct"
language_model = AutoModelForCausalLM.from_pretrained(
    language_model_name,
    torch_dtype="auto",
    device_map="auto"
)
tokenizer = AutoTokenizer.from_pretrained(language_model_name)

def generate_code(prompt):
    try:
        # プロンプトに基づいてコードを生成
        messages = [
            {"role": "system", "content": "あなたは優れたプログラミングアシスタントです。"},
            {"role": "user", "content": prompt}
        ]
        text = tokenizer.apply_chat_template(
            messages,
            tokenize=False,
            add_generation_prompt=True
        )
        model_inputs = tokenizer([text], return_tensors="pt").to(device)

        generated_ids = language_model.generate(
            model_inputs.input_ids,
            max_new_tokens=512
        )
        generated_ids = [
            output_ids[len(input_ids):] for input_ids, output_ids in zip(model_inputs.input_ids, generated_ids)
        ]

        response = tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0]
        return response
    except Exception as e:
        return f"コード生成エラー: {str(e)}"

def extract_code(response, language):
    code_match = re.search(rf'```{language}(.*?)```', response, re.DOTALL)
    if code_match:
        code = code_match.group(1).strip()
        return code
    else:
        return f"{language}コードが見つかりませんでした。"

def execute_code(code, optimization_level):
    try:
        file_name = "generated_code.cpp"
        executable = "generated_code"

        # 生成されたコードをファイルに保存
        with open(file_name, "w") as file:
            file.write(code)

        compile_commands = {
            "No Optimization": ["g++", "-o", executable, file_name],
            "With Optimization": ["g++", "-o", executable, file_name, "-O3"],
            "With AVX512": ["g++", "-o", executable, file_name, "-O3", "-mavx512f"]
        }

        compile_command = compile_commands[optimization_level]
        start_time = time.time()
        subprocess.run(compile_command, check=True)
        compile_time = time.time() - start_time

        start_time = time.time()
        result = subprocess.run(["./" + executable], capture_output=True, text=True)
        exec_time = time.time() - start_time

        return {
            'compile_command': ' '.join(compile_command),
            'compile_time': compile_time,
            'exec_time': exec_time,
            'output': result.stdout if result.returncode == 0 else result.stderr
        }
    except Exception as e:
        return f"コード実行エラー: {str(e)}"

def handle_interaction(input_text, sample_prompt):
    prompt = input_text or sample_prompt
    response = generate_code(prompt)
    code = extract_code(response, "cpp")
    return response, code

def handle_code_execution(code, optimization_level):
    result = execute_code(code, optimization_level)
    result_str = f"コンパイルコマンド: {result['compile_command']}\n"
    result_str += f"コンパイル時間: {result['compile_time']}\n"
    result_str += f"実行時間: {result['exec_time']}\n"
    result_str += f"実行結果: {result['output']}\n"
    return result_str

# サンプルプロンプト
sample_prompts = [
    "1から1,000,000までの数の合計を計算するC++プログラムを書いてください。",
    "1から1,000,000までの数の平方根を計算して合計を表示するC++プログラムを書いてください。",
    "100,000個の要素を持つ配列を初期化し、その各要素に1を足すC++プログラムを書いてください。",
    "フィボナッチ数列の第40項を計算する再帰的なC++プログラムを書いてください。",
    "1から100までの素数を判定して表示するC++プログラムを書いてください。",
    "100,000個の要素を持つ配列をバブルソートするC++プログラムを書いてください。",
    "1,000,000回の行列の乗算を行うC++プログラムを書いてください。",
    "100,000個の要素を持つ配列の各要素を逆順にするC++プログラムを書いてください。",
    "1から1,000,000までの数の階乗を計算するC++プログラムを書いてください。",
    "100,000回のハッシュマップの挿入と検索を行うC++プログラムを書いてください。"
]

with gr.Blocks() as iface:
    gr.Markdown("# 日本語プログラミングアシスタント")
    with gr.Row():
        with gr.Column():
            input_text = gr.Textbox(label="入力テキスト")
            sample_prompt = gr.Dropdown(sample_prompts, label="サンプルプロンプト")
            optimization_level = gr.Radio(["No Optimization", "With Optimization", "With AVX512"], label="最適化レベルの選択")
            generate_button = gr.Button("チャットの応答を生成")
            response_output = gr.Textbox(label="言語モデルの応答")

        with gr.Column():
            code_editor = gr.Textbox(label="プログラミングエディタ", lines=15)
            execute_button = gr.Button("コードを実行")
            execution_result_output = gr.Textbox(label="実行結果", lines=10)

    generate_button.click(handle_interaction, inputs=[input_text, sample_prompt], outputs=[response_output, code_editor])
    execute_button.click(handle_code_execution, inputs=[code_editor, optimization_level], outputs=execution_result_output)

if __name__ == "__main__":
    iface.launch(debug=True)


通常のC++コードをAVX-512命令を使用してコンパイルする場合、コード自体に特別な変更を加える必要はありません。

以下のように、通常のC++コードをコンパイル時に-mavx512fフラグを追加することで、AVX-512命令セットを有効にできます:


g++ -O3 -march=native -mavx512f -o sample_opt_simd sample.cpp

これは、コンパイラに対してAVX-512命令セットを使用して最適化を行うように指示します。

C++コンパイラーの最適化設定は、コンパイル時に指定するオプションによって制御されます。以下は、一般的なC++コンパイラー(特にGCCおよびClang)で使用される最適化オプションの一覧です。

GCCおよびClangの最適化オプション
-O0:

最適化を無効にします。
デバッグを容易にするため、コンパイル時間が短く、デバッグ情報が豊富です。
-O1:

基本的な最適化を行います。
コンパイル時間と実行時間のバランスを取ります。
-O2:

より多くの最適化を行い、実行時間の短縮を目指します。
大部分のアプリケーションに対して有効です。
-O3:

さらに多くの最適化を行います。
高度な最適化を含み、実行時間をさらに短縮しますが、コンパイル時間が長くなることがあります。
-Ofast:

-O3の最適化に加え、標準に準拠しない一部の最適化も行います。
数値計算の精度が若干犠牲になる可能性があります。
-Os:

実行速度よりも実行ファイルのサイズを最小化するように最適化します。
組み込みシステムなどの小さいメモリ容量のデバイスに有用です。
-Og:

デバッグのために最適化します。
デバッグ情報を保ちながら、適度な最適化を行います。
-march:

ターゲットとなるCPUのアーキテクチャを指定します。
例: -march=native はコンパイルするマシンのCPUに最適化します。
-mtune:

特定のCPUに合わせてパフォーマンスを調整しますが、他のCPUでも動作します。
例: -mtune=native はコンパイルするマシンのCPUに最適化します。
-mavx512f:

AVX-512命令セットを有効にします。
対応するCPUが必要です

AIモデルの生成内容は厳密に正しいという保証はありません。と書いておかないと。
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