2
3

楽しく PythonコードとPyCUDAコードを比較できるインターフェースのツール。

Last updated at Posted at 2024-07-29

c15b8aef-7103-418d-a931-87c2eedbcddd.png

タイトル: 小さなエンジニアの大冒険

第1章: コードの世界へ

小学5年生のタケルは、コンピュータが大好きな男の子だった。彼はPythonというプログラミング言語を少し勉強しており、簡単なゲームや計算ツールを作ることができるようになった。しかし、彼の夢はもっと大きかった。彼は「GPU」を使ってコンピュータの計算をもっと速くする方法を学びたいと思っていた。

ある日、タケルは自分の部屋でパソコンに向かってプログラムを書いていると、友達のマサルがやってきた。「タケル、君が最近GPUの勉強をしているって聞いたよ。どう? うまくいってる?」

タケルは少し困った顔をした。「うーん、GPUのコードって難しいんだ。Pythonでは簡単にできるけど、PyCUDAっていうライブラリを使ってGPUコードを書くのはまだよくわからないんだ。」

第2章: 新しいツールとの出会い

その夜、タケルはパソコンでGPUの勉強を続けていたが、なかなか思うように進まなかった。そんな時、彼は「PythonとPyCUDAコードを並べて比較できるツール」を見つけた。このツールを使えば、PythonのコードをPyCUDA用のコードに変換する方法を学ぶことができるらしい。

「これだ!これなら僕も理解できるかもしれない。」タケルは嬉しそうに声を上げた。

彼はツールを使って、自分のPythonコードとそのPyCUDA版を並べて表示し、実行することができることに気づいた。これで、コードの違いが一目でわかる。

第3章: AIの助けを借りて

タケルはCHAT GPTを使ってさまざまなコードを試してみた。Pythonで簡単な計算をするコードを入力し、CHAT GPT が自動的にPyCUDA用に変換してくれるのを見て、彼は感心した。「すごい!でもやっぱり、PyCUDAのコードはちょっと難しいな。」

タケルはその通りだと思った。AIがコードを変換してくれるとはいえ、GPUの細かい設定や実行時間の計測など、まだまだ学ぶことがたくさんあった。「でも、これならAIの助けを借りて少しずつ学んでいけばいいんだ。」

第4章: 成長と冒険

タケルは毎日少しずつ勉強を続け、PythonとPyCUDAの両方を使いこなせるようになっていった。彼は、自分のプロジェクトでGPUを使って高速な計算を実現することができるようになり、学校の科学クラブでもその成果を発表することができた。

「最初は難しかったけど、このツールのおかげでGPUのことがよくわかるようになった。これからももっと勉強して、いろんなことに挑戦してみたいな!」と、タケルは自信に満ちた表情で語った。

そして、タケルの冒険はまだまだ続く。彼の夢は大きく、未来にはもっと多くの計算や発見が待っている。今日の学びが、明日の大きな一歩になることを信じて、彼は再びパソコンの前に座り、次のプロジェクトに取り掛かるのであった。

おわり

前回のあらすじ。

実行結果。

スクリーンショット 2024-07-30 003733.png

スクリーンショット 2024-07-30 003757.png

概要。

Pythonコード実行: Pythonコードをサブプロセスで実行し、結果を出力します。
PyCUDAコード実行: PyCUDAコードをサブプロセスでコンパイルし、GPUで実行します。
Gradioインターフェース: 左側にはPythonコードの入力ボックスと出力ボックス、右側にはPyCUDAコードの入力ボックスと出力ボックスがあります。
これにより、Google Colab上GPUでこのツールを使用し、PythonコードとPyCUDAコードをそれぞれ実行し、その結果を表示することができます。

PythonコードとPyCUDAコードを並べて比較することで、両者の違いを理解しやすくなります。

サンプルのPythonコードとPyCUDAコード。

テーマ1: マトリックスの行列積

Pythonコード(行列積)


import numpy as np

# 行列のサイズ
N = 512

# ランダムに初期化された2つの行列
A = np.random.rand(N, N).astype(np.float32)
B = np.random.rand(N, N).astype(np.float32)

# 行列積の計算
def matrix_multiply(A, B):
    return np.dot(A, B)

