WIP backup: snapshot all local notes, slides, tutorials, and kernel work

Backup commit grouping all in-progress local work so nothing is lost:

- Modified CK-UA kernel + example sources (unified_attention.cpp,
  unified_attention_kernel.hpp) and CMake/build files.
- Updated dispatcher README and ctypes_utils.py.
- New unified_attention example notes: PARAMETERS.md, VARIABLES.md.
- New unified_attention instances for d128 fp16/bf16 (mask/nmask, gqa6).
- New 99_toy_tutorial/ collection: bank-conflict investigations
  (test_*.cpp, *.js, *.gdb, *.asm, *.md), tile distribution / row
  reduction / calling_gemm / thread_buffer tutorials.
- Slide decks and supporting assets (bank_conflict_slides.qmd/.html,
  tile_distribution_slides.qmd, assets/, *_files/, step1_reshape_only,
  xor_full_steps_simple).
- GDB helper script (break_on_ds_read.gdb).

Not intended for upstream review; pure WIP snapshot.
This commit is contained in:
root
2026-05-11 20:34:52 +00:00
parent 3f076a6fc1
commit 393ebc1a50
664 changed files with 257117 additions and 69 deletions

View File

@@ -0,0 +1,233 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: MIT
# Copyright (c) 2024, Advanced Micro Devices, Inc. All rights reserved.
"""
Analyze bank conflict profiling results from rocprofv3
This script processes the SQLite databases produced by rocprofv3 profiling
and provides a comprehensive comparison between plain and XOR transpose
implementations.
Usage:
python3 analyze_bank_conflicts.py <plain_profile_dir> <xor_profile_dir>
Example:
python3 analyze_bank_conflicts.py /tmp/plain /tmp/xor
"""
import sqlite3
import sys
from pathlib import Path
from typing import Dict, Tuple, Optional
def find_results_db(profile_dir: Path) -> Optional[Path]:
"""
Find the results.db file in the profiling output directory.
rocprofv3 creates a timestamped subdirectory containing results.db
"""
db_files = list(profile_dir.glob("*/results.db"))
if not db_files:
return None
return db_files[0]
def query_pmc_results(db_path: Path) -> Tuple[int, int]:
"""
Query SQ_LDS_BANK_CONFLICT and SQ_INSTS_LDS from results.db
Returns:
Tuple of (conflicts, lds_instructions)
"""
conn = sqlite3.connect(db_path)
cursor = conn.cursor()
query = """
SELECT counter_name, SUM(counter_value) as total
FROM pmc_events
WHERE counter_name IN ('SQ_LDS_BANK_CONFLICT', 'SQ_INSTS_LDS')
GROUP BY counter_name
"""
results = cursor.execute(query).fetchall()
conn.close()
data = dict(results)
conflicts = data.get('SQ_LDS_BANK_CONFLICT', 0)
lds_insts = data.get('SQ_INSTS_LDS', 0)
return int(conflicts), int(lds_insts)
def print_separator(char='', width=78):
"""Print a horizontal separator line"""
print(char * width)
def print_header(title: str):
"""Print a formatted header box"""
width = 78
print()
print("" + "" * (width - 2) + "")
print(f"{title.center(width - 4)}")
print("" + "" * (width - 2) + "")
print()
def print_analysis(plain_dir: Path, xor_dir: Path):
"""Print comprehensive analysis of both versions"""
# Find results.db files
plain_db = find_results_db(plain_dir)
xor_db = find_results_db(xor_dir)
if plain_db is None:
print(f"Error: Could not find results.db in {plain_dir}", file=sys.stderr)
sys.exit(1)
if xor_db is None:
print(f"Error: Could not find results.db in {xor_dir}", file=sys.stderr)
sys.exit(1)
# Query both databases
plain_conflicts, plain_insts = query_pmc_results(plain_db)
xor_conflicts, xor_insts = query_pmc_results(xor_db)
# Handle edge cases
if plain_insts == 0 or xor_insts == 0:
print("Error: No LDS instructions found in profiling data!", file=sys.stderr)
print("Make sure the kernels executed successfully.", file=sys.stderr)
sys.exit(1)
# Calculate rates
plain_rate = (plain_conflicts / plain_insts * 100)
xor_rate = (xor_conflicts / xor_insts * 100)
plain_per_inst = plain_conflicts / plain_insts
xor_per_inst = xor_conflicts / xor_insts
# Calculate improvements
conflict_reduction = plain_conflicts - xor_conflicts
reduction_pct = (conflict_reduction / plain_conflicts * 100) if plain_conflicts > 0 else 0
rate_improvement = plain_rate - xor_rate
speedup = plain_per_inst / xor_per_inst if xor_per_inst > 0 else 0
# Theoretical minimum (2-way conflicts for 64 threads / 32 banks)
theoretical_min_conflicts_per_inst = 2.0
theoretical_min_rate = 100.0 # 100% = 1 conflict per instruction = 2-way
gap_to_optimal = xor_per_inst / theoretical_min_conflicts_per_inst
# Print results
print_header("Bank Conflict Analysis Results")
# Main comparison table
print(f"{'Metric':<35} {'Plain LDS':>20} {'XOR LDS':>20}")
print_separator()
print(f"{'SQ_LDS_BANK_CONFLICT':<35} {plain_conflicts:>20,} {xor_conflicts:>20,}")
print(f"{'SQ_INSTS_LDS':<35} {plain_insts:>20,} {xor_insts:>20,}")
print(f"{'Conflict Rate (%)':<35} {plain_rate:>20.1f} {xor_rate:>20.1f}")
print(f"{'Conflicts per Instruction':<35} {plain_per_inst:>20.2f} {xor_per_inst:>20.2f}")
print_separator()
# Improvement metrics
print()
print(f"{'Improvement Metrics':<35} {'Value':>20}")
print_separator()
print(f"{'Conflict Reduction (absolute)':<35} {conflict_reduction:>20,}")
print(f"{'Conflict Reduction (%)':<35} {reduction_pct:>20.1f}%")
print(f"{'Rate Improvement (percentage points)':<35} {rate_improvement:>20.1f}")
print(f"{'Speedup (conflicts/inst)':<35} {speedup:>20.2f}x")
print_separator()
# Theoretical analysis
print()
print(f"{'Theoretical Analysis':<35} {'Value':>20}")
print_separator()
print(f"{'Theoretical minimum (64t/32b)':<35} {theoretical_min_conflicts_per_inst:>20.1f}")
print(f"{'Theoretical min rate (%)':<35} {theoretical_min_rate:>20.1f}%")
print(f"{'Current XOR (conflicts/inst)':<35} {xor_per_inst:>20.2f}")
print(f"{'Gap to theoretical optimal':<35} {gap_to_optimal:>20.2f}x")
print_separator()
# Interpretation
print_header("Interpretation")
print(f"✓ XOR swizzling reduces bank conflicts by {reduction_pct:.1f}%")
print()
print(f" Plain LDS: ~{plain_per_inst:.1f}-way conflicts per LDS instruction")
print(f" ({plain_conflicts:,} total conflicts / {plain_insts:,} instructions)")
print()
print(f" XOR LDS: ~{xor_per_inst:.1f}-way conflicts per LDS instruction")
print(f" ({xor_conflicts:,} total conflicts / {xor_insts:,} instructions)")
print()
print(f"✓ Serialization improvement: {speedup:.1f}× fewer conflicts per instruction")
print()
print(f"✓ Theoretical minimum: ~{theoretical_min_conflicts_per_inst:.0f}-way conflicts")
print(f" (Pigeonhole principle: 64 threads / 32 banks = 2 threads per bank minimum)")
print()
print(f"✓ Gap to theoretical optimal: {gap_to_optimal:.1f}× away from best possible")
print()
# Performance impact estimation
if plain_per_inst > 0:
# Estimate transpose speedup (rough approximation)
transpose_speedup = plain_per_inst / xor_per_inst if xor_per_inst > 0 else 1.0
print(f"Estimated Performance Impact:")
print(f" If transpose is 10% of kernel time:")
print(f" Overall speedup: ~{(1.0 / (0.9 + 0.1/transpose_speedup) - 1.0) * 100:.1f}% faster")
print(f" (Amdahl's law: 90% unchanged + 10% × {transpose_speedup:.1f}× faster)")
print()
# Recommendations
print_header("Recommendations")
if xor_per_inst > theoretical_min_conflicts_per_inst * 2:
print("⚠ Current XOR implementation is {:.1f}× from theoretical optimal.".format(gap_to_optimal))
print()
print("Potential further optimizations:")
print(" 1. Try 32×32 tiles instead of 64×32 (may achieve near-zero conflicts)")
print(" 2. Profile with omniperf for detailed bank conflict analysis")
print(" 3. Consider double buffering if LDS usage is not a constraint")
print()
else:
print("✓ XOR implementation is close to theoretical optimal!")
print()
print(f" Gap: {gap_to_optimal:.1f}× from theoretical minimum")
print(" Further optimization may not be worth the complexity.")
print()
print("For detailed tutorial on bank conflicts and XOR swizzling, see:")
print(" example/ck_tile/99_toy_tutorial/BANK_CONFLICT_TUTORIAL.md")
print()
def main():
"""Main entry point"""
if len(sys.argv) != 3:
print(f"Usage: {sys.argv[0]} <plain_profile_dir> <xor_profile_dir>", file=sys.stderr)
print(file=sys.stderr)
print("Example:", file=sys.stderr)
print(f" {sys.argv[0]} /tmp/plain /tmp/xor", file=sys.stderr)
sys.exit(1)
plain_dir = Path(sys.argv[1])
xor_dir = Path(sys.argv[2])
# Validate directories exist
if not plain_dir.exists():
print(f"Error: Plain profile directory not found: {plain_dir}", file=sys.stderr)
sys.exit(1)
if not xor_dir.exists():
print(f"Error: XOR profile directory not found: {xor_dir}", file=sys.stderr)
sys.exit(1)
# Run analysis
print_analysis(plain_dir, xor_dir)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,151 @@
#!/bin/bash
# SPDX-License-Identifier: MIT
# Copyright (c) 2024, Advanced Micro Devices, Inc. All rights reserved.
# Comprehensive bank conflict profiling script for CK Tile Tutorial 11
# Profiles both plain and XOR transpose implementations and compares results
set -e
# Configuration
BUILD_DIR="${1:-relbuild}"
OUTPUT_DIR="${2:-/tmp/bank_conflict_analysis}"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
echo "╔════════════════════════════════════════════════╗"
echo "║ Bank Conflict Analysis Suite ║"
echo "║ CK Tile Tutorial 11 ║"
echo "╚════════════════════════════════════════════════╝"
echo ""
echo "Build directory: $BUILD_DIR"
echo "Output directory: $OUTPUT_DIR"
echo ""
# Check if build directory exists
if [ ! -d "$BUILD_DIR" ]; then
echo "Error: Build directory '$BUILD_DIR' not found!"
echo "Usage: $0 [build_dir] [output_dir]"
echo "Example: $0 relbuild /tmp/my_analysis"
exit 1
fi
# Create output directory
mkdir -p "$OUTPUT_DIR"
# Build both tutorials
echo "[1/5] Building tutorials..."
echo "----------------------------------------"
cd "$BUILD_DIR"
echo "Building plain transpose..."
if ! cmake --build . --target aa_tutorial_11_plain_transpose -j$(nproc); then
echo "Error: Failed to build aa_tutorial_11_plain_transpose"
exit 1
fi
echo "Building production transpose (XOR)..."
if ! cmake --build . --target aa_tutorial_11_production_transpose -j$(nproc); then
echo "Error: Failed to build aa_tutorial_11_production_transpose"
exit 1
fi
echo "✓ Build complete"
echo ""
# Check if binaries exist
if [ ! -f "./bin/aa_tutorial_11_plain_transpose" ]; then
echo "Error: aa_tutorial_11_plain_transpose binary not found!"
exit 1
fi
if [ ! -f "./bin/aa_tutorial_11_production_transpose" ]; then
echo "Error: aa_tutorial_11_production_transpose binary not found!"
exit 1
fi
# Profile plain transpose
echo "[2/5] Profiling plain transpose (no XOR)..."
echo "----------------------------------------"
if ! rocprofv3 --pmc SQ_LDS_BANK_CONFLICT,SQ_INSTS_LDS \
-d "$OUTPUT_DIR/plain" \
-- ./bin/aa_tutorial_11_plain_transpose; then
echo "Error: Profiling plain transpose failed!"
echo "Make sure rocprofv3 is installed and you have GPU access."
exit 1
fi
echo "✓ Plain transpose profiled"
echo ""
# Profile XOR transpose
echo "[3/5] Profiling XOR transpose..."
echo "----------------------------------------"
if ! rocprofv3 --pmc SQ_LDS_BANK_CONFLICT,SQ_INSTS_LDS \
-d "$OUTPUT_DIR/xor" \
-- ./bin/aa_tutorial_11_production_transpose; then
echo "Error: Profiling XOR transpose failed!"
exit 1
fi
echo "✓ XOR transpose profiled"
echo ""
# Find the results.db files
echo "[4/5] Locating profile results..."
echo "----------------------------------------"
PLAIN_DB=$(find "$OUTPUT_DIR/plain" -name "results.db" -type f | head -1)
XOR_DB=$(find "$OUTPUT_DIR/xor" -name "results.db" -type f | head -1)
if [ -z "$PLAIN_DB" ]; then
echo "Error: Could not find results.db in $OUTPUT_DIR/plain"
exit 1
fi
if [ -z "$XOR_DB" ]; then
echo "Error: Could not find results.db in $OUTPUT_DIR/xor"
exit 1
fi
echo "Plain results: $PLAIN_DB"
echo "XOR results: $XOR_DB"
echo ""
# Analyze results
echo "[5/5] Analyzing results..."
echo "----------------------------------------"
if [ -f "$SCRIPT_DIR/analyze_bank_conflicts.py" ]; then
python3 "$SCRIPT_DIR/analyze_bank_conflicts.py" \
"$OUTPUT_DIR/plain" \
"$OUTPUT_DIR/xor"
else
# Fallback: manual SQLite query if Python script not available
echo "Python analysis script not found. Using direct SQLite queries:"
echo ""
echo "Plain Transpose Results:"
sqlite3 "$PLAIN_DB" "
SELECT
SUM(CASE WHEN counter_name = 'SQ_LDS_BANK_CONFLICT' THEN counter_value ELSE 0 END) as conflicts,
SUM(CASE WHEN counter_name = 'SQ_INSTS_LDS' THEN counter_value ELSE 0 END) as lds_insts,
ROUND(100.0 * conflicts / lds_insts, 2) as conflict_rate_percent
FROM pmc_events;"
echo ""
echo "XOR Transpose Results:"
sqlite3 "$XOR_DB" "
SELECT
SUM(CASE WHEN counter_name = 'SQ_LDS_BANK_CONFLICT' THEN counter_value ELSE 0 END) as conflicts,
SUM(CASE WHEN counter_name = 'SQ_INSTS_LDS' THEN counter_value ELSE 0 END) as lds_insts,
ROUND(100.0 * conflicts / lds_insts, 2) as conflict_rate_percent
FROM pmc_events;"
fi
echo ""
echo "╔════════════════════════════════════════════════╗"
echo "║ Analysis Complete! ║"
echo "╚════════════════════════════════════════════════╝"
echo ""
echo "Results saved to: $OUTPUT_DIR"
echo ""
echo "To view detailed results:"
echo " sqlite3 $PLAIN_DB"
echo " sqlite3 $XOR_DB"
echo ""