examples
Differences
This shows you the differences between two versions of the page.
| Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
| examples [2025/12/29 20:57] – [MPI] dimitar | examples [2026/06/13 15:24] (current) – nshegunov | ||
|---|---|---|---|
| Line 1: | Line 1: | ||
| - | ====MPI4PI==== | + | ====== Guide to SLURM Resource Management and Job Submission ====== |
| - | TOD | + | |
| - | ---- | + | This guide provides an advanced overview of the **SLURM |
| - | ==== PyTorch==== | + | |
| - | Consider | + | |
| - | + | ||
| - | <code python> | + | |
| - | import torch | + | |
| - | def test_pytorch(): | + | ===== 1. Theoretical Overview & Cluster Architecture ===== |
| - | print(" | + | |
| - | print(" | + | |
| - | + | ||
| - | if torch.cuda.is_available(): | + | |
| - | print(" | + | |
| - | device | + | |
| - | else: | + | |
| - | device | + | |
| - | + | ||
| - | # Simple tensor operation | + | |
| - | x = torch.tensor([1.0, 2.0, 3.0], device=device) | + | |
| - | y = torch.tensor([4.0, | + | |
| - | z = x + y | + | |
| - | print(" | + | |
| - | test_pytorch() | + | An HPC cluster is a massive parallel computing instrument built from a collection of individual commodity servers called **Nodes**, interconnected by an ultra-fast, low-latency network fabric. Slurm isolates users from this underlying hardware complexity while ensuring optimal resource distribution. |
| - | </ | + | |
| - | To test it on the unite cluster you can use the folling sbatch scrpit to run it: | + | Slurm operates under a master-worker orchestration framework managed by three primary daemons: |
| - | <code bash> | + | |
| - | # | + | |
| - | # | + | |
| - | #SBATCH --output=pytorch_test.out | + | |
| - | #SBATCH --error=pytorch_test.err | + | |
| - | #SBATCH --time=00:10:00 | + | |
| - | #SBATCH --partition=a40 | + | |
| - | #SBATCH --gres=gpu: | + | |
| - | #SBATCH --mem=4G | + | |
| - | #SBATCH --cpus-per-task=2 | + | |
| - | # Load necessary modules | + | * '' |
| - | module load python/pytorch-2.5.1-llvm-cuda-12.3-python-3.13.1-llvm | + | * '' |
| + | * '' | ||
| - | # Activate your virtual environment if needed | + | ==== Resource Hierarchies ==== |
| - | # source ~/ | + | When submitting a workload to Slurm, it is crucial to understand how resource requests translate into physical hardware allocations: |
| - | # Run the PyTorch script | + | * **Cluster: |
| - | python3.13 pytorch_test.py | + | * **Partition: |
| + | * **Node:** A single distinct physical server chassis possessing localized processors, volatile system memory (RAM), and explicit hardware buses. | ||
| + | * **Core/ | ||
| + | * **Task:** An independent process instance. In distributed execution (like MPI), 1 Task generally maps to 1 unique process loop. | ||
| + | * **CPUs-per-task: | ||
| - | </ | + | ==== Deep Dive: CPU and Memory Architecture (NUMA) |
| - | ---- | + | Modern compute nodes are not monolithic. A single node typically contains two or more CPU **sockets** |
| - | ====Pandas==== | + | |
| - | Consider the following simple python test script( “pandas_test.py”): | + | |
| - | <code python> | + | |
| - | import pandas | + | |
| - | import numpy as np | + | |
| - | # Create | + | * **Local Memory Access:** If a program running on CPU Socket 0 requests |
| - | data = { | + | * **Remote Memory Access:** If a program running on Socket 0 requests data stored in the RAM attached to Socket |
| - | ' | + | |
| - | ' | + | |
| - | ' | + | |
| - | } | + | |
| - | df = pd.DataFrame(data) | + | |
| - | print(" | + | |
| - | print(df) | + | |
| - | # Test basic operations | + | **Why this matters for Slurm:** When you request resources, Slurm attempts to bind your tasks to specific cores and local memory banks (CPU affinity/ |
| - | print("\nSum of each column:" | + | |
| - | print(df.sum()) | + | |
| - | print(" | + | --- |
| - | print(df.mean()) | + | |
| - | # Adding a new column | + | ===== 2. Advanced Cluster Topology and Inter-Node Communication ===== |
| - | df[' | + | |
| - | print(" | + | |
| - | print(df) | + | |
| - | # Filtering rows | + | To write optimal code for an HPC cluster, developers must account for how components talk to one another. Communication speeds and delays |
| - | filtered_df = df[df[' | + | |
| - | print(" | + | |
| - | print(filtered_df) | + | |
| - | # Check if NaN values exist | + | ==== Memory and Communication Layout ==== |
| - | print(" | + | ^ Execution Scope ^ Communication Medium ^ Relative Bandwidth ^ Latency Complexity ^ |
| - | print(df.isna().sum()) | + | | **Intra-Core** |
| - | </code> | + | | **Intra-Node** |
| + | | **Inter-Node** (Across Servers) | InfiniBand Network Fabric | ||
| - | You can use the following snatch script to run it: | + | ==== Network Topologies: Fat-Tree Architecture ==== |
| - | <code bash> | + | HPC networks like the one powering the UNITE cluster avoid standard corporate network setups (which bottleneck under heavy loads) in favor of a specialized **Fat-Tree Topology**. |
| - | # | + | |
| - | #SBATCH --job-name=pytorch_test | + | |
| - | #SBATCH --output=pytorch_test.out | + | |
| - | #SBATCH --error=pytorch_test.err | + | |
| - | #SBATCH --time=00:10:00 | + | |
| - | # | + | |
| - | #SBATCH --gres=gpu:1 | + | |
| - | #SBATCH --mem=4G | + | |
| - | # | + | |
| - | # Load necessary modules | + | * **Non-Blocking Fabric:** In a standard IT tree, links get narrower as you go up toward the core switches, causing traffic jams. A **Fat-Tree topology** does the opposite: the network connections multiply and get thicker |
| - | module load python/3.13.1-llvm | + | * **Full Bisection Bandwidth: |
| - | module load python/ | + | |
| - | # Activate | + | ==== Deep Dive: InfiniBand & OS Kernel Bypass ==== |
| - | # source ~/ | + | When your code spans across multiple physical nodes (e.g., an MPI job), it bypasses standard slow Ethernet connections. Ethernet requires the operating system' |
| - | # Run the PyTorch script | + | The UNITE cluster utilizes |
| - | python3.13 pandas_test.py | + | |
| - | </ | + | |
| - | ---- | + | |
| - | ====Simple C/C++ program==== | + | |
| - | The following is a simple | + | |
| - | <code C> | + | * **Zero-Copy Networking: |
| - | #include <stdio.h> | + | * **Kernel Bypass:** Neither the CPU nor the operating system on Node A or Node B is involved in the transfer. This drops network latency down to single-digit microseconds (<2us), allowing distributed applications to scale linearly. |
| - | #include <stdlib.h> | + | |
| - | #include < | + | |
| - | # | + | |
| - | /* | + | ==== GPU Interconnects |
| - | * Perform element-wise addition of two vectors | + | On GPU partitions |
| - | * | + | |
| - | * Parameters: | + | |
| - | | + | |
| - | | + | |
| - | | + | |
| - | * size: Number of elements in vectors | + | |
| - | */ | + | |
| - | void vector_addition(const double *a, const double *b, double *result, size_t size) { | + | |
| - | for (size_t i = 0; i < size; i++) { | + | |
| - | result[i] = a[i] + b[i]; | + | |
| - | } | + | |
| - | } | + | |
| - | int main() { | + | * **PCIe Bus:** If two GPUs must communicate via the standard motherboard PCIe bus, bandwidth is limited |
| - | const size_t size = 10000000; | + | * **NVLink:** High-end cluster architectures utilize NVIDIA NVLink bridges, allowing GPUs to share memory pools directly at massive speeds (hundreds of GB/s). When writing distributed PyTorch or CuPy scripts across multiple GPUs on the same node, the framework relies on this hardware topology. |
| - | printf(" | + | --- |
| - | printf(" | + | |
| - | printf(" | + | |
| - | printf(" | + | |
| - | printf(" | + | ===== 3. Scheduler Theory: How Slurm Assigns Resources ===== |
| - | double *vector_a | + | |
| - | double *vector_b | + | |
| - | double *result | + | |
| - | if (vector_a == NULL || vector_b == NULL || result == NULL) { | + | Slurm does not simply process jobs sequentially. It uses complex algorithms to maximize cluster utilization and ensure fairness. |
| - | fprintf(stderr, | + | |
| - | return 1; | + | |
| - | } | + | |
| - | printf(" | + | * **Fair-Share Scheduling: |
| - | | + | * **Backfilling Algorithm: |
| - | | + | |
| - | vector_a[i] = (double)rand() / RAND_MAX; | + | |
| - | vector_b[i] = (double)rand() / RAND_MAX; | + | |
| - | } | + | |
| - | printf(" | + | --- |
| - | vector_addition(vector_a, | + | |
| - | printf(" | + | ===== 4. Crucial Cluster Etiquette: The Shared Environment ===== |
| - | for (int i = 0; i < 5; i++) { | + | |
| - | printf(" | + | |
| - | } | + | |
| - | free(vector_a); | + | ^ WARNING: NEVER RUN COMPUTATIONS ON THE LOGIN NODE! ^ |
| - | | + | | When you connect to the UNITE cluster via SSH, you land directly on the **Login Node**. This node's sole purpose is file management, code editing, and checking job queues. Heavy processes like compilation, |
| - | free(result); | + | |
| - | return 0; | + | To execute |
| - | } | + | |
| - | </code> | + | |
| - | The following is the respective batch script for compiling and running the program. You can see the output of the program in the generated // | + | --- |
| - | <code bash> | + | ===== 5. Key Slurm Commands Workflow |
| - | # | + | |
| - | #SBATCH --job-name=vector_sum | + | |
| - | #SBATCH --output=vector_sum_%j.out | + | |
| - | #SBATCH --error=vector_sum_%j.err | + | |
| - | #SBATCH --nodes=1 | + | |
| - | #SBATCH --ntasks=1 | + | |
| - | #SBATCH --cpus-per-task=1 | + | |
| - | #SBATCH --time=00:10:00 | + | |
| - | #SBATCH --partition=unite | + | |
| - | echo " | + | Interact with the scheduler fabric using these primary terminal operations: |
| - | echo "SLURM Job Information" | + | |
| - | echo " | + | |
| - | echo "Job ID: $SLURM_JOB_ID" | + | |
| - | echo "Node: $SLURM_NODELIST" | + | |
| - | echo " | + | |
| - | echo "" | + | |
| - | # Load necessary modules | + | ^ Command ^ Action Description ^ Typical Use Case ^ |
| - | module load gcc | + | | '' |
| + | | '' | ||
| + | | '' | ||
| + | | '' | ||
| + | | '' | ||
| - | # Compile the program | + | --- |
| - | echo " | + | |
| - | gcc -O3 -march=native | + | |
| - | if [ $? -ne 0 ]; then | + | ===== 6. Interactive Development vs. Non-Interactive Batch ===== |
| - | echo " | + | |
| - | exit 1 | + | |
| - | fi | + | |
| - | echo "Compilation | + | ==== Workflow A: Interactive Sessions (Testing & Compilation) ==== |
| - | echo "" | + | To compile libraries, debug scripts line-by-line, |
| - | echo " | + | For a standard multi-purpose compute slice: |
| - | ./ | + | < |
| - | + | $ srun --partition=unite --cpus-per-task=4 --time=00: | |
| - | echo "" | + | |
| - | echo "Job finished at: $(date)" | + | |
| </ | </ | ||
| - | ---- | ||
| - | |||
| - | ====Simple Python program==== | ||
| - | The following is a simple **Python** program which performs element-wise addition of 2 vectors. It does **not** use any dependent libraries: | ||
| - | |||
| - | <code Python> | ||
| - | # | ||
| - | import random | ||
| - | import time | ||
| - | |||
| - | def vector_addition(a, | ||
| - | """ | ||
| - | Perform element-wise addition of two vectors | ||
| - | |||
| - | Parameters: | ||
| - | a: First input vector (list) | ||
| - | b: Second input vector (list) | ||
| - | |||
| - | Returns: | ||
| - | result: Output vector (a + b) | ||
| - | """ | ||
| - | return [a[i] + b[i] for i in range(len(a))] | ||
| - | |||
| - | |||
| - | def main(): | ||
| - | size = 10000000 | ||
| - | |||
| - | print(" | ||
| - | print(" | ||
| - | print(" | ||
| - | print(f" | ||
| - | |||
| - | print(" | ||
| - | random.seed(time.time()) | ||
| - | |||
| - | vector_a = [random.random() for _ in range(size)] | ||
| - | vector_b = [random.random() for _ in range(size)] | ||
| - | |||
| - | print(" | ||
| - | result = vector_addition(vector_a, | ||
| - | |||
| - | print(" | ||
| - | for i in range(5): | ||
| - | print(f" | ||
| - | |||
| - | if __name__ | + | For a dedicated deep learning workspace utilizing NVIDIA graphics hardware: |
| - | main() | + | < |
| + | $ srun --partition=a40 --gres=gpu:1 --cpus-per-task=4 --time=00: | ||
| </ | </ | ||
| + | Once executed, Slurm places your terminal directly onto a secure, isolated compute shell. When your testing is complete, type '' | ||
| - | The following is the respective | + | ==== Workflow B: Batch Processing (Production Runs) ==== |
| + | For heavy workloads that take hours or days, write a batch script | ||
| - | <code bash> | + | Here is a template structure for a production batch file ('' |
| + | <file bash run_job.sh> | ||
| #!/bin/bash | #!/bin/bash | ||
| - | #SBATCH --job-name=vector_sum | + | #SBATCH --job-name=unite_production_job |
| - | #SBATCH --output=vector_sum_%j.out | + | #SBATCH --partition=unite |
| - | #SBATCH --error=vector_sum_%j.err | + | #SBATCH --output=logs_%j.out |
| - | #SBATCH --nodes=1 | + | #SBATCH --error=logs_%j.err |
| - | #SBATCH --ntasks=1 | + | #SBATCH --nodes=1 |
| - | #SBATCH --cpus-per-task=1 | + | #SBATCH --ntasks=1 |
| - | #SBATCH --time=00:10:00 | + | #SBATCH --cpus-per-task=4 # CPU worker core threads assigned to this task |
| - | #SBATCH --partition=unite | + | #SBATCH --mem=16G # Safe RAM allocation limit request |
| + | #SBATCH --time=02: | ||
| - | echo " | + | # 1. Clean the inherited terminal environment states |
| - | echo "SLURM Job Information" | + | module purge |
| - | echo " | + | |
| - | echo "Job ID: $SLURM_JOB_ID" | + | |
| - | echo "Node: $SLURM_NODELIST" | + | |
| - | echo " | + | |
| - | echo "" | + | |
| - | echo "Python version:" | + | # 2. Execute target execution logic or launch binaries |
| - | python3 --version | + | echo "Starting production workload execution on node: $(hostname)" |
| - | echo "" | + | # Run your commands here... |
| + | </ | ||
| - | echo " | + | To place this file into the cluster scheduler pipeline, execute: |
| - | which python3 | + | < |
| - | echo "" | + | $ sbatch run_job.sh |
| - | + | ||
| - | echo " | + | |
| - | echo " | + | |
| - | echo " | + | |
| - | echo "" | + | |
| - | + | ||
| - | python3 vector_sum.py | + | |
| - | + | ||
| - | echo "" | + | |
| - | echo " | + | |
| - | echo "Job finished at: $(date)" | + | |
| - | echo " | + | |
| </ | </ | ||
| - | ---- | + | --- |
| - | ====Python program with dependencies==== | + | ===== 7. Reference Guides and Real-World Examples ===== |
| - | The following is a simple **Python** program which computes the sum of 2 vectors 3 times using **NumPy**. | + | |
| - | <code Python> | + | To see how to apply these Slurm parameters across different compiler systems, runtime environments, |
| - | # | + | |
| - | import numpy as np | + | |
| - | import time | + | |
| - | def vector_addition(size=10000000): | + | * **Native Applications |
| - | | + | |
| + | * [[unite_cpp_mpi|Compiling and Running Distributed C++ MPI Applications]] — How to request multi-node processing | ||
| + | * **GPU-Accelerated Data Science & ML (Python): | ||
| + | * [[unite_python_cupy|Accelerated Mathematical Computations with CuPy]] — Leveraging GPU hardware tensors on the **unite** partition utilizing tracking hooks like '' | ||
| + | * [[unite_python_torch|Deep Learning Model Frameworks with PyTorch]] — High-performance neural processing allocations specifically built to execute on the advanced **a40** hardware partition. | ||
| - | vector_a = np.random.rand(size) | + | --- |
| - | vector_b = np.random.rand(size) | + | |
| - | print(" | + | ===== 8. Troubleshooting Common Queue States ===== |
| - | result | + | |
| - | return result | + | If you run '' |
| - | + | ||
| - | def main(): | + | |
| - | print(" | + | |
| - | print(" | + | |
| - | print(" | + | |
| - | + | ||
| - | sizes = [1000000, 10000000, 50000000] | + | |
| - | + | ||
| - | for size in sizes: | + | |
| - | result = vector_addition(size) | + | |
| - | + | ||
| - | print(f" | + | |
| - | print(f" | + | |
| - | print(" | + | |
| - | + | ||
| - | if __name__ == " | + | |
| - | main() | + | |
| - | </ | + | |
| - | + | ||
| - | The following is the respective batch script for compiling and running the program. You can see the output of the program in the generated // | + | |
| - | + | ||
| - | <code bash> | + | |
| - | # | + | |
| - | #SBATCH --job-name=vector_sum_numpy | + | |
| - | #SBATCH --output=vector_sum_numpy_%j.out | + | |
| - | #SBATCH --error=vector_sum_numpy_%j.err | + | |
| - | #SBATCH --nodes=1 | + | |
| - | #SBATCH --ntasks=1 | + | |
| - | #SBATCH --cpus-per-task=1 | + | |
| - | #SBATCH --time=00: | + | |
| - | #SBATCH --partition=unite | + | |
| - | + | ||
| - | ################################################################################ | + | |
| - | # CONFIGURATION: | + | |
| - | ################################################################################ | + | |
| - | # Options: " | + | |
| - | PYTHON_ENV_METHOD=" | + | |
| - | + | ||
| - | VENV_PATH=" | + | |
| - | CONDA_ENV_NAME=" | + | |
| - | CONDA_MODULE=" | + | |
| - | PYTHON_MODULE=" | + | |
| - | NUMPY_MODULE=" | + | |
| - | + | ||
| - | ################################################################################ | + | |
| - | # Setup Instructions (run once on login node before first job submission) | + | |
| - | ################################################################################ | + | |
| - | # For venv: | + | |
| - | # | + | |
| - | # | + | |
| - | # pip install numpy | + | |
| - | # | + | |
| - | # | + | |
| - | # For conda: | + | |
| - | # | + | |
| - | # conda create -n numpy_env python=3.9 numpy | + | |
| - | # conda deactivate | + | |
| - | # | + | |
| - | # For module: | + | |
| - | # Check available modules: module avail python | + | |
| - | # You need to load both python | + | |
| - | # Then you need to modify PYTHON_MODULE and NUMPY_MODULE variables above accordingly. | + | |
| - | ################################################################################ | + | |
| - | + | ||
| - | echo " | + | |
| - | echo "SLURM Job Information" | + | |
| - | echo " | + | |
| - | echo "Job ID: $SLURM_JOB_ID" | + | |
| - | echo "Node: $SLURM_NODELIST" | + | |
| - | echo " | + | |
| - | echo "" | + | |
| - | + | ||
| - | echo " | + | |
| - | echo "" | + | |
| - | + | ||
| - | if [ " | + | |
| - | echo " | + | |
| - | if [ -f " | + | |
| - | source " | + | |
| - | echo " | + | |
| - | else | + | |
| - | echo " | + | |
| - | echo " | + | |
| - | exit 1 | + | |
| - | fi | + | |
| - | + | ||
| - | elif [ " | + | |
| - | echo " | + | |
| - | module load " | + | |
| - | source activate " | + | |
| - | echo "Conda environment activated: $CONDA_ENV_NAME" | + | |
| - | + | ||
| - | elif [ " | + | |
| - | echo " | + | |
| - | module load " | + | |
| - | module load " | + | |
| - | echo " | + | |
| - | + | ||
| - | else | + | |
| - | echo " | + | |
| - | echo "Valid options: venv, conda, module" | + | |
| - | exit 1 | + | |
| - | fi | + | |
| - | + | ||
| - | # Verify Python and NumPy | + | |
| - | echo "" | + | |
| - | echo " | + | |
| - | python3 --version | + | |
| - | + | ||
| - | echo "" | + | |
| - | echo "NumPy version:" | + | |
| - | python3 -c " | + | |
| - | echo "" | + | |
| - | echo " | + | |
| - | which python3 | + | |
| - | + | ||
| - | echo "" | + | |
| - | echo " | + | |
| - | echo " | + | |
| - | echo " | + | |
| - | echo "" | + | |
| - | + | ||
| - | python3 vector_sum_numpy.py | + | |
| - | + | ||
| - | echo "" | + | |
| - | echo " | + | |
| - | + | ||
| - | if [ " | + | |
| - | deactivate | + | |
| - | echo " | + | |
| - | elif [ " | + | |
| - | conda deactivate | + | |
| - | echo "Conda environment deactivated" | + | |
| - | elif [ " | + | |
| - | # Modules are automatically unloaded when job ends | + | |
| - | echo " | + | |
| - | fi | + | |
| - | + | ||
| - | echo "" | + | |
| - | echo " | + | |
| - | echo "Job finished at: $(date)" | + | |
| - | echo " | + | |
| - | </ | + | |
| - | + | ||
| - | ---- | + | |
| - | + | ||
| - | ====C/C++ program with dependencies==== | + | |
| - | The following is a simple **C/C++** program which compresses and decompresses a string using **zLib**. | + | |
| - | + | ||
| - | <code C++> | + | |
| - | #include < | + | |
| - | #include < | + | |
| - | #include < | + | |
| - | #include < | + | |
| - | + | ||
| - | #define CHUNK 16384 | + | |
| - | + | ||
| - | int main() { | + | |
| - | const char *original = "Hello, this is a test string for zlib compression! " | + | |
| - | "We'll compress this text and then decompress it to verify it works."; | + | |
| - | + | ||
| - | printf(" | + | |
| - | printf(" | + | |
| - | + | ||
| - | // Compression | + | |
| - | uLong source_len = strlen(original) + 1; | + | |
| - | uLong compressed_len = compressBound(source_len); | + | |
| - | unsigned char *compressed = (unsigned char *)malloc(compressed_len); | + | |
| - | + | ||
| - | if (compress(compressed, | + | |
| - | fprintf(stderr, | + | |
| - | free(compressed); | + | |
| - | return 1; | + | |
| - | } | + | |
| - | + | ||
| - | printf(" | + | |
| - | printf(" | + | |
| - | + | ||
| - | // Decompression | + | |
| - | uLong decompressed_len = source_len; | + | |
| - | unsigned char *decompressed = (unsigned char *)malloc(decompressed_len); | + | |
| - | + | ||
| - | if (uncompress(decompressed, | + | |
| - | fprintf(stderr, | + | |
| - | free(compressed); | + | |
| - | free(decompressed); | + | |
| - | return 1; | + | |
| - | } | + | |
| - | + | ||
| - | printf(" | + | |
| - | printf(" | + | |
| - | + | ||
| - | if (strcmp(original, | + | |
| - | printf(" | + | |
| - | } else { | + | |
| - | printf(" | + | |
| - | } | + | |
| - | + | ||
| - | free(compressed); | + | |
| - | free(decompressed); | + | |
| - | + | ||
| - | return 0; | + | |
| - | } | + | |
| - | </ | + | |
| - | + | ||
| - | The following is the respective batch script for compiling and running the program. You can see the output of the program in the generated // | + | |
| - | + | ||
| - | <code bash> | + | |
| - | # | + | |
| - | #SBATCH --job-name=zlib_compress | + | |
| - | #SBATCH --output=zlib_compress_%j.out | + | |
| - | #SBATCH --error=zlib_compress_%j.err | + | |
| - | #SBATCH --time=00:05:00 | + | |
| - | #SBATCH --nodes=1 | + | |
| - | #SBATCH --ntasks=1 | + | |
| - | #SBATCH --cpus-per-task=1 | + | |
| - | #SBATCH --partition=unite | + | |
| - | + | ||
| - | module load gcc/ | + | |
| - | module load zlib/ | + | |
| - | + | ||
| - | echo " | + | |
| - | module list | + | |
| - | + | ||
| - | echo "" | + | |
| - | echo " | + | |
| - | gcc -o zlib_compress zlib_compress.c -lz | + | |
| - | + | ||
| - | if [ $? -eq 0 ]; then | + | |
| - | echo " | + | |
| - | echo "" | + | |
| - | echo " | + | |
| - | echo " | + | |
| - | ./ | + | |
| - | else | + | |
| - | echo " | + | |
| - | exit 1 | + | |
| - | fi | + | |
| - | </ | + | |
| - | + | ||
| - | ---- | + | |
| - | ====MPI==== | + | |
| - | + | ||
| - | The following is an example **C/C++** application which uses **MPI** to perform element-wise addition of two vectors. Each **MPI** task computes the addition of its local region and then sends it back to the leader. Using **MPI** with **Python** is similar assuming that you know how to manage **Python** dependencies on the cluster which is described in the previous section. What is important here is to understand how to manage the resources of the system. | + | |
| - | + | ||
| - | <code C++> | + | |
| - | #include < | + | |
| - | #include < | + | |
| - | #include < | + | |
| - | + | ||
| - | #define VECTOR_SIZE 100000 | + | |
| - | + | ||
| - | int main(int argc, char** argv) { | + | |
| - | int rank, size; | + | |
| - | int i; | + | |
| - | + | ||
| - | MPI_Init(& | + | |
| - | MPI_Comm_rank(MPI_COMM_WORLD, | + | |
| - | MPI_Comm_size(MPI_COMM_WORLD, | + | |
| - | + | ||
| - | int local_size = VECTOR_SIZE / size; | + | |
| - | + | ||
| - | int *local_a = (int*)malloc(local_size * sizeof(int)); | + | |
| - | int *local_b = (int*)malloc(local_size * sizeof(int)); | + | |
| - | int *local_c = (int*)malloc(local_size * sizeof(int)); | + | |
| - | + | ||
| - | int *a = NULL; | + | |
| - | int *b = NULL; | + | |
| - | int *c = NULL; | + | |
| - | + | ||
| - | if (rank == 0) { | + | |
| - | a = (int*)malloc(VECTOR_SIZE * sizeof(int)); | + | |
| - | b = (int*)malloc(VECTOR_SIZE * sizeof(int)); | + | |
| - | c = (int*)malloc(VECTOR_SIZE * sizeof(int)); | + | |
| - | + | ||
| - | for (i = 0; i < VECTOR_SIZE; | + | |
| - | a[i] = i + 1; | + | |
| - | } | + | |
| - | + | ||
| - | for (i = 0; i < VECTOR_SIZE; | + | |
| - | b[i] = (i + 1) * 2; | + | |
| - | } | + | |
| - | } | + | |
| - | + | ||
| - | MPI_Scatter(a, | + | |
| - | MPI_Scatter(b, | + | |
| - | + | ||
| - | printf(" | + | |
| - | for (i = 0; i < local_size; i++) { | + | |
| - | local_c[i] = local_a[i] + local_b[i]; | + | |
| - | } | + | |
| - | + | ||
| - | MPI_Gather(local_c, | + | |
| - | + | ||
| - | if (rank == 0) { | + | |
| - | printf(" | + | |
| - | for (i = 0; i < 5; i++) { | + | |
| - | printf(" | + | |
| - | } | + | |
| - | printf(" | + | |
| - | + | ||
| - | free(a); | + | |
| - | free(b); | + | |
| - | free(c); | + | |
| - | } | + | |
| - | + | ||
| - | free(local_a); | + | |
| - | free(local_b); | + | |
| - | free(local_c); | + | |
| - | + | ||
| - | MPI_Finalize(); | + | |
| - | + | ||
| - | return 0; | + | |
| - | } | + | |
| - | </ | + | |
| - | + | ||
| - | The following is the respective batch script for compiling and running the program. You can see the output of the program in the generated // | + | |
| - | + | ||
| - | <code bash> | + | |
| - | # | + | |
| - | #SBATCH --job-name=vector_sum_mpi | + | |
| - | #SBATCH --output=vector_mpi_%j.out | + | |
| - | #SBATCH --error=vector_mpi_%j.err | + | |
| - | #SBATCH --ntasks=4 | + | |
| - | #SBATCH --time=00: | + | |
| - | #SBATCH --partition=unite | + | |
| - | + | ||
| - | module load mpi/ | + | |
| - | + | ||
| - | echo " | + | |
| - | mpicc -o vector_sum_mpi vector_sum_mpi.c | + | |
| - | + | ||
| - | if [ $? -ne 0 ]; then | + | |
| - | echo " | + | |
| - | exit 1 | + | |
| - | fi | + | |
| - | + | ||
| - | echo " | + | |
| - | echo " | + | |
| - | echo " | + | |
| - | + | ||
| - | mpirun -np $SLURM_NTASKS ./ | + | |
| - | + | ||
| - | echo " | + | |
| - | echo "Job completed!" | + | |
| - | echo " | + | |
| - | </ | + | |
| - | ---- | + | * **(Resources): |
| + | * **(Priority): | ||
| + | * **(PartitionTimeLimit): | ||
| + | * **(InvalidQOS): | ||
examples.1767034664.txt.gz · Last modified: 2025/12/29 20:57 by dimitar