# 行列積を計算して表示
C = matrix_multiply(A, B)
print(C)

PyCUDAコード(GPUで行列積)


import pycuda.driver as cuda
import pycuda.autoinit
import pycuda.compiler as compiler
import numpy as np

# CUDAカーネル
kernel_code = """
__global__ void matrix_multiply(float *A, float *B, float *C, int N) {
    int row = threadIdx.y + blockIdx.y * blockDim.y;
    int col = threadIdx.x + blockIdx.x * blockDim.x;

    if (row < N && col < N) {
        float value = 0;
        for (int k = 0; k < N; k++) {
            value += A[row * N + k] * B[k * N + col];
        }
        C[row * N + col] = value;
    }
}
"""

# 行列のサイズ
N = 512
A = np.random.rand(N, N).astype(np.float32)
B = np.random.rand(N, N).astype(np.float32)
C = np.zeros((N, N), dtype=np.float32)

# CUDAカーネルのコンパイルと設定
module = compiler.SourceModule(kernel_code)
matrix_multiply = module.get_function("matrix_multiply")

# GPUメモリの割り当てとデータ転送
A_gpu = cuda.mem_alloc(A.nbytes)
B_gpu = cuda.mem_alloc(B.nbytes)
C_gpu = cuda.mem_alloc(C.nbytes)
cuda.memcpy_htod(A_gpu, A)
cuda.memcpy_htod(B_gpu, B)

# カーネルの実行
block_size = 16
grid_size = (N + block_size - 1) // block_size
matrix_multiply(A_gpu, B_gpu, C_gpu, np.int32(N), block=(block_size, block_size, 1), grid=(grid_size, grid_size))

# 結果の転送と表示
cuda.memcpy_dtoh(C, C_gpu)
print(C)
テーマ2: ハッシュ値のブルートフォース

Pythonコード(ハッシュ値のブルートフォース)


import hashlib
import itertools
import string

# ターゲットハッシュ
target_hash = '5e884898da28047151d0e56f8dc6292773603d0d'

# ブルートフォース関数
def brute_force(target_hash, length):
    chars = string.ascii_lowercase + string.digits
    for attempt in itertools.product(chars, repeat=length):
        attempt_str = ''.join(attempt)
        attempt_hash = hashlib.sha1(attempt_str.encode()).hexdigest()
        if attempt_hash == target_hash:
            return attempt_str
    return None

# 実行
length = 4
result = brute_force(target_hash, length)
print(f"Found: {result}")

PyCUDAコード(GPUでのハッシュブルートフォース)


import pycuda.driver as cuda
import pycuda.autoinit
import pycuda.compiler as compiler
import numpy as np
import hashlib
import itertools
import string

# CUDAカーネル
kernel_code = """
__device__ char chars[] = "abcdefghijklmnopqrstuvwxyz0123456789";
__device__ int chars_len = 36;

__global__ void brute_force_kernel(char *target_hash, char *result, int length) {
    int idx = threadIdx.x + blockIdx.x * blockDim.x;
    int num_chars = chars_len;
    
    // Generate combination index
    int temp = idx;
    int combination[length];
    for (int i = 0; i < length; i++) {
        combination[i] = temp % num_chars;
        temp /= num_chars;
    }
    
    // Generate string from combination
    char attempt[length + 1];
    for (int i = 0; i < length; i++) {
        attempt[i] = chars[combination[i]];
    }
    attempt[length] = '\\0';
    
    // Compute hash
    char attempt_hash[41];
    attempt_hash[40] = '\\0';
    
    // Here would be the actual SHA1 hash computation code.
    // Pseudo-code for hash computation and comparison:
    // hash_function(attempt, attempt_hash);
    
    if (strcmp(attempt_hash, target_hash) == 0) {
        for (int i = 0; i < length; i++) {
            result[i] = attempt[i];
        }
        result[length] = '\\0';
    }
}
"""

# 設定
target_hash = '5e884898da28047151d0e56f8dc6292773603d0d'
length = 4
block_size = 256
grid_size = (36**length + block_size - 1) // block_size

