Getting Started

Introduction

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.

Step #1 - Identify your app's sparse matrix-related main bottleneck

flowchart LR subgraph Application["your_host_app.c"] direction TB A["func_a()"] B["func_b()"] C["func_heavy()"] D["func_c()"] E["func_d()"] end subgraph CPU["CPU"] Application end subgraph EC2["Computer"] direction LR CPU end A ~~~ B B ~~~ C C ~~~ D D ~~~ E style C stroke:#cc0000,stroke-width:4px
#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;
}

Step #2 - Install the Sparsr SDK

Download from the Sparsr Developer Zone.

Step #3 - Write your Sparsr Kernel

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.

flowchart LR subgraph Application["your_host_app.c"] direction TB A["func_a()"] B["func_b()"] C["func_heavy()"] D["func_c()"] E["func_d()"] end subgraph CPU["CPU"] Application end subgraph MidCol[" "] direction TB RAM["RAM"] PCIe["PCIe Controller"] end subgraph Sparsr["Sparsr"] Backend["your_kernel.spex"] end subgraph FPGA_Memory_Kernel["Sparsr System"] direction TB CMEM["CMEM"] Sparsr end subgraph FPGA["FPGA"] direction TB FPGA_Memory_Kernel end subgraph EC2["AWS EC2 F2"] direction LR CPU MidCol FPGA end CMEM ~~~ Sparsr A ~~~ B B ~~~ C C ~~~ D D ~~~ E RAM ~~~ PCIe CPU ~~~ MidCol MidCol ~~~ FPGA RAM <--> PCIe style C stroke:#cc0000,stroke-width:4px style MidCol fill:none,stroke:none
// 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)

Step #4 - Test the functionality on the Sparsr Software Emulator

The following makefile will:

  1. Cross-assemble your kernel by using spasm to convert your assembly code (.spasm) to Sparsr machine code (.spex) (the binary executed by Sparsr).
  2. Compile your C application for CPU.
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.

Step #5 - Run your entire application using Sparsr

TBD.