Welcome to the Sparsr Getting Started guide. Sparsr is a processor/accelerator optimized for sparse matrix operations. This step-by-step tutorial walks you through identifying performance bottlenecks in sparse matrix operations of your application, setting up the Sparsr SDK, writing custom Sparsr assembly kernels, and running accelerated workloads on both the Sparsr Software Emulator and actual Sparsr accelerator hardware.
#include <stdio.h>
// Forward declarations
void func_a(void);
void func_b(void);
void func_heavy(void);
void func_c(void);
void func_d(void);
int main(void) {
func_a();
func_b();
func_heavy(); // WARNING: Performance Bottleneck
func_c();
func_d();
return 0;
}
Download from the Sparsr Developer Zone.
Isolate your bottlenecked sparse operation into a Sparser kernel. This is done by converting your bottlenecked CPU-bound code into a Sparsr Kernel, a new application written in Sparsr Assembly or in C language (with assembly embeddings) that will be executed on Sparsr. Your main application (the Host App) remains mostly untouched, but when the previously-bottlenecked code is reached, the execution jumps to Sparsr, and then returns to your application.
// File: host/myapp.c
#include <stdio.h>
#include <stdbool.h>
#include "sparsr.h"
// Configuration constant to run your original CPU-bound algorithm or your new Sparsr-bound algorithm
#define USE_SPARSR true
// Forward declarations
void func_a(void);
void func_b(void);
void func_heavy(void);
void func_heavy_on_sparsr(void);
void func_c(void);
void func_d(void);
// Stubbed compressed sparse matrix data and kernel definition
const char* my_kernel = "csr_matrix_vector_multiply";
int stub_sparse_matrix_data[] = {1, 0, 4, 0, 0, 9, 3, 0};
int main(void) {
sparsr_init();
func_a();
func_b();
#if USE_SPARSR
func_heavy_on_sparsr();
#else
func_heavy(); // WARNING: Performance Bottleneck
#endif
func_c();
func_d();
sparsr_stop_kernel();
return 0;
}
// Accelerated function utilizing the Sparsr emulator API. This code runs as part of your Host App on CPU, and its main purpose is to transfer data between your Host App and Sparsr and handle the execution of your optimized algorithm on Sparsr.
void func_heavy_on_sparsr(void) {
load_kernel("my_kernel.spas");
sparsr_load_data(stub_sparse_matrix_data);
sparsr_run_kernel();
sparsr_read_data();
}
# File: kernel/myalgo.spas
# Your Kernel, written in Sparsr Assembly Language, running on Sparsr.
LW $t1,1($zero)
LW $t2,2($zero)
ADD $t3,$t1,$t2
AND $t4,$t1,$t2
XOR $t5,$t1,$t2
OR $t6,$t1,$t2
SW $t3,3($zero)
SW $t4,4($zero)
SW $t5,5($zero)
SW $t6,6($zero)
The following makefile will:
spasm to convert your assembly code (.spasm) to Sparsr machine code (.spex) (the binary executed by Sparsr).CC ?= gcc
CFLAGS ?= -O0 -g -Wall -Wextra -std=c99
BUILD_DIR := build
KERNEL_SRC := kernel/myalgo.spasm
KERNEL_BIN := $(BUILD_DIR)/myalgo.spex
HOST_SRC := host/main.c
HOST_BIN := $(BUILD_DIR)/myapp
PATH_BIN := ../../bin
PATH_LIB := ../../lib
PATH_INCLUDE := ../../include
SPASM_BIN := $(PATH_BIN)/spasm
SPARSR_LIB := $(PATH_LIB)/libsparsr_host.so
SPARSR_SOFTEMU_LIB := $(PATH_LIB)/libsparsr_softemu.so
all: $(KERNEL_BIN) $(HOST_BIN)
$(KERNEL_BIN): $(KERNEL_SRC) $(SPASM_BIN)
@mkdir -p $(BUILD_DIR)
cd $(BUILD_DIR) && LD_LIBRARY_PATH=$(PATH_LIB):$$LD_LIBRARY_PATH ../$(SPASM_BIN) ../$(KERNEL_SRC)
$(HOST_BIN): $(HOST_SRC) $(KERNEL_BIN) $(SPARSR_LIB) $(SPARSR_SOFTEMU_LIB)
@mkdir -p $(BUILD_DIR)
$(CC) $(CFLAGS) -I$(PATH_INCLUDE) -o $@ $(HOST_SRC) -L$(PATH_LIB) -Wl,-rpath,'$$ORIGIN/../../../lib' -lsparsr_host -lsparsr_softemu -lstdc++ -pthread
run: all
./$(HOST_BIN)
clean:
rm -rf $(BUILD_DIR)
.PHONY: all run clean
Run make, then execute ./build/myapp. Your application will run fully on CPU (without FPGAs nor custom hardware). Your Sparsr Kernel will run on the Sparsr Software Emulator, at a very low speed. Use this to test your application functionality, not for production performance.
TBD.