From 66b0699fa8653415b0a44e606a4ada51fdb451ad Mon Sep 17 00:00:00 2001 From: cephi Date: Thu, 12 Dec 2024 01:18:26 -0500 Subject: [PATCH] Added partial support for synthetic matrices --- analysis/data_stat.py | 6 ++++- pytorch/batch.py | 27 ++++++++++++++++++----- pytorch/run.py | 16 +++++++++----- pytorch/spmv.py | 51 +++++++++++++++++++++++++++++++++++-------- 4 files changed, 79 insertions(+), 21 deletions(-) diff --git a/analysis/data_stat.py b/analysis/data_stat.py index a543b44..095eace 100644 --- a/analysis/data_stat.py +++ b/analysis/data_stat.py @@ -16,7 +16,7 @@ class Stat(Enum): MATRIX_SHAPE = 'matrix shape' MATRIX_SIZE = 'matrix size' MATRIX_NNZ = 'matrix nnz' - MATRIX_DENSITY = 'matrix density %' + MATRIX_DENSITY = 'matrix density' #POWER_BEFORE = 'power before' #POWER = 'power' @@ -65,6 +65,10 @@ class Stat(Enum): L2D_CACHE_MISS_RATE = 'L2D cache miss rate' LL_CACHE_MISS_RATE = 'LL cache miss rate' +class Format(Enum): + CSR = 'csr' + COO = 'coo' + class Cpu(Enum): #ALTRA = altra_names #XEON = xeon_names diff --git a/pytorch/batch.py b/pytorch/batch.py index b35212d..6d98142 100644 --- a/pytorch/batch.py +++ b/pytorch/batch.py @@ -1,4 +1,4 @@ -from data_stat import Cpu +from data_stat import Cpu, Format import argparse import glob @@ -10,7 +10,10 @@ parser = argparse.ArgumentParser() parser.add_argument('cpu', choices=[x.name.lower() for x in Cpu]) parser.add_argument('output_dir') parser.add_argument('matrix_dir') -parser.add_argument('iterations', type=int) +parser.add_argument('format', type=str, + choices=[fmt.name.lower() for fmt in Format]) +parser.add_argument('base_iterations', type=int) +parser.add_argument('min_time_s', type=int) parser.add_argument('baseline_time_s', type=int) parser.add_argument('baseline_delay_s', type=int) parser.add_argument('--perf', action='store_const', const='--perf') @@ -18,6 +21,7 @@ parser.add_argument('--power', action='store_const', const='--power') parser.add_argument('--distribute', action='store_true') args = parser.parse_args() args.cpu = Cpu[args.cpu.upper()] +args.format = Format[args.format.upper()] srun_args = { Cpu.ALTRA: [ @@ -40,18 +44,29 @@ srun_args = { '--cpus-per-task', '16', '--ntasks-per-node', '1', '--prefer', 'EPYC-7313P' + ], + Cpu.XEON_4216: [ + '--account', 'nexus', + '--partition', 'tron', + '--qos', 'tron-exempt', + '--cpus-per-task', '32', + '--ntasks-per-node', '1', + '--prefer', 'Xeon,4216' ] } python = { Cpu.ALTRA: 'python3', - Cpu.EPYC_7313P: 'python3.11' + Cpu.EPYC_7313P: 'python3.11', + Cpu.XEON_4216: 'python3.11' } def srun(srun_args_list: list, run_args, matrix_file: str) -> list: run_args_list = [ args.cpu.name.lower(), matrix_file, - str(args.iterations), + args.format.name.lower(), + str(args.base_iterations), + str(args.min_time_s), str(args.baseline_time_s), str(args.baseline_delay_s)] if args.perf is not None: @@ -74,10 +89,10 @@ for i, matrix in enumerate(glob.glob(f'{args.matrix_dir.rstrip("/")}/*.mtx')): output_filename = '_'.join([ args.cpu.name.lower(), + str(args.min_time_s), str(args.baseline_time_s), str(args.baseline_delay_s), - os.path.splitext(os.path.basename(matrix))[0], - str(args.iterations)]) + os.path.splitext(os.path.basename(matrix))[0]]) json_filepath = f'{args.output_dir.rstrip("/")}/{output_filename}.json' raw_filepath = f'{args.output_dir.rstrip("/")}/{output_filename}.output' diff --git a/pytorch/run.py b/pytorch/run.py index 82ce627..29ff3eb 100644 --- a/pytorch/run.py +++ b/pytorch/run.py @@ -1,5 +1,5 @@ import data_stat -from data_stat import Stat, Cpu +from data_stat import Stat, Cpu, Format import argparse import os, sys @@ -10,6 +10,8 @@ import time parser = argparse.ArgumentParser() parser.add_argument('cpu', choices=[x.name.lower() for x in Cpu]) parser.add_argument('matrix_file') +parser.add_argument('format', type=str, + choices=[fmt.name.lower() for fmt in Format]) parser.add_argument('base_iterations', type=int) parser.add_argument('min_time_s', type=int) parser.add_argument('baseline_time_s', type=int) @@ -19,6 +21,7 @@ parser.add_argument('--power', action='store_true') parser.add_argument('-d', '--debug', action='store_true') args = parser.parse_args() args.cpu = Cpu[args.cpu.upper()] +args.format = Format[args.format.upper()] python = { Cpu.ALTRA: 'python3', @@ -40,16 +43,16 @@ def program(cpu: Cpu, matrix_file: str, iterations: int) -> list: return [ 'apptainer', 'run', 'pytorch-altra.sif', '-c', 'numactl --cpunodebind=0 --membind=0 ' - + f'python3 spmv.py {matrix_file} {iterations}'] + + f'python3 spmv.py {iterations} csr -m {matrix_file}'] elif cpu == Cpu.EPYC_7313P: return [ 'apptainer', 'run', 'pytorch-epyc_7313p.sif', - 'python3', 'spmv.py', f'{matrix_file}', f'{iterations}'] + 'python3', 'spmv.py', f'{iterations}', 'csr', '-m', f'{matrix_file}'] elif cpu == Cpu.XEON_4216: return [ 'apptainer', 'run', 'pytorch-xeon_4216.sif', 'numactl', '--cpunodebind=0', '--membind=0', - 'python3', 'spmv.py', f'{matrix_file}', f'{iterations}'] + 'python3', 'spmv.py', f'{iterations}', 'csr', '-m', f'{matrix_file}'] def baseline_power(cpu: Cpu, baseline_time_s: int) -> list: power_process = subprocess.Popen(['./power.sh', str(baseline_time_s)], @@ -88,7 +91,7 @@ iterations = args.base_iterations program_result = run_program(program(args.cpu, args.matrix_file, iterations)) while program_result[0][Stat.TIME_S.name] < args.min_time_s: # Increase the number of iterations by difference between the current time taken and the desired time. - iterations *= 1 / ((args.min_time_s - program_result[0][Stat.TIME_S.name]) / args.min_time_s) + iterations *= 1 / (program_result[0][Stat.TIME_S.name] / args.min_time_s) # Add another 5% for safety. iterations += iterations * 0.05 iterations = int(iterations) @@ -157,6 +160,9 @@ if args.power: args.baseline_time_s) ) baseline_wattage = baseline_joules / (args.baseline_time_s * 2) + if args.debug: + print(baseline_joules) + print(baseline_wattage) result[Stat.J_1KI.name] = ( (result[Stat.J.name] / result[Stat.ITERATIONS.name]) * 1000 diff --git a/pytorch/spmv.py b/pytorch/spmv.py index d063b35..6afe600 100644 --- a/pytorch/spmv.py +++ b/pytorch/spmv.py @@ -1,4 +1,4 @@ -from data_stat import Stat +from data_stat import Stat, Format import torch, scipy import numpy as np @@ -8,18 +8,48 @@ import json import sys, os parser = argparse.ArgumentParser() -parser.add_argument('matrix_file', help='the input matrix (.mtx) file') parser.add_argument('iterations', type=int, help='the number of iterations of multiplication to perform') +parser.add_argument('format', type=str, + choices=[fmt.name.lower() for fmt in Format], + help='the sparse format to use') +parser.add_argument('-m', '--matrix_file', help='the input matrix (.mtx) file') +parser.add_argument('-ss', '--synthetic_size', type=int, + help='the synthetic matrix parameters size (rows)') +parser.add_argument('-sd', '--synthetic_density', type=float, + help='the synthetic matrix density (%)') args = parser.parse_args() +args.format = Format[args.format.upper()] device = 'cpu' -matrix = scipy.io.mmread(args.matrix_file) -matrix = torch.sparse_coo_tensor( - np.vstack((matrix.row, matrix.col)), - matrix.data, matrix.shape, - device=device -).to_sparse_csr().type(torch.float) +if args.matrix_file is not None: + matrix = scipy.io.mmread(args.matrix_file) + matrix = torch.sparse_coo_tensor( + np.vstack((matrix.row, matrix.col)), + matrix.data, matrix.shape, + device=device, dtype=torch.float32) +elif args.synthetic_size is not None and args.synthetic_density is not None: + nnz = int((args.synthetic_size ** 2) * (args.synthetic_density / 100)) + row_indices = torch.randint(0, args.synthetic_size, (nnz,)) + col_indices = torch.randint(0, args.synthetic_size, (nnz,)) + indices = torch.stack([row_indices, col_indices]) + values = torch.randn(nnz) + + matrix = torch.sparse_coo_tensor( + indices, values, + size=(args.synthetic_size, args.synthetic_size), + device=device, dtype=torch.float32) +else: + print("No matrix specified!") + exit(1) + +if args.format == Format.CSR: + matrix = matrix.to_sparse_csr().type(torch.float32) +elif args.format == Format.COO: + pass +else: + print("Unrecognized format!") + exit(1) vector = torch.rand(matrix.shape[1], device=device) @@ -35,7 +65,10 @@ end = time.time() result = dict() -result[Stat.MATRIX_FILE.name] = os.path.splitext(os.path.basename(args.matrix_file))[0] +if args.matrix_file is not None: + result[Stat.MATRIX_FILE.name] = os.path.splitext(os.path.basename(args.matrix_file))[0] +else: + result[Stat.MATRIX_FILE.name] = "synthetic" print(f"Matrix: {result[Stat.MATRIX_FILE.name]}", file=sys.stderr) result[Stat.MATRIX_SHAPE.name] = matrix.shape