neuroscan-ai / scripts /stress_test.py
cyd0806's picture
Upload NeuroScan AI code
35b365b verified
#!/usr/bin/env python3
"""
NeuroScan AI ๅนถๅ‘ๅŽ‹ๅŠ›ๆต‹่ฏ•
ๆต‹่ฏ• CPU/GPU ๅณฐๅ€ผไฝฟ็”จๆƒ…ๅ†ต๏ผŒๆ”ฏๆŒ 2-3 ไปปๅŠกๅนถๅ‘
"""
import os
import sys
import time
import threading
import multiprocessing
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor, as_completed
from pathlib import Path
import psutil
import numpy as np
# ๆทปๅŠ ้กน็›ฎๆ น็›ฎๅฝ•ๅˆฐ่ทฏๅพ„
sys.path.insert(0, str(Path(__file__).parent.parent))
# ๅ…จๅฑ€็›‘ๆŽงๆ•ฐๆฎ
monitor_data = {
"cpu_percent": [],
"memory_percent": [],
"memory_gb": [],
"gpu_memory_gb": [],
"gpu_util": []
}
stop_monitor = False
def get_gpu_stats():
"""่Žทๅ–GPU็Šถๆ€"""
try:
import torch
if torch.cuda.is_available():
# ่Žทๅ–ๅฝ“ๅ‰GPU็š„ๆ˜พๅญ˜ไฝฟ็”จ
allocated = torch.cuda.memory_allocated() / (1024**3)
reserved = torch.cuda.memory_reserved() / (1024**3)
# ไฝฟ็”จnvidia-smi่Žทๅ–ๆ€ปไฝ“ๆ˜พๅญ˜
import subprocess
result = subprocess.run(
['nvidia-smi', '--query-gpu=memory.used,utilization.gpu', '--format=csv,noheader,nounits', '-i', '0'],
capture_output=True, text=True
)
if result.returncode == 0:
parts = result.stdout.strip().split(',')
mem_used = float(parts[0]) / 1024 # ่ฝฌๆขไธบGB
gpu_util = float(parts[1])
return mem_used, gpu_util
return allocated, 0
return 0, 0
except:
return 0, 0
def resource_monitor(interval=0.5):
"""ๅŽๅฐ่ต„ๆบ็›‘ๆŽง็บฟ็จ‹"""
global stop_monitor, monitor_data
while not stop_monitor:
# CPU
cpu_percent = psutil.cpu_percent(interval=None)
monitor_data["cpu_percent"].append(cpu_percent)
# ๅ†…ๅญ˜
mem = psutil.virtual_memory()
monitor_data["memory_percent"].append(mem.percent)
monitor_data["memory_gb"].append(mem.used / (1024**3))
# GPU
gpu_mem, gpu_util = get_gpu_stats()
monitor_data["gpu_memory_gb"].append(gpu_mem)
monitor_data["gpu_util"].append(gpu_util)
time.sleep(interval)
def run_single_pipeline(task_id, data_pair):
"""่ฟ่กŒๅ•ไธชๅˆ†ๆžๆตๆฐด็บฟ"""
baseline_path, followup_path = data_pair
print(f" ๐Ÿ”„ ไปปๅŠก {task_id}: ๅผ€ๅง‹ๅค„็† {Path(baseline_path).parent.name}")
start_time = time.time()
try:
# ๅฏผๅ…ฅๆจกๅ—
from app.services.dicom import DicomLoader
from app.services.registration import ImageRegistrator
from app.services.analysis import ChangeDetector
loader = DicomLoader()
registrator = ImageRegistrator()
detector = ChangeDetector()
# 1. ๅŠ ่ฝฝๆ•ฐๆฎ
t0 = time.time()
baseline_data, _ = loader.load_nifti(baseline_path)
followup_data, _ = loader.load_nifti(followup_path)
load_time = time.time() - t0
# 2. ้…ๅ‡†
t0 = time.time()
reg_result = registrator.register(followup_data, baseline_data, use_deformable=True)
reg_time = time.time() - t0
# 3. ๅ˜ๅŒ–ๆฃ€ๆต‹
t0 = time.time()
change_result = detector.detect_changes(baseline_data, reg_result["warped_image"])
detect_time = time.time() - t0
total_time = time.time() - start_time
return {
"task_id": task_id,
"status": "success",
"load_time": load_time,
"reg_time": reg_time,
"detect_time": detect_time,
"total_time": total_time,
"data_shape": baseline_data.shape
}
except Exception as e:
return {
"task_id": task_id,
"status": "error",
"error": str(e),
"total_time": time.time() - start_time
}
def run_segmentation_task(task_id, nifti_path):
"""่ฟ่กŒๅˆ†ๅ‰ฒไปปๅŠก๏ผˆGPUๅฏ†้›†ๅž‹๏ผ‰"""
print(f" ๐Ÿง  ๅˆ†ๅ‰ฒไปปๅŠก {task_id}: ๅผ€ๅง‹ๅค„็†")
start_time = time.time()
try:
import torch
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
from app.services.segmentation import OrganSegmentor
segmentor = OrganSegmentor()
# ๆ‰ง่กŒๅˆ†ๅ‰ฒ
from app.services.dicom import DicomLoader
loader = DicomLoader()
data, _ = loader.load_nifti(nifti_path)
# ๅˆ†ๅ‰ฒๆŽจ็†
result = segmentor.segment(data)
total_time = time.time() - start_time
# ่ฎฐๅฝ•GPUๅณฐๅ€ผ
peak_mem = torch.cuda.max_memory_allocated() / (1024**3)
return {
"task_id": task_id,
"status": "success",
"total_time": total_time,
"gpu_peak_gb": peak_mem
}
except Exception as e:
return {
"task_id": task_id,
"status": "error",
"error": str(e),
"total_time": time.time() - start_time
}
def get_test_data_pairs(data_dir, max_pairs=5):
"""่Žทๅ–ๆต‹่ฏ•ๆ•ฐๆฎๅฏน"""
data_path = Path(data_dir) / "processed"
pairs = []
for case_dir in sorted(data_path.glob("real_lung_*"))[:max_pairs]:
baseline = case_dir / "baseline.nii.gz"
followup = case_dir / "followup.nii.gz"
if baseline.exists() and followup.exists():
pairs.append((str(baseline), str(followup)))
return pairs
def print_stats(title, data_list):
"""ๆ‰“ๅฐ็ปŸ่ฎกไฟกๆฏ"""
if not data_list:
return
arr = np.array(data_list)
print(f" {title}:")
print(f" ๅนณๅ‡: {np.mean(arr):.2f}")
print(f" ๅณฐๅ€ผ: {np.max(arr):.2f}")
print(f" ๆœ€ๅฐ: {np.min(arr):.2f}")
def main():
global stop_monitor, monitor_data
print("=" * 70)
print("๐Ÿ”ฅ NeuroScan AI ๅนถๅ‘ๅŽ‹ๅŠ›ๆต‹่ฏ•")
print("=" * 70)
# ็ณป็ปŸไฟกๆฏ
print(f"\n๐Ÿ“Š ็ณป็ปŸ้…็ฝฎ:")
print(f" CPU ๆ ธๅฟƒ: {psutil.cpu_count(logical=False)} ็‰ฉ็†ๆ ธ / {psutil.cpu_count()} ้€ป่พ‘ๆ ธ")
print(f" ๆ€ปๅ†…ๅญ˜: {psutil.virtual_memory().total / (1024**3):.1f} GB")
try:
import torch
if torch.cuda.is_available():
print(f" GPU: {torch.cuda.get_device_name(0)}")
print(f" GPUๆ˜พๅญ˜: {torch.cuda.get_device_properties(0).total_memory / (1024**3):.1f} GB")
except:
print(" GPU: ไธๅฏ็”จ")
# ่Žทๅ–ๆต‹่ฏ•ๆ•ฐๆฎ
data_dir = Path(__file__).parent.parent / "data"
pairs = get_test_data_pairs(data_dir, max_pairs=5)
if len(pairs) < 2:
print("\nโŒ ๆต‹่ฏ•ๆ•ฐๆฎไธ่ถณ๏ผŒ้œ€่ฆ่‡ณๅฐ‘ 2 ๅฏนๆ•ฐๆฎ")
print(" ่ฏทๅ…ˆ่ฟ่กŒ: python scripts/download_datasets.py --dataset learn2reg")
return
print(f"\n๐Ÿ“ ๆ‰พๅˆฐ {len(pairs)} ๅฏนๆต‹่ฏ•ๆ•ฐๆฎ")
# ========================================
# ๆต‹่ฏ• 1: ๅ•ไปปๅŠกๅŸบๅ‡†
# ========================================
print("\n" + "=" * 70)
print("๐Ÿ“Œ ๆต‹่ฏ• 1: ๅ•ไปปๅŠกๅŸบๅ‡†ๆต‹่ฏ•")
print("=" * 70)
monitor_data = {k: [] for k in monitor_data}
stop_monitor = False
# ๅฏๅŠจ็›‘ๆŽง
monitor_thread = threading.Thread(target=resource_monitor, args=(0.2,))
monitor_thread.start()
result = run_single_pipeline(1, pairs[0])
stop_monitor = True
monitor_thread.join()
if result["status"] == "success":
print(f"\n โœ… ๅ•ไปปๅŠกๅฎŒๆˆ:")
print(f" ๅŠ ่ฝฝๆ—ถ้—ด: {result['load_time']:.2f}s")
print(f" ้…ๅ‡†ๆ—ถ้—ด: {result['reg_time']:.2f}s")
print(f" ๆฃ€ๆต‹ๆ—ถ้—ด: {result['detect_time']:.2f}s")
print(f" ๆ€ปๆ—ถ้—ด: {result['total_time']:.2f}s")
print(f"\n ๐Ÿ“ˆ ๅ•ไปปๅŠก่ต„ๆบๅณฐๅ€ผ:")
print(f" CPU ๅณฐๅ€ผ: {max(monitor_data['cpu_percent']):.1f}%")
print(f" ๅ†…ๅญ˜ๅณฐๅ€ผ: {max(monitor_data['memory_gb']):.1f} GB ({max(monitor_data['memory_percent']):.1f}%)")
print(f" GPUๆ˜พๅญ˜ๅณฐๅ€ผ: {max(monitor_data['gpu_memory_gb']):.2f} GB")
single_task_time = result["total_time"]
single_cpu_peak = max(monitor_data['cpu_percent'])
single_mem_peak = max(monitor_data['memory_gb'])
# ========================================
# ๆต‹่ฏ• 2: 2 ไปปๅŠกๅนถๅ‘
# ========================================
print("\n" + "=" * 70)
print("๐Ÿ“Œ ๆต‹่ฏ• 2: 2 ไปปๅŠกๅนถๅ‘ๅŽ‹ๅŠ›ๆต‹่ฏ•")
print("=" * 70)
monitor_data = {k: [] for k in monitor_data}
stop_monitor = False
monitor_thread = threading.Thread(target=resource_monitor, args=(0.2,))
monitor_thread.start()
start_time = time.time()
results = []
with ThreadPoolExecutor(max_workers=2) as executor:
futures = []
for i, pair in enumerate(pairs[:2]):
futures.append(executor.submit(run_single_pipeline, i+1, pair))
for future in as_completed(futures):
results.append(future.result())
concurrent_2_time = time.time() - start_time
stop_monitor = True
monitor_thread.join()
success_count = sum(1 for r in results if r["status"] == "success")
print(f"\n โœ… 2ไปปๅŠกๅนถๅ‘ๅฎŒๆˆ: {success_count}/2 ๆˆๅŠŸ")
print(f" ๆ€ป่€—ๆ—ถ: {concurrent_2_time:.2f}s")
print(f" ๅนถ่กŒๆ•ˆ็އ: {(single_task_time * 2 / concurrent_2_time * 100):.1f}%")
print(f"\n ๐Ÿ“ˆ 2ไปปๅŠกๅนถๅ‘่ต„ๆบๅณฐๅ€ผ:")
print(f" CPU ๅณฐๅ€ผ: {max(monitor_data['cpu_percent']):.1f}%")
print(f" ๅ†…ๅญ˜ๅณฐๅ€ผ: {max(monitor_data['memory_gb']):.1f} GB ({max(monitor_data['memory_percent']):.1f}%)")
print(f" GPUๆ˜พๅญ˜ๅณฐๅ€ผ: {max(monitor_data['gpu_memory_gb']):.2f} GB")
concurrent_2_cpu = max(monitor_data['cpu_percent'])
concurrent_2_mem = max(monitor_data['memory_gb'])
# ========================================
# ๆต‹่ฏ• 3: 3 ไปปๅŠกๅนถๅ‘
# ========================================
print("\n" + "=" * 70)
print("๐Ÿ“Œ ๆต‹่ฏ• 3: 3 ไปปๅŠกๅนถๅ‘ๅŽ‹ๅŠ›ๆต‹่ฏ•")
print("=" * 70)
if len(pairs) < 3:
print(" โš ๏ธ ๆ•ฐๆฎไธ่ถณ๏ผŒ่ทณ่ฟ‡ 3 ไปปๅŠกๆต‹่ฏ•")
else:
monitor_data = {k: [] for k in monitor_data}
stop_monitor = False
monitor_thread = threading.Thread(target=resource_monitor, args=(0.2,))
monitor_thread.start()
start_time = time.time()
results = []
with ThreadPoolExecutor(max_workers=3) as executor:
futures = []
for i, pair in enumerate(pairs[:3]):
futures.append(executor.submit(run_single_pipeline, i+1, pair))
for future in as_completed(futures):
results.append(future.result())
concurrent_3_time = time.time() - start_time
stop_monitor = True
monitor_thread.join()
success_count = sum(1 for r in results if r["status"] == "success")
print(f"\n โœ… 3ไปปๅŠกๅนถๅ‘ๅฎŒๆˆ: {success_count}/3 ๆˆๅŠŸ")
print(f" ๆ€ป่€—ๆ—ถ: {concurrent_3_time:.2f}s")
print(f" ๅนถ่กŒๆ•ˆ็އ: {(single_task_time * 3 / concurrent_3_time * 100):.1f}%")
print(f"\n ๐Ÿ“ˆ 3ไปปๅŠกๅนถๅ‘่ต„ๆบๅณฐๅ€ผ:")
print(f" CPU ๅณฐๅ€ผ: {max(monitor_data['cpu_percent']):.1f}%")
print(f" ๅ†…ๅญ˜ๅณฐๅ€ผ: {max(monitor_data['memory_gb']):.1f} GB ({max(monitor_data['memory_percent']):.1f}%)")
print(f" GPUๆ˜พๅญ˜ๅณฐๅ€ผ: {max(monitor_data['gpu_memory_gb']):.2f} GB")
concurrent_3_cpu = max(monitor_data['cpu_percent'])
concurrent_3_mem = max(monitor_data['memory_gb'])
# ========================================
# ๆต‹่ฏ• 4: GPU ๅˆ†ๅ‰ฒไปปๅŠก (ๅฏ้€‰)
# ========================================
print("\n" + "=" * 70)
print("๐Ÿ“Œ ๆต‹่ฏ• 4: GPU ๅˆ†ๅ‰ฒไปปๅŠกๅณฐๅ€ผๆต‹่ฏ•")
print("=" * 70)
try:
import torch
if torch.cuda.is_available():
torch.cuda.reset_peak_memory_stats()
monitor_data = {k: [] for k in monitor_data}
stop_monitor = False
monitor_thread = threading.Thread(target=resource_monitor, args=(0.2,))
monitor_thread.start()
# ่ฟ่กŒๅˆ†ๅ‰ฒ
seg_result = run_segmentation_task(1, pairs[0][0])
stop_monitor = True
monitor_thread.join()
if seg_result["status"] == "success":
print(f"\n โœ… ๅˆ†ๅ‰ฒไปปๅŠกๅฎŒๆˆ:")
print(f" ่€—ๆ—ถ: {seg_result['total_time']:.2f}s")
print(f" GPUๅณฐๅ€ผ: {seg_result.get('gpu_peak_gb', max(monitor_data['gpu_memory_gb'])):.2f} GB")
else:
print(f"\n โš ๏ธ ๅˆ†ๅ‰ฒไปปๅŠก่ทณ่ฟ‡: {seg_result.get('error', 'unknown')}")
print(f"\n ๐Ÿ“ˆ ๅˆ†ๅ‰ฒไปปๅŠก่ต„ๆบๅณฐๅ€ผ:")
print(f" CPU ๅณฐๅ€ผ: {max(monitor_data['cpu_percent']):.1f}%")
print(f" ๅ†…ๅญ˜ๅณฐๅ€ผ: {max(monitor_data['memory_gb']):.1f} GB")
print(f" GPUๆ˜พๅญ˜ๅณฐๅ€ผ: {max(monitor_data['gpu_memory_gb']):.2f} GB")
gpu_seg_peak = max(monitor_data['gpu_memory_gb'])
else:
print(" โš ๏ธ GPU ไธๅฏ็”จ๏ผŒ่ทณ่ฟ‡ๅˆ†ๅ‰ฒๆต‹่ฏ•")
gpu_seg_peak = 0
except Exception as e:
print(f" โš ๏ธ ๅˆ†ๅ‰ฒๆต‹่ฏ•ๅคฑ่ดฅ: {e}")
gpu_seg_peak = 0
# ========================================
# ๆœ€็ปˆๆŠฅๅ‘Š
# ========================================
print("\n" + "=" * 70)
print("๐Ÿ“‹ ๅŽ‹ๅŠ›ๆต‹่ฏ•ๆ€ป็ป“ๆŠฅๅ‘Š")
print("=" * 70)
print(f"""
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ NeuroScan AI ่ต„ๆบ้œ€ๆฑ‚ๆŠฅๅ‘Š โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ ๆต‹่ฏ•ๅœบๆ™ฏ โ”‚ CPU ๅณฐๅ€ผ โ”‚ ๅ†…ๅญ˜ๅณฐๅ€ผ โ”‚ GPU ๆ˜พๅญ˜ๅณฐๅ€ผ โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ ๅ•ไปปๅŠก้…ๅ‡† โ”‚ {single_cpu_peak:>6.1f}% โ”‚ {single_mem_peak:>6.1f} GB โ”‚ ~0 GB (CPU) โ”‚
โ”‚ 2ไปปๅŠกๅนถๅ‘ โ”‚ {concurrent_2_cpu:>6.1f}% โ”‚ {concurrent_2_mem:>6.1f} GB โ”‚ ~0 GB (CPU) โ”‚
โ”‚ 3ไปปๅŠกๅนถๅ‘ โ”‚ {concurrent_3_cpu if 'concurrent_3_cpu' in dir() else 0:>6.1f}% โ”‚ {concurrent_3_mem if 'concurrent_3_mem' in dir() else 0:>6.1f} GB โ”‚ ~0 GB (CPU) โ”‚
โ”‚ GPUๅˆ†ๅ‰ฒไปปๅŠก โ”‚ ~50% โ”‚ ~8 GB โ”‚ {gpu_seg_peak:>6.1f} GB โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ ๆŽจ่็กฌไปถ้…็ฝฎ โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ ๆœ€ไฝŽ้…็ฝฎ (ๅ•ไปปๅŠก): 4ๆ ธ CPU, 8GB ๅ†…ๅญ˜, ๆ— ้œ€GPU โ”‚
โ”‚ ๆ ‡ๅ‡†้…็ฝฎ (2ๅนถๅ‘): 8ๆ ธ CPU, 16GB ๅ†…ๅญ˜, 12GB GPU (ๅฏ้€‰) โ”‚
โ”‚ ๆŽจ่้…็ฝฎ (3ๅนถๅ‘): 16ๆ ธ CPU, 32GB ๅ†…ๅญ˜, 24GB GPU โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
""")
print("โœ… ๅŽ‹ๅŠ›ๆต‹่ฏ•ๅฎŒๆˆ!")
if __name__ == "__main__":
main()