# データの準備
target_hash_gpu = cuda.mem_alloc(len(target_hash) + 1)
result_gpu = cuda.mem_alloc((length + 1) * 256)
cuda.memcpy_htod(target_hash_gpu, target_hash.encode())

# CUDAカーネルのコンパイルと実行
module = compiler.SourceModule(kernel_code)
brute_force_kernel = module.get_function("brute_force_kernel")
brute_force_kernel(target_hash_gpu, result_gpu, np.int32(length), block=(block_size, 1, 1), grid=(grid_size, 1))

# 結果の転送
result = np.zeros(length + 1, dtype=np.uint8)
cuda.memcpy_dtoh(result, result_gpu)
print(f"Found: {result.decode()}")
テーマ3: 全ビットコインのマイニング

Pythonコード(ビットコインのマイニング)


import hashlib
import time

# 簡略化されたビットコインマイニング関数
def mine_block(nonce, difficulty):
    prefix = '0' * difficulty
    while True:
        text = f"Block data {nonce}".encode()
        hash_result = hashlib.sha256(text).hexdigest()
        if hash_result.startswith(prefix):
            return nonce, hash_result
        nonce += 1

# 実行
difficulty = 4
nonce = 0
start_time = time.time()
result_nonce, result_hash = mine_block(nonce, difficulty)
end_time = time.time()

print(f"Nonce: {result_nonce}")
print(f"Hash: {result_hash}")
print(f"Time taken: {end_time - start_time} seconds")

PyCUDAコード(GPUでのビットコインマイニング)


import pycuda.driver as cuda
import pycuda.autoinit
import pycuda.compiler as compiler
import numpy as np
import hashlib
import time

# CUDAカーネル
kernel_code = """
__device__ char prefix[] = "0000";

__global__ void mine_block_kernel(unsigned int *nonces, char *results, int difficulty, int num_nonces) {
    int idx = threadIdx.x + blockIdx.x * blockDim.x;
    if (idx >= num_nonces) return;

    unsigned int nonce = nonces[idx];
    char text[256];
    sprintf(text, "Block data %u", nonce);
    
    // Compute hash
    unsigned char hash[32];
    // Pseudo-code for SHA256 hash computation:
    // hash_function(text, hash);
    
    bool match = true;
    for (int i = 0; i < difficulty; i++) {
        if (hash[i] != prefix[i]) {
            match = false;
            break;
        }
    }

    if (match) {
        results[idx] = nonce;
    }
}
"""

# 設定
difficulty = 4
num_nonces = 1024
block_size = 256
grid_size = (num_nonces + block_size - 1) // block_size

# データの準備
nonces = np.arange(num_nonces, dtype=np.uint32)
results = np.zeros(num_nonces, dtype=np.uint32)
nonces_gpu = cuda.mem_alloc(nonces.nbytes)
results_gpu = cuda.mem_alloc(results.nbytes)
cuda.memcpy_htod(nonces_gpu, nonces)

# CUDAカーネルのコンパイルと実行
module = compiler.SourceModule(kernel_code)
mine_block_kernel = module.get_function("mine_block_kernel")
start_time = time.time()
mine_block_kernel(nonces_gpu, results_gpu, np.int32(difficulty), np.int32(num_nonces), block=(block_size, 1, 1), grid=(grid_size, 1))
end_time = time.time()

# 結果の転送と表示
cuda.memcpy_dtoh(results, results_gpu)
print(f"Nonces with valid hash: {results[results > 0]}")
print(f"Time taken: {end_time - start_time} seconds")
テーマ4: ソートアルゴリズム(バブルソート)

Pythonコード(バブルソート)


import numpy as np

# バブルソートの関数
def bubble_sort(arr):
    n = len(arr)
    for i in range(n):
        for j in range(0, n-i-1):
            if arr[j] > arr[j+1]:
                arr[j], arr[j+1] = arr[j+1], arr[j]

# 配列の初期化とソート
arr = np.random.randint(0, 1000, size=1000)
bubble_sort(arr)

# 結果の表示
print(arr)

PyCUDAコード(GPUでのバブルソート)


import pycuda.driver as cuda
import pycuda.autoinit
import pycuda.compiler as compiler
import numpy as np

# CUDAカーネル
kernel_code = """
__global__ void bubble_sort_kernel(int *arr, int n) {
    int idx = threadIdx.x + blockIdx.x * blockDim.x;
    int i, j, temp;
    
    if (idx >= n) return;
    
    for (i = 0; i < n; i++) {
        for (j = 0; j < n-i-1; j++) {
            if (arr[j] > arr[j+1]) {
                temp = arr[j];
                arr[j] = arr[j+1];
                arr[j+1] = temp;
            }
        }
    }
}
"""

# 設定
n = 1000
arr = np.random.randint(0, 1000, size=n, dtype=np.int32)

# CUDAカーネルのコンパイルと設定
module = compiler.SourceModule(kernel_code)
bubble_sort_kernel = module.get_function("bubble_sort_kernel")

# GPUメモリの割り当てとデータ転送
arr_gpu = cuda.mem_alloc(arr.nbytes)
cuda.memcpy_htod(arr_gpu, arr)

# カーネルの実行
block_size = (256, 1, 1)
grid_size = (1, 1)
bubble_sort_kernel(arr_gpu, np.int32(n), block=block_size, grid=grid_size)

# 結果の転送と表示
cuda.memcpy_dtoh(arr, arr_gpu)
print(arr)

説明

処理時間の計測: time.time()を使用して、コードの実行開始と終了の時間を計測し、実行時間を算出しています。
出力: python_outputとpycuda_outputにはそれぞれPythonコードとPyCUDAコードの実行結果を表示し、python_durationとpycuda_durationには処理時間を表示します。
インターフェース: 入力ボックスのサイズを大きく設定し、実行時間も表示されるようにしました。

このコードをGoogle Colabで実行すると、両方のコードとその実行結果と処理時間を確認できるインターフェースが提供されます。

PythonコードとPyCUDAコードの実行ツールのコード。

# 必要なライブラリのインストールコマンド
!pip install gradio pycuda numpy

import gradio as gr
import subprocess
import time

def run_code(code):
    start_time = time.time()
    try:
        process = subprocess.Popen(
            ["python3", "-c", code],
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            text=True
        )
        output, error = process.communicate()
        duration = time.time() - start_time
        if process.returncode != 0:
            return error, f"Execution time: {duration:.4f} seconds"
        return output, f"Execution time: {duration:.4f} seconds"
    except Exception as e:
        duration = time.time() - start_time
        return str(e), f"Execution time: {duration:.4f} seconds"

def interface():
    with gr.Blocks() as demo:
        gr.Markdown("# PythonとPyCUDAコード実行ツール")

        with gr.Row():
            with gr.Column():
                gr.Markdown("### Pythonコード")
                python_code = gr.Textbox(
                    label="Python Code",
                    lines=20,
                    placeholder="Enter Python code here...",
                    elem_id="python_code"
                )
                python_output = gr.Textbox(
                    label="Output",
                    lines=10,
                    placeholder="Python code output will be shown here...",
                    interactive=False
                )
                python_duration = gr.Textbox(
                    label="Execution Time",
                    lines=1,
                    placeholder="Execution time will be shown here...",
                    interactive=False
                )
                python_run = gr.Button("Run Python Code")
                python_run.click(run_code, inputs=python_code, outputs=[python_output, python_duration])

            with gr.Column():
                gr.Markdown("### PyCUDAコード")
                pycuda_code = gr.Textbox(
                    label="PyCUDA Code",
                    lines=20,
                    placeholder="Enter PyCUDA code here...",
                    elem_id="pycuda_code"
                )
                pycuda_output = gr.Textbox(
                    label="Output",
                    lines=10,
                    placeholder="PyCUDA code output will be shown here...",
                    interactive=False
                )
                pycuda_duration = gr.Textbox(
                    label="Execution Time",
                    lines=1,
                    placeholder="Execution time will be shown here...",
                    interactive=False
                )
                pycuda_run = gr.Button("Run PyCUDA Code")
                pycuda_run.click(run_code, inputs=pycuda_code, outputs=[pycuda_output, pycuda_duration])

    demo.launch(share=True)

if __name__ == "__main__":
    interface()

2
3
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
2
3