rsb-examples(3)
β¨ NAME β¨
okay so this is librsb β aka the rsb-examples situation β aka example programs and code, and bestie it's a whole vibe π
π DESCRIPTION π
Examples of usage of rsb in C (<rsb.h> and <blas_sparse.h>), C++ (optional <rsb.hpp>), Fortran (<rsb.F90> and <rsb_blas_sparse.F90>). like, that's the whole tea π«β¨
π SYNOPSIS π
π Files π
all the little guys, ok, here we go:
assemble.cppβ C++ example based on<rsb.hpp>, assemblingRsbMatrixby pieces π§©autotune.cppβ C++ example based on<rsb.hpp>for performance-benchmarking a matrix from file usingRsbMatrix.tune_spmm(), for various right-hand side counts π₯bench.cppβ C++ example based on<rsb.hpp>for performance-benchmarking a matrix from file usingRsbMatrix.spmm(), for various right-hand side counts β±οΈbuild.cppβ C++ example based on<rsb.hpp>using timings for common matrix operations onRsbMatrix:RsbMatrix.get_coo(),rsb_coo_sort(),rsb_time()π οΈexample.cppβ C++ example based on<rsb.hpp>usingRsbMatrix.spmm()β¨misc.cppβ C++ example based on<rsb.hpp>showing variousRsbMatrixoperations πmtx2bin.cppβ C++ example based on<rsb.hpp>converting a Matrix Market file into a custom format usingRsbMatrix.get_coo()πrender.cppβ C++ example based on<rsb.hpp>of invoking the autotuner on anRsbMatrixmatrix withRsbMatrix.tune_spmm()and rendering it withRsbMatrix.rndr()π¨span.cppβ C++ example based on<rsb.hpp>illustrating use ofRsbMatrix.file_save(), andstd::span-based versions ofRsbMatrix.tune_spmm(),RsbMatrix.spmv()πtwonnz.cppβ C++ example based on<rsb.hpp>measuringRsbMatrix.spmm()performance of a matrix with only two elements; this is is effectively measuring performance of result vector scaling π―autotune.cβ CRSBautotune"exampleprogrambasedon<rsb.h>.usesrsb_file_mtx_load(),rsb_spmm(),rsb_tune_spmm().πbacksolve.cβ C triangular solve example program. Usesrsb_spsv(),rsb_tune_spsm(). Based on<rsb.h>. πΊcplusplus.cppβ C++ program using the C<rsb.h>interface. Usesrsb_tune_spmm(),rsb_spmv(). π¦hello-spblas.cβ A first ChelloRSB"exampleprogramusingaSparseBLASinterfaceand<rsb.h>.UsesBLAS_duscr_begin(),BLAS_ussp(),BLAS_usgp(),BLAS_duscr_insert_entries(),BLAS_duscr_end(),BLAS_dusget_element(),BLAS_dusmv(),BLAS_usds().πhello.cβ A firsthelloRSB"exampleCprogrambasedon<rsb.h>.Usesrsb_lib_set_opt(),rsb_mtx_get_info_str().πΈio-spblas.cβ Example C program using the Sparse BLAS interface and reading from file usingrsb_blas_file_mtx_load(),BLAS_usgp(),BLAS_dusmv(),BLAS_usds(). π₯power.cβ A toy<rsb.h>-based C program implementing the power method for computing matrix eigenvalues. Usesrsb_spmv(). β‘snippets.cβ Collection of C snippets of other examples. Used piecewise the documentation. Not intended to be read as example. πtranspose.cβ A toy<rsb.h>-based C program showing instantiation, transposition and other operations on a single matrix. Usesrsb_mtx_clone(),rsb_file_mtx_save(),rsb_file_mtx_get_dims(),rsb_file_mtx_load(). π
Detailed Description β¨
Examples of usage of rsb in C (<rsb.h> and <blas_sparse.h>), C++ (optional <rsb.hpp>), Fortran (<rsb.F90> and <rsb_blas_sparse.F90>). yes that's the same sentence twice, the manpage said it twice, so I said it twice, no cap π―
The following fully working example programs illustrate correct ways of using the library.
- First example in C, using
<rsb.h>:examples_hello_c. - First example in C, using
<blas_sparse.h>:examples_hello_spblas_c. - Autotuning example in C, using
<rsb.h>:examples_autotune_c. - I/O example in C, using
<blas_sparse.h>:examples_io_spblas_c. - Example transposing a matrix in C, using
<rsb.h>in C:examples_transpose_c. - Example showing the power method in C, using
<rsb.h>in C:examples_power_c. - Example in Fortran, using
<rsb_blas_sparse.F90>:examples_fortran_F90. - Example in Fortran, using
<rsb.F90>:examples_fortran_rsb_fi_F90. - Example in C, using
<rsb.h>:examples_backsolve_c. - Misc example snippets in C, using
<rsb.h>:examples_snippets_c. - Benchmark invocation from shell script:
examples_bench_sh.
Once installed rsb, the script displayed here (examples/make.sh) should be sufficient to build these examples π
#!/bin/bash
# Example script to build the librsb example programs.
# Uses librsb-config in the $PATH for build flags.
# Environment-provided $LIBRSB_CONFIG override that.
set -e
set -x
srcdir=${srcdir:-`pwd`}
builddir=${builddir:-`pwd`}
prefix="/usr"
exec_prefix="${prefix}"
bindir="${exec_prefix}/bin"
if test -z "${LIBRSB_CONFIG}"; then
export PATH="${bindir}:$PATH"
fi
LIBRSB_CONFIG=${LIBRSB_CONFIG:-librsb-config}
PKG_CONFIG=pkg-config
WANT_PKGCONFIG="yes"
WANT_CLEANUP=${WANT_CLEANUP:-false}
if test x"yes" == x"yes" ; then
CXX="`${LIBRSB_CONFIG} --cxx`"
if test x"rsblib" != x"" -a x"${CXX}" != x"" ; then
for s in ${srcdir}/*.cpp
do
p=${builddir}/`basename ${s/.cpp/}`
rm -f $p
CXXFLAGS=`${LIBRSB_CONFIG} --cxxflags --I_opts`
LDFLAGS=`${LIBRSB_CONFIG} --ldflags --extra_libs`
LINK=`${LIBRSB_CONFIG} --link`
o="${p}.o"
ccmd="$CXX $CXXFLAGS -c $s -o $o"
lcmd="$LINK $o $LDFLAGS -o $p"
echo "$ccmd && $lcmd"
( $ccmd && $lcmd )
${WANT_CLEANUP} && rm -f "$p"
# one may use a single command, but that's error-prone (may miss libraries):
#cmd="$CXX $CXXFLAGS $s $LDFLAGS -o $p"
#echo $cmd
#$cmd
done
fi
fi
if test x"yes" == x"yes" ; then
for s in ${srcdir}/*.c
do
p=`basename ${s/.c/}`
if test $p == hello-spblas -a x"yes" != x"yes" ; then continue; fi
if test $p == io-spblas -a x"yes" != x"yes" ; then continue; fi
rm -f $p
CFLAGS=`${LIBRSB_CONFIG} --I_opts --cppflags`
LDFLAGS=`${LIBRSB_CONFIG} --ldflags --extra_libs`
CC=`${LIBRSB_CONFIG} --cc`
LINK=`${LIBRSB_CONFIG} --link`
o="${p}.o"
ccmd="$CC $CFLAGS -c $s -o $o"
lcmd="$LINK $o $LDFLAGS -o $p"
echo "$ccmd && $lcmd"
( $ccmd && $lcmd )
${WANT_CLEANUP} && rm -f "$p"
# one may use a single command, but that's error-prone (may miss libraries):
#cmd="$CC $CFLAGS $s $LDFLAGS -o $p"
#echo $cmd
#$cmd
if test x"${WANT_PKGCONFIG}" != x"no" ; then
CFLAGS=`${PKG_CONFIG} --cflags librsb`
LIBS=`${PKG_CONFIG} --libs --static librsb`
ccmd="$CC $CFLAGS -c $s -o $o"
lcmd="$LINK $o $LIBS -o $p"
${WANT_CLEANUP} && rm -f "$p"
echo "$ccmd && $lcmd"
( $ccmd && $lcmd )
fi
done
fi
if test x"yes" == x"yes" ; then
if test x"yes" = x"yes" ; then
FP=${srcdir}/fortran_rsb_fi.F90
if test x"yes" = x"yes" ; then
FP+=\ ${srcdir}/fortran.F90
fi
# activated if you have built the Fortran modules and installed them in the right path.
for s in $FP
do
p=`basename ${s/.F90/}`
rm -f $p
FCFLAGS=`${LIBRSB_CONFIG} --I_opts --fcflags`
LDFLAGS=`${LIBRSB_CONFIG} --ldflags --extra_libs`
FC=`${LIBRSB_CONFIG} --fc`
LINK=`${LIBRSB_CONFIG} --link`
o="${p}.o"
FCLIBS=`${LIBRSB_CONFIG} --fclibs`
ccmd="$FC $FCFLAGS -c $s -o $o"
lcmd="$LINK $o $LDFLAGS $FCLIBS -o $p"
echo "$ccmd && $lcmd"
( $ccmd && $lcmd )
${WANT_CLEANUP} && rm -f "$p"
done
fi
fi
echo " [*] done building examples!"
now meet the icon itself: examples/hello.c: π₯Ήπ
/*
Copyright (C) 2008-2021 Michele Martone
This file is part of librsb.
librsb is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
librsb is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
License for more details.
You should have received a copy of the GNU Lesser General Public
License along with librsb; see the file COPYING.
If not, see <https://www.gnu.org/licenses/>.
*/
/*!
\ingroup rsb-examples
@file
@author Michele Martone
@brief A first "hello RSB" example C program based on <rsb.h>.
Uses #rsb_lib_set_opt(), #rsb_mtx_get_info_str().
\include hello.c
*/
#include <rsb.h> /* librsb header to include */
#include <stdio.h> /* printf() */
int main(const int argc, char * const argv[])
{
/*!
A Hello-RSB program.
This program shows how to use the rsb.h interface correctly to:
- initialize the library using #rsb_lib_init()
- set library options using #rsb_lib_set_opt()
- revert such changes
- allocate (build) a single sparse matrix in the RSB format
using #rsb_mtx_alloc_from_coo_const()
- prints information obtained via #rsb_mtx_get_info_str()
- multiply the matrix times a vector using #rsb_spmv()
- deallocate the matrix using #rsb_mtx_free()
- finalize the library using #rsb_lib_exit()
In this example, we use #RSB_DEFAULT_TYPE as matrix type.
This type depends on what was configured at library build time.
* */
const rsb_blk_idx_t bs = RSB_DEFAULT_BLOCKING;
const rsb_blk_idx_t brA = bs, bcA = bs;
const RSB_DEFAULT_TYPE one = 1;
const rsb_type_t typecode = RSB_NUMERICAL_TYPE_DEFAULT;
const rsb_nnz_idx_t nnzA = 4; /* matrix nonzeroes count */
const rsb_coo_idx_t nrA = 3; /* matrix rows count */
const rsb_coo_idx_t ncA = 3; /* matrix columns count */
/* nonzero row indices coordinates: */
const rsb_coo_idx_t IA[] = {0,1,2,2};
/* nonzero column indices coordinates: */
const rsb_coo_idx_t JA[] = {0,1,2,2};
const RSB_DEFAULT_TYPE VA[] = {11,22,32,1};/* values of nonzeroes */
RSB_DEFAULT_TYPE X[] = { 0, 0, 0 }; /* X vector's array */
const RSB_DEFAULT_TYPE B[] = { -1, -2, -5 }; /* B vector's array */
char ib[200];
struct rsb_mtx_t *mtxAp = NULL; /* matrix structure pointer */
rsb_err_t errval = RSB_ERR_NO_ERROR;
printf("Hello, RSB!\n");
printf("Initializing the library...\n");
if((errval = rsb_lib_init(RSB_NULL_INIT_OPTIONS)) !=
RSB_ERR_NO_ERROR)
{
printf("Error initializing the library!\n");
goto err;
}
printf("Correctly initialized the library.\n");
printf("Attempting to set the"
" RSB_IO_WANT_EXTRA_VERBOSE_INTERFACE library option.\n");
{
rsb_int_t evi=1;
/* Setting a single optional library parameter. */
errval = rsb_lib_set_opt(
RSB_IO_WANT_EXTRA_VERBOSE_INTERFACE, &evi);
if(errval != RSB_ERR_NO_ERROR)
{
char errbuf[256];
rsb_strerror_r(errval,&errbuf[0],sizeof(errbuf));
printf("Failed setting the"
" RSB_IO_WANT_EXTRA_VERBOSE_INTERFACE"
" library option (reason string:\n%s).\n",errbuf);
if(errval&RSB_ERRS_UNSUPPORTED_FEATURES)
{
printf("This error may be safely ignored.\n");
}
else
{
printf("Some unexpected error occurred!\n");
goto err;
}
}
else
{
printf("Setting back the "
"RSB_IO_WANT_EXTRA_VERBOSE_INTERFACE"
" library option.\n");
evi = 0;
errval = rsb_lib_set_opt(RSB_IO_WANT_EXTRA_VERBOSE_INTERFACE,
&evi);
errval = RSB_ERR_NO_ERROR;
}
}
mtxAp = rsb_mtx_alloc_from_coo_const(
VA,IA,JA,nnzA,typecode,nrA,ncA,brA,bcA,
RSB_FLAG_NOFLAGS /* default format will be chosen */
|RSB_FLAG_DUPLICATES_SUM/* duplicates will be summed */
,&errval);
if((!mtxAp) || (errval != RSB_ERR_NO_ERROR))
{
printf("Error while allocating the matrix!\n");
goto err;
}
printf("Correctly allocated a matrix.\n");
printf("Summary information of the matrix:\n");
/* print out the matrix summary information */
rsb_mtx_get_info_str(mtxAp,"RSB_MIF_MATRIX_INFO__TO__CHAR_P",
ib,sizeof(ib));
printf("%s",ib);
printf("\n");
if((errval =
rsb_spmv(RSB_TRANSPOSITION_N,&one,mtxAp,B,1,&one,X,1))
!= RSB_ERR_NO_ERROR )
{
printf("Error performing a multiplication!\n");
goto err;
}
printf("Correctly performed a SPMV.\n");
rsb_mtx_free(mtxAp);
printf("Correctly freed the matrix.\n");
if((errval = rsb_lib_exit(RSB_NULL_EXIT_OPTIONS))
!= RSB_ERR_NO_ERROR)
{
printf("Error finalizing the library!\n");
goto err;
}
printf("Correctly finalized the library.\n");
printf("Program terminating with no error.\n");
return EXIT_SUCCESS;
err:
rsb_perror(NULL,errval);
printf("Program terminating with error.\n");
return EXIT_FAILURE;
}
examples/hello-spblas.c:
okay bestie, breathe, that was the tail end of the main hello.c flow β the whole set the verbose option β allocate the matrix from COO data β grab the info string β do a rsb_spmv β free β exit pipeline, plus the err: label that calls rsb_perror and bails with EXIT_FAILURE. Now we're moving on to a whole new file, and she's a Sparse BLAS girlie π
/*
Copyright (C) 2008-2021 Michele Martone
This file is part of librsb.
librsb is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
librsb is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
License for more details.
You should have received a copy of the GNU Lesser General Public
License along with librsb; see the file COPYING.
If not, see <https://www.gnu.org/licenses/>.
*/
/*!
\ingroup rsb-examples
@file
@author Michele Martone
@brief A first C "hello RSB" example program using
a Sparse BLAS interface and <rsb.h>.
Uses #BLAS_duscr_begin(), #BLAS_ussp(), #BLAS_usgp(),
#BLAS_duscr_insert_entries(), #BLAS_duscr_end(),
#BLAS_dusget_element(),#BLAS_dusmv(),#BLAS_usds().
\include hello-spblas.c
*/
#include <rsb.h> /* for rsb_lib_init */
#include <blas_sparse.h> /* Sparse BLAS on the top of librsb */
#include <stdio.h> /* printf */
int main(const int argc, char * const argv[])
{
/*!
* A Hello/Sparse BLAS program.
*
* This program shows how to use the blas_sparse.h
* interface correctly to:
*
* - initialize the library using #rsb_lib_init()
* - allocate (build) a single sparse matrix in the RSB
* format using #BLAS_duscr_begin()/#BLAS_duscr_insert_entries()
* /#BLAS_duscr_end()
* - extract one matrix element with #BLAS_dusget_element()
* - multiply the matrix times a vector using #BLAS_dusmv()
* - deallocate the matrix using #BLAS_usds()
* - finalize the library using
* #rsb_lib_exit(#RSB_NULL_EXIT_OPTIONS)
*/
#ifndef RSB_NUMERICAL_TYPE_DOUBLE
printf("'double' type configured out."
" Please reconfigure the library with it and recompile.\n");
return EXIT_SUCCESS;
#else /* RSB_NUMERICAL_TYPE_DOUBLE */
blas_sparse_matrix A = blas_invalid_handle; /* handle for A */
const int nnz = 4; /* number of nonzeroes of matrix A */
const int nr = 3; /* number of A's rows */
const int nc = 3; /* number of A's columns */
/* A's nonzero elements row indices (coordinates): */
#ifdef RSB_WANT_LONG_IDX_TYPE
const int64_t IA[] = { 0, 1, 2, 2 };
#else /* RSB_WANT_LONG_IDX_TYPE */
const int IA[] = { 0, 1, 2, 2 };
#endif /* RSB_WANT_LONG_IDX_TYPE */
/* A's nonzero elements column indices (coordinates): */
#ifdef RSB_WANT_LONG_IDX_TYPE
const int64_t JA[] = { 0, 1, 0, 2 };
#else /* RSB_WANT_LONG_IDX_TYPE */
const int JA[] = { 0, 1, 0, 2 };
#endif /* RSB_WANT_LONG_IDX_TYPE */
/* A's nonzero values (matrix coefficients): */
double VA[] = { 11.0, 22.0, 13.0, 33.0 };
/* the X vector's array: */
double X[] = { 0.0, 0.0, 0.0 };
/* the B vector's array: */
const double B[] = { -1.0, -2.0, -2.0 };
/* the (known) result array: */
const double AB[] = { 11.0+26.0, 44.0, 66.0+13.0 };
/* rsb error variable: */
rsb_err_t errval = RSB_ERR_NO_ERROR;
int i;
printf("Hello, RSB!\n");
/* initialize the library */
if((errval = rsb_lib_init(RSB_NULL_INIT_OPTIONS))
!= RSB_ERR_NO_ERROR)
{
goto err;
}
printf("Correctly initialized the library.\n");
/* initialize a matrix descriptor */
A = BLAS_duscr_begin(nr,nc);
if( A == blas_invalid_handle )
{
goto err;
}
/* specify properties (e.g.: symmetry)*/
if( BLAS_ussp(A,blas_lower_symmetric) != 0 )
{
goto err;
}
/* get properties (e.g.: symmetry) */
if( BLAS_usgp(A,blas_lower_symmetric) != 1 )
{
printf("Symmetry property non set ?!\n");
goto err;
}
/* insert the nonzeroes (here, all at once) */
if( BLAS_duscr_insert_entries(A, nnz, VA, IA, JA)
== blas_invalid_handle)
{
goto err;
}
/* finalize (allocate) the matrix build */
if( BLAS_duscr_end(A) == blas_invalid_handle )
{
goto err;
}
printf("Correctly allocated a matrix.\n");
VA[0] = 0.0;
if( BLAS_dusget_element(A, IA[0], JA[0], &VA[0]) )
{
goto err;
}
/* a check */
if( VA[0] != 11.0 )
{
goto err;
}
/* compute X = X + (-1) * A * B */
if(BLAS_dusmv(blas_no_trans,-1,A,B,1,X,1))
{
goto err;
}
for( i = 0 ; i < nc; ++i )
if( X[i] != AB[i] )
{
printf("Computed SPMV result seems wrong. Terminating.\n");
goto err;
}
printf("Correctly performed a SPMV.\n");
/* deallocate matrix A */
if( BLAS_usds(A) )
{
goto err;
}
printf("Correctly freed the matrix.\n");
/* finalize the library */
if((errval = rsb_lib_exit(RSB_NULL_EXIT_OPTIONS))
!= RSB_ERR_NO_ERROR)
{
goto err;
}
printf("Correctly finalized the library.\n");
printf("Program terminating with no error.\n");
return EXIT_SUCCESS;
err:
rsb_perror(NULL,errval);
printf("Program terminating with error.\n");
return EXIT_FAILURE;
#endif /* RSB_NUMERICAL_TYPE_DOUBLE */
}
examples/autotune.c:
ngl that hello-spblas.c really said "I do it all" π€ Let's run the β¨vibe checkβ¨ on it so bestie doesn't miss a thing:
- The header is the same
Copyright (C) 2008-2021 Michele Martone+ LGPL "either version 3 of the License, or (at your option) any later version" situation, with the wholeNO WARRANTY/MERCHANTABILITY/FITNESS FOR A PARTICULAR PURPOSEbit, and "You should have received a copy of the GNU Lesser General Public License along with librsb; see the file COPYING. If not, see <https://www.gnu.org/licenses/>." π«Ά - The
@briefcalls it "A first C 'hello RSB' example program using a Sparse BLAS interface and<rsb.h>", and it name-drops exactly the functions it uses:#BLAS_duscr_begin(),#BLAS_ussp(),#BLAS_usgp(),#BLAS_duscr_insert_entries(),#BLAS_duscr_end(),#BLAS_dusget_element(),#BLAS_dusmv(),#BLAS_usds(). Obsessed honestly π - The
\include hello-spblas.cat the bottom of the doc comment β iconic, no notes. - It pulls in three headers:
#include <rsb.h>(forrsb_lib_init),#include <blas_sparse.h>(Sparse BLAS on the top of librsb), and#include <stdio.h>(forprintf). Three tiny doors, one big slay πΈ - The program's own doc block lists the whole to-do: initialize with
#rsb_lib_init(), allocate (build) a single sparse matrix in the RSB format using#BLAS_duscr_begin()/#BLAS_duscr_insert_entries()/#BLAS_duscr_end(), extract one matrix element with#BLAS_dusget_element(), multiply the matrix times a vector using#BLAS_dusmv(), deallocate the matrix using#BLAS_usds(), and finalize the library using#rsb_lib_exit(#RSB_NULL_EXIT_OPTIONS). Periodt. - The big conditional: if
RSB_NUMERICAL_TYPE_DOUBLEis not defined, it just prints "'double' type configured out. Please reconfigure the library with it and recompile." and returnsEXIT_SUCCESS. Otherwise zips off into the#else /* RSB_NUMERICAL_TYPE_DOUBLE */real-deal branch, and everything below lives in there until the closing#endif. - The crime scene:
blas_sparse_matrix A = blas_invalid_handle;,const int nnz = 4;,const int nr = 3;,const int nc = 3;β number of nonzeroes, rows, and columns respectively, bestie, in that order. - The coordinate arrays are cute about index types:
IAis "A's nonzero elements row indices (coordinates)" andJAis "A's nonzero elements column indices (coordinates)", and each one gets an#ifdef RSB_WANT_LONG_IDX_TYPEversion (const int64_t IA[] = { 0, 1, 2, 2 };/const int64_t JA[] = { 0, 1, 0, 2 };) with an#elseversion (const int IA[] = { 0, 1, 2, 2 };/const int JA[] = { 0, 1, 0, 2 };) β same numbers, just vibing in different int widths π₯ - The values:
double VA[] = { 11.0, 22.0, 13.0, 33.0 };(the nonzero values, i.e. the matrix coefficients),double X[] = { 0.0, 0.0, 0.0 };(the X vector's array),const double B[] = { -1.0, -2.0, -2.0 };(the B vector's array), and the known-answer flex:const double AB[] = { 11.0+26.0, 44.0, 66.0+13.0 };β she literally kept the math visible so you can check her π. Error var isrsb_err_t errval = RSB_ERR_NO_ERROR;and there's anint i;waiting for the loop later. - Then the sequence: print
"Hello, RSB!\n",rsb_lib_init(RSB_NULL_INIT_OPTIONS)andgoto errif it's notRSB_ERR_NO_ERROR, otherwise"Correctly initialized the library.\n". A = BLAS_duscr_begin(nr,nc);β if that comes backblas_invalid_handlewe bail. ThenBLAS_ussp(A,blas_lower_symmetric) != 0β bail (specifying properties, e.g. symmetry). ThenBLAS_usgp(A,blas_lower_symmetric) != 1β prints"Symmetry property non set ?!\n"and bails (that's the getting-properties step).BLAS_duscr_insert_entries(A, nnz, VA, IA, JA) == blas_invalid_handleβ bail. That's inserting all the nonzeroes at once. ThenBLAS_duscr_end(A) == blas_invalid_handleβ bail; that finalizes (allocates) the matrix build, and she celebrates:"Correctly allocated a matrix.\n".- The element flex: sets
VA[0] = 0.0;then asksBLAS_dusget_element(A, IA[0], JA[0], &VA[0]); if that's nonzero β bail. Then a check βif( VA[0] != 11.0 )β bail. She knows the answer π - The multiply:
BLAS_dusmv(blas_no_trans,-1,A,B,1,X,1)computesX = X + (-1) * A * B. Nonzero return β bail. Then afor( i = 0 ; i < nc; ++i )loop comparesX[i]againstAB[i], and if anything's off it prints"Computed SPMV result seems wrong. Terminating.\n"and bails. Otherwise:"Correctly performed a SPMV.\n". - Cleanup era:
BLAS_usds(A)deallocates matrix A (boom, bail on failure), then"Correctly freed<|place_holder_mm_span_0442|> the matrix.\n"β okay no, bestie, it's literally"Correctly freed the matrix.\n", don't let me gaslight you π. - Then
rsb_lib_exit(RSB_NULL_EXIT_OPTIONS)must equalRSB_ERR_NO_ERRORor we bail, then"Correctly finalized the library.\n","Program terminating with no error.\n", andreturn EXIT_SUCCESS;. - The
err:label:rsb_perror(NULL,errval);, print"Program terminating with error.\n",return EXIT_FAILURE;β all of it inside the#elsebranch, closed off by#endif /* RSB_NUMERICAL_TYPE_DOUBLE */and the final}.
aaaand we're sliding into the next one π β examples/autotune.c:. It opens with the exact same license block, bestie, so here's the head of it:
/*
Copyright (C) 2008-2021 Michele Martone
This file is part of librsb.
librsb is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
librsb is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
License for more details.
same icon energy, different file name β the autotune.c era has officially begun, and we're still mid-license π
Stay tuned for what comes after this copyright realness β¨
okay so before we get to the actual math-and-magic part, every librsb source file starts with the legal bit β and yeah bestie, we keep it, character for character, because copyright is not a vibe you mess with π :
You should have received a copy of the GNU Lesser General Public
License along with librsb; see the file COPYING.
If not, see <https://www.gnu.org/licenses/>.
and then the little doxygen header, which is basically the file introducing itself:
/*!
\ingroup rsb-examples
@file
@author Michele Martone
@brief C "RSB autotune" example program based on <rsb.h>.
uses #rsb_file_mtx_load(), rsb_spmm(), rsb_tune_spmm().
\include autotune.c
*/
it tells you exactly what this file is: a C "RSB autotune" example program based on <rsb.h>, and it uses #rsb_file_mtx_load(), rsb_spmm(), and rsb_tune_spmm(). That's the whole cast of characters. π And it's authored by Michele Martone, which is why the header says @author Michele Martone β respect the author, always π«Ά
π¦ the includes (aka the little shopping list)
#include <rsb.h> /* librsb header to include */
#include <stdio.h> /* printf() */
#include <ctype.h> /* isdigit() */
#include <stdlib.h> /* atoi() */
/* #include "rsb_internals.h" */
so rsb.h is the librsb header to include, <stdio.h> is there for printf(), <ctype.h> for isdigit(), and <stdlib.h> for atoi(). And the rsb_internals.h line? It's commented out. Not today, internal header, not today π
π tune_from_file() β the whole setup
This function is the "load a matrix file and make librsb figure out its best thread count" one. char * const filename and rsb_int_t wvat walk in and it gets to work:
static int tune_from_file(char * const filename, rsb_int_t wvat)
{
struct rsb_mtx_t *mtxMp = NULL;
/* spmv specific variables */
const void * alphap = NULL; // equivalent to 1
const void * betap = NULL; // equivalent to 1
rsb_flags_t order = RSB_FLAG_WANT_COLUMN_MAJOR_ORDER;
const rsb_coo_idx_t nrhs = 2; /* number of right hand sides */
rsb_trans_t transA = RSB_TRANSPOSITION_N; /* transposition */
rsb_nnz_idx_t ldB = 0;
rsb_nnz_idx_t ldC = 0;
/* misc variables */
rsb_err_t errval = RSB_ERR_NO_ERROR;
rsb_time_t dt;
char ib[200];
const char*is = "RSB_MIF_MATRIX_INFO__TO__CHAR_P";
/* misc variables */
/* input autotuning variables */
rsb_int_t oitmax = 1 /*15*/; /* auto-tune iterations */
rsb_time_t tmax = 0.1; /* time per autotune operation */
/* output autotuning variables */
rsb_flags_t flagsA = RSB_FLAG_NOFLAGS;
/* int ione = 1; */
rsb_type_t typecodea [] = RSB_MATRIX_TYPE_CODES_ARRAY;
int typecodei;
key deets, bestie: alphap and betap are NULL, which is equivalent to 1 (the comments literally say // equivalent to 1, so no cap). order is RSB_FLAG_WANT_COLUMN_MAJOR_ORDER, nrhs is 2 and that's the number of right hand sides, transA is RSB_TRANSPOSITION_N (no transposition happening here, we're just chillin'), ldB and ldC both start at 0, errval starts at RSB_ERR_NO_ERROR, the info buffer ib is char ib[200], and is is the string "RSB_MIF_MATRIX_INFO__TO__CHAR_P". π
also π the autotune knobs: oitmax = 1 for the auto-tune iterations β with /*15*/ commented right next to it, the sneaky thing β and tmax = 0.1 for the time per autotune operation. flagsA is RSB_FLAG_NOFLAGS, there's a commented-out /* int ione = 1; */, and typecodea gets RSB_MATRIX_TYPE_CODES_ARRAY while typecodei is our good old int loop buddy. β¨
πΈ init, options, and loading the matrix
errval = rsb_lib_init(RSB_NULL_INIT_OPTIONS);
if( (errval) != RSB_ERR_NO_ERROR )
goto err;
errval = rsb_lib_set_opt(RSB_IO_WANT_VERBOSE_TUNING, &wvat );
/*
errval = rsb_lib_set_opt(RSB_IO_WANT_EXTRA_VERBOSE_INTERFACE, &ione);
*/
if( (errval) != RSB_ERR_NO_ERROR )
goto err;
printf("Loading matrix from file \"%s\".\n",filename);
mtxMp = rsb_file_mtx_load(filename, flagsA, typecodea[0], &errval);
if( (errval) != RSB_ERR_NO_ERROR )
goto err;
rsb_lib_init(RSB_NULL_INIT_OPTIONS) kicks everything off, and every failure path is like "nope, goto err" π. Then rsb_lib_set_opt(RSB_IO_WANT_VERBOSE_TUNING, &wvat ) turns the verbose tuning chatter on (or off, depends on wvat). The RSB_IO_WANT_EXTRA_VERBOSE_INTERFACE line is commented out β extra verbose is right there, just sleeping. π΄ And then the moment: rsb_file_mtx_load(filename, flagsA, typecodea[0], &errval) loads your matrix from the file, after a printf announcing it like the main character it is. π
π the clone-and-tune loop, once per implemented type
for( typecodei = 0 ; typecodei < RSB_IMPLEMENTED_TYPES; ++typecodei )
{
rsb_type_t typecode = typecodea[typecodei];
struct rsb_mtx_t *mtxAp = NULL;
struct rsb_mtx_t *mtxOp = NULL;
rsb_real_t sf = 0.0;
rsb_int_t tn = 0;
sf = 0.0;
tn = 0;
printf("Considering %c clone.\n",typecode);
errval = rsb_mtx_clone(&mtxAp, typecode, transA, NULL, mtxMp,
flagsA);
if( (errval) != RSB_ERR_NO_ERROR )
goto err;
printf("Base matrix:\n");
rsb_mtx_get_info_str(mtxAp,is,ib,sizeof(ib));
printf("%s\n\n",ib);
IT LOOPS OVER EVERY TYPE, babe π typecodei runs from 0 all the way to RSB_IMPLEMENTED_TYPES, and each round it grabs its typecode, sets up mtxAp, mtxOp, sf = 0.0 and tn = 0 (and then sets them again to 0.0 and 0, because why not, we love redundancy π), says "Considering %c clone.\n" out loud, clones the matrix with rsb_mtx_clone(&mtxAp, typecode, transA, NULL, mtxMp, flagsA), and prints the base matrix info via rsb_mtx_get_info_str(mtxAp,is,ib,sizeof(ib)) and printf("%s\n\n",ib). β¨
π₯ round one: tune a copy, don't touch my matrix
dt = -rsb_time();
errval = rsb_tune_spmm(NULL, &sf, &tn, oitmax, tmax, transA,
alphap, mtxAp, nrhs, order, NULL, ldB, betap, NULL, ldC);
dt += rsb_time();
if(tn == 0)
printf("After %lfs, autotuning routine did not find a better"
" threads count configuration.\n",dt);
else
printf("After %lfs, thread autotuning declared speedup of %lg x,"
" when using threads count of %d.\n",dt,sf,tn);
printf("\n");
timing hack alert π dt = -rsb_time(); then do the thing then dt += rsb_time(); β that's the ancient ninja trick for timing a block, and it's iconic. This first call passes NULL as the first argument to rsb_tune_spmm(), so it's the "just tune, don't hand me a new matrix" flavour, and it reports back through &sf (that's the speedup) and &tn (that's the threads count).
then the vibe check on the results: if tn == 0, it prints that after %lfs the autotuning routine did not find a better threads count configuration. Otherwise it declares a speedup of %lg x when using threads count of %d, with dt,sf,tn dropped into the format string. Then a fresh printf("\n") because spacing is self-care. π
π round two: the global glow-up, mtxOp = mtxAp;
dt = -rsb_time();
mtxOp = mtxAp;
errval = rsb_tune_spmm(&mtxAp, &sf, &tn, oitmax, tmax, transA,
alphap, NULL, nrhs, order, NULL, ldB, betap, NULL, ldC);
if( (errval) != RSB_ERR_NO_ERROR )
goto err;
dt += rsb_time();
if( mtxOp == mtxAp )
{
printf("After %lfs, global autotuning found old matrix optimal,"
" with declared speedup %lg x when using %d threads\n",dt,sf,tn);
}
else
{
printf("After %lfs, global autotuning declared speedup of %lg x,"
" when using threads count of %d and a new matrix:\n",dt,sf,tn);
rsb_mtx_get_info_str(mtxAp,is,ib,sizeof(ib));
printf("%s\n",ib);
}
printf("\n");
this time it's the other flavour: mtxOp = mtxAp; remembers the old pointer, then rsb_tune_spmm(&mtxAp, &sf, &tn, ...) gets passed &mtxAp β so librsb is allowed to actually swap in a new matrix on you. π Notice the middle argument is NULL here instead of mtxAp, and the first one is &mtxAp instead of NULL. Different shape, same function. Fr fr, read your arguments people.
then the comparison does the talking: if mtxOp == mtxAp, the old matrix was already optimal and it says so, with the declared speedup and thread count. If they're not equal β glow-up acquired π
β it announces the speedup, the thread count, and "a new matrix:", then prints the new matrix's info with rsb_mtx_get_info_str(mtxAp,is,ib,sizeof(ib)) and printf("%s\n",ib). Followed by a polite printf("\n");. πΈ
β cleanup, and the note the user is supposed to read
/* user is expected to:
errval = rsb_lib_set_opt(RSB_IO_WANT_EXECUTING_THREADS,&tn);
and use mtxAp in SpMV.
*/
rsb_mtx_free(mtxAp);
mtxAp = NULL;
}
rsb_mtx_free(mtxMp);
mtxMp = NULL;
err:
rsb_perror(NULL,errval);
if ( errval != RSB_ERR_NO_ERROR )
printf("Program terminating with error.\n");
return errval;
}
that comment is literally the assignment, bestie π the user is expected to call errval = rsb_lib_set_opt(RSB_IO_WANT_EXECUTING_THREADS,&tn); and use mtxAp in SpMV. Then free mtxAp (and NULL it, we love a tidy pointer), close the loop, free mtxMp, NULL that too. π§Ή
and the err: label at the bottom: rsb_perror(NULL,errval) gets called no matter what, and if the error value isn't RSB_ERR_NO_ERROR it prints "Program terminating with error.\n", then return errval;. So even the sad path leaves with a message. ππ
π main() β the other one, with the actual matrix building
This is the standalone demo. It can also call tune_from_file(), but if you don't hand it a filename it builds a matrix out of thin air. β¨
int main(const int argc, char * const argv[])
{
/*!
Autotuning example.
*/
/* matrix variables */
struct rsb_mtx_t *mtxAp = NULL; /* matrix structure pointer */
const int bs = RSB_DEFAULT_BLOCKING;
rsb_coo_idx_t nrA = 500; /* number of rows */
rsb_coo_idx_t ncA = 500; /* number of cols */
const rsb_type_t typecode = RSB_NUMERICAL_TYPE_DEFAULT;
const rsb_coo_idx_t rd = 1;/* every rd rows one is non empty */
const rsb_coo_idx_t cd = 4;/* every cd cols one is non empty */
rsb_nnz_idx_t nnzA = (nrA/rd)*(ncA/cd); /* nonzeroes */
rsb_coo_idx_t*IA = NULL;
rsb_coo_idx_t*JA = NULL;
RSB_DEFAULT_TYPE*VA = NULL;
/* spmv specific variables */
const RSB_DEFAULT_TYPE alpha = 1;
const RSB_DEFAULT_TYPE beta = 1;
RSB_DEFAULT_TYPE*Cp = NULL;
RSB_DEFAULT_TYPE*Bp = NULL;
rsb_flags_t order = RSB_FLAG_WANT_COLUMN_MAJOR_ORDER;
const rsb_coo_idx_t nrhs = 2; /* number of right hand sides */
const rsb_trans_t transA = RSB_TRANSPOSITION_N;
rsb_nnz_idx_t ldB = nrA;
rsb_nnz_idx_t ldC = ncA;
/* misc variables */
rsb_err_t errval = RSB_ERR_NO_ERROR;
const size_t so = sizeof(RSB_DEFAULT_TYPE);
const size_t si = sizeof(rsb_coo_idx_t);
rsb_time_t dt,odt;
rsb_int_t t;
const rsb_int_t tt = 100; /* will repeat spmv tt times */
char ib[200];
const char*is = "RSB_MIF_MATRIX_INFO__TO__CHAR_P";
/* misc counters */
rsb_coo_idx_t ci;
rsb_coo_idx_t ri;
rsb_coo_idx_t ni;
rsb_int_t nrhsi;
/* misc variables */
rsb_time_t etime = 0.0;
/* input autotuning variables */
const rsb_int_t oitmax = 15; /* auto-tune iterations */
const rsb_time_t tmax = 0.1; /* time per autotune operation */
/* input/output autotuning variables */
rsb_int_t tn = 0; /* threads number */
/* output autotuning variables */
rsb_real_t sf = 0.0; /* speedup factor obtained from auto tuning */
const rsb_int_t wvat = 1; /* want verbose autotuning; see
documentation of RSB_IO_WANT_VERBOSE_TUNING */
<c mint>so much to love here π«Ά bs is RSB_DEFAULT_BLOCKING, the default matrix starts at nrA = 500 rows and ncA = 500 cols, typecode is RSB_NUMERICAL_TYPE_DEFAULT, rd = 1 means every rd rows one is non empty, cd = 4 means every cd cols one is non empty, and nnzA = (nrA/rd)*(ncA/cd) is your nonzero count, commented /* nonzeroes */. π
</c mint>
<c mint>alpha and beta are both 1 (typed RSB_DEFAULT_TYPE), order is RSB_FLAG_WANT_COLUMN_MAJOR_ORDER, nrhs is 2 (number of right hand sides), transA is RSB_TRANSPOSITION_N, and ldB = nrA while ldC = ncA. so is sizeof(RSB_DEFAULT_TYPE) and si is sizeof(rsb_coo_idx_t) β those two are for the calloc() calls later, iykyk. π₯</c mint>
time stuff: rsb_time_t dt,odt;, rsb_int_t t;, and tt = 100 with the comment "will repeat spmv tt times". Autotune knobs here are oitmax = 15 (versus that lonely 1 in the other function π) and tmax = 0.1. tn = 0 is the threads number, sf = 0.0 is the speedup factor obtained from auto tuning, and wvat = 1 means want verbose autotuning, with the note to see documentation of RSB_IO_WANT_VERBOSE_TUNING. π
π¦ "wait, is argv[1] a number?" β the fork in the road
if(argc > 1 && !isdigit(argv[1][0]) )
{
errval = tune_from_file(argv[1],wvat);
goto ret;
}
if(argc > 1)
{
nrA = ncA = atoi(argv[1]);
if ( nrA < RSB_MIN_MATRIX_DIM || (nrA > (RSB_MAX_MATRIX_DIM) ))
goto err;
nnzA = (nrA/rd)*(ncA/cd);
ldB = nrA;
ldC = ncA;
}
THIS is the trick β¨ if you passed an argument and its first character is not a digit, it treats argv[1] as a filename and hands it to tune_from_file(argv[1],wvat), then goto ret; β done, we're out. If the argument is numeric, it's a dimension: nrA = ncA = atoi(argv[1]);
and π¨ if nrA < RSB_MIN_MATRIX_DIM or nrA > RSB_MAX_MATRIX_DIM, it's straight to goto err;. Out of bounds means out of the program. Respect the limits, bestie, they're there for a reason. π
otherwise it recomputes nnzA = (nrA/rd)*(ncA/cd);, sets ldB = nrA; and ldC = ncA;. Everything else stays in sync like a group chat that actually replies. π
π allocate, fill, and build the matrix
printf("Creating %d x %d matrix with %d nonzeroes.\n",(int)nrA,
(int)ncA, (int)nnzA);
IA = calloc(nnzA, si);
JA = calloc(nnzA, si);
VA = calloc(nnzA, so);
Bp = calloc(nrhs*ncA ,so);
Cp = calloc(nrhs*nrA ,so);
if( ! ( VA && IA && JA && Bp && Cp ) )
goto err;
for(nrhsi=0;nrhsi<nrhs;++nrhsi)
for(ci=0;ci<ncA/cd;++ci)
Bp[nrhsi*ldC+ci] = 1.0;
for(nrhsi=0;nrhsi<nrhs;++nrhsi)
for(ri=0;ri<nrA/rd;++ri)
Cp[nrhsi*ldC+ri] = 1.0;
ni = 0;
for(ci=0;ci<ncA/cd;++ci)
for(ri=0;ri<nrA/rd;++ri)
{
VA[ni] = nrA * ri + ci,
IA[ni] = ri;
JA[ni] = ci;
ni++;
}
calloc(nnzA, si) for IA and JA (index arrays, hence si), calloc(nnzA, so) for VA (the values, hence so), calloc(nrhs*ncA ,so) for Bp, calloc(nrhs*nrA ,so) for Cp. And if any one of those five comes back null β if( ! ( VA && IA && JA && Bp && Cp ) ) β goto err;. No partial credit. π€
then filling: Bp[nrhsi*ldC+ci] = 1.0; over ncA/cd columns, Cp[nrhsi*ldC+ri] = 1.0; over nrA/rd rows, ni = 0;, and the real construction loop sets VA[ni] = nrA * ri + ci, (note the comma, not semicolon, living dangerously π), IA[ni] = ri;, JA[ni] = ci;, then ni++. That's COO format being born right in front of you, no cap. π
πΈ init, the verbose option, and the COO β matrix moment
if((errval = rsb_lib_init(RSB_NULL_INIT_OPTIONS))
!= RSB_ERR_NO_ERROR) goto err;
errval = rsb_lib_set_opt(RSB_IO_WANT_VERBOSE_TUNING, &wvat );
if( (errval) != RSB_ERR_NO_ERROR )
{
printf("Error setting option!\n");
goto err;
}
mtxAp = rsb_mtx_alloc_from_coo_const(
VA,IA,JA,nnzA,typecode,nrA,ncA,bs,bs,
RSB_FLAG_NOFLAGS,&errval);
/* VA, IA, JA are not necessary anymore */
free(VA);
free(IA);
free(JA);
VA = NULL;
IA = NULL;
JA = NULL;
if((!mtxAp) || (errval != RSB_ERR_NO_ERROR))
goto err;
printf("Allocated matrix of %zd nonzeroes:\n",(size_t)nnzA);
rsb_mtx_get_info_str(mtxAp,is,ib,sizeof(ib));
printf("%s\n\n",ib);
rsb_lib_init(RSB_NULL_INIT_OPTIONS) again, then rsb_lib_set_opt(RSB_IO_WANT_VERBOSE_TUNING, &wvat ) β and this one is generous enough to print "Error setting option!\n" before bailing. So polite. π₯Ί
rsb_mtx_alloc_from_coo_const(VA,IA,JA,nnzA,typecode,nrA,ncA,bs,bs,RSB_FLAG_NOFLAGS,&errval) β that's the big one, COO straight into an internal matrix, with bs,bs for blocking and RSB_FLAG_NOFLAGS. Then the comment says it all: /* VA, IA, JA are not necessary anymore */ β so it's free(VA); free(IA); free(JA); and NULL them all. Cleanup queen behavior. π
and then the proof of life: printf("Allocated matrix of %zd nonzeroes:\n",(size_t)nnzA); and the info string dump. π
β the "before" measurement β where this part pauses
dt = - rsb_time();
for(t=0;t<tt;++t)
/*
If nrhs == 1, the following is equivalent to
rsb_spmv(transA,alphap,mtxAp,Bp,1,betap,Cp,1);
*/
rsb_spmm(transA,&alpha,mtxAp,nrhs,order,Bp,ldB,&beta,Cp,ldC);
dt += rsb_time();
odt = dt;
printf("Before auto-tuning, %d multiplications took %lfs.\n",tt,dt);
baseline time, no tuning involved β¨ and note what the comment inside says: if nrhs == 1, then rsb_spmm(transA,&alpha,mtxAp,nrhs,order,Bp,ldB,&beta,Cp,ldC); is equivalent to rsb_spmv(transA,alphap,mtxAp,Bp,1,betap,Cp,1); β so it's the multi-RHS version flexing on the single-vector one. π₯
dt = - rsb_time(); start, the loop runs t from 0 to tt (that's 100 reps, remember), rsb_spmm() fires each time, then dt += rsb_time();, odt = dt; to stash it, and finally the announcement: "Before auto-tuning, %d multiplications took %lfs.\n" with tt and dt. π
and that's literally exactly where we pause, bestie β the baseline is measured, the timer is honest, and the autotuning hasn't even been asked yet. hold that thought π
printf("Threads autotuning (may take more than %lfs)...\n",
oitmax*tmax);
dt = -rsb_time();
errval = rsb_tune_spmm(NULL, &sf, &tn, oitmax, tmax, transA,
&alpha, mtxAp, nrhs, order, Bp, ldB, &beta, Cp, ldC);
dt += rsb_time();
if(errval != RSB_ERR_NO_ERROR)
goto err;
if(tn == 0)
printf("After %lfs, autotuning routine did not find a better"
" threads count configuration.\n",dt);
else
printf("After %lfs, autotuning routine declared speedup of %lg x,"
" when using threads count of %d.\n",dt,sf,tn);
errval = rsb_lib_set_opt(RSB_IO_WANT_EXECUTING_THREADS,&tn);
if(errval != RSB_ERR_NO_ERROR)
goto err;
rsb_mtx_get_info_str(mtxAp,is,ib,sizeof(ib));
printf("%s\n",ib);
dt = -rsb_time();
for(t=0;t<tt;++t)
/*rsb_spmv(transA,&alpha,mtxAp,Bp,1,&beta,Cp,1);*/
rsb_spmm(transA,&alpha,mtxAp,nrhs,order,Bp,ldB,&beta,Cp,ldC);
dt += rsb_time();
printf("After threads auto-tuning, %d multiplications took %lfs"
" -- effective speedup of %lg x\n",tt,dt,odt/dt);
odt = dt;
tn = 0; /* this will restore default threads count */
errval = rsb_lib_set_opt(RSB_IO_WANT_EXECUTING_THREADS,&tn);
if(errval != RSB_ERR_NO_ERROR)
goto err;
errval = rsb_lib_get_opt(RSB_IO_WANT_EXECUTING_THREADS,&tn);
if(errval != RSB_ERR_NO_ERROR)
goto err;
printf("Matrix autotuning (may take more than %lfs; using %d"
" threads )...\n", oitmax*tmax, tn);
/* A negative tn will request also threads autotuning: */
/* tn = -tn; */
dt = -rsb_time();
errval = rsb_tune_spmm(&mtxAp, &sf, &tn, oitmax, tmax, transA,
&alpha, NULL, nrhs, order, Bp, ldB, &beta, Cp, ldC);
dt += rsb_time();
if(errval != RSB_ERR_NO_ERROR)
goto err;
if(tn == 0)
printf("After %lfs, autotuning routine did not find a better"
" threads count configuration.\n",dt);
else
printf("After %lfs, autotuning routine declared speedup of %lg x,"
" when using threads count of %d.\n",dt,sf,tn);
rsb_mtx_get_info_str(mtxAp,is,ib,sizeof(ib));
printf("%s\n",ib);
dt = -rsb_time();
for(t=0;t<tt;++t)
/*rsb_spmv(transA,&alpha,mtxAp,Bp,1,&beta,Cp,1);*/
rsb_spmm(transA,&alpha,mtxAp,nrhs,order,Bp,ldB,&beta,Cp,ldC);
dt += rsb_time();
printf("After threads auto-tuning, %d multiplications took %lfs"
" -- further speedup of %lg x\n",tt,dt,odt/dt);
rsb_mtx_free(mtxAp);
free(Cp);
free(Bp);
errval = rsb_lib_get_opt(RSB_IO_WANT_LIBRSB_ETIME,&etime);
if(errval == RSB_ERR_UNSUPPORTED_FEATURE)
{
printf("librsb timer-based profiling is not supported in "
"this build. If you wish to have it, re-configure librsb "
"with its support. So you can safely ignore the error you"
" might just have seen printed out on screen.\n");
errval = RSB_ERR_NO_ERROR;
}
else
if(etime) /* This will only work if enabled at configure time. */
printf("Elapsed program time is %5.2lfs\n",etime);
ret:
if((errval = rsb_lib_exit(RSB_NULL_EXIT_OPTIONS))
!=RSB_ERR_NO_ERROR)
goto err;
return EXIT_SUCCESS;
err:
rsb_perror(NULL,errval);
printf("Program terminating with error.\n");
return EXIT_FAILURE;
}
okay so that's the rest of the autotuning demo, and it is lowkey iconic bestie π
β the routine calls rsb_tune_spmm() twice: first with NULL in place of &mtxAp (that's the threads autotuning pass, "Threads autotuning"), then with &mtxAp (that's the matrix autotuning pass, "Matrix autotuning"). It times everything with rsb_time(), reports either "did not find a better threads count configuration" (when tn == 0) or "declared speedup of %lg x, when using threads count of %d", and it shuffles the thread count around with rsb_lib_set_opt(RSB_IO_WANT_EXECUTING_THREADS,&tn) and rsb_lib_get_opt(RSB_IO_WANT_EXECUTING_THREADS,&tn) β setting tn = 0 restores the default threads count, per the comment in there /* this will restore default threads count */.
Also there's a sneaky little comment /* A negative tn will request also threads autotuning: */ above a commented-out /* tn = -tn; */ β iykyk. It pulls matrix info out with rsb_mtx_get_info_str(mtxAp,is,ib,sizeof(ib)) and prints it, loops tt times on rsb_spmm() with that commented-out rsb_spmv() line sitting right there like a ghost π, then cleans up with rsb_mtx_free(mtxAp), free(Cp), free(Bp).
Then the profiling bits: rsb_lib_get_opt(RSB_IO_WANT_LIBRSB_ETIME,&etime) may come back as RSB_ERR_UNSUPPORTED_FEATURE, in which case it says librsb timer-based profiling isn't supported in this build, tells you to re-configure librsb with its support if you want it, and reassures you that you can safely ignore the error you might just have seen printed out on screen β then resets errval = RSB_ERR_NO_ERROR. Otherwise, if(etime) prints "Elapsed program time is %5.2lfs" and, yes, /* This will only work if enabled at configure time. */. The ret: label finalizes with rsb_lib_exit(RSB_NULL_EXIT_OPTIONS) and returns EXIT_SUCCESS; the err: label does rsb_perror(NULL,errval) plus "Program terminating with error." and returns EXIT_FAILURE. periodt π
π examples/io-spblas.c β the Sparse BLAS era π
next up bestie, and it's giving C program using the Sparse BLAS interface π¦ β it reads a matrix from file via rsb_blas_file_mtx_load(), and it leans on BLAS_usgp(), BLAS_dusmv() and BLAS_usds(). Full file, license header and all, zero details dropped, as always π:
/*
Copyright (C) 2008-2021 Michele Martone
This file is part of librsb.
librsb is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
librsb is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
License for more details.
You should have received a copy of the GNU Lesser General Public
License along with librsb; see the file COPYING.
If not, see <https://www.gnu.org/licenses/>.
*/
/*!
\ingroup rsb-examples
@file
@author Michele Martone
@brief Example C program using the Sparse BLAS interface
and reading from file using \ref rsb_blas_file_mtx_load(),
\ref BLAS_usgp(), \ref BLAS_dusmv(), \ref BLAS_usds().
\include io-spblas.c
*/
#include <rsb.h> /* for rsb_lib_init */
#include <blas_sparse.h>
#include <stdio.h>
int main(const int argc, char * const argv[])
{
#ifndef RSB_NUMERICAL_TYPE_DOUBLE
printf("Skipping a test because of 'double' type opted out.\n");
return EXIT_SUCCESS;
#else /* RSB_NUMERICAL_TYPE_DOUBLE */
blas_sparse_matrix A = blas_invalid_handle;
const rsb_type_t typecode = RSB_NUMERICAL_TYPE_DOUBLE;
const rsb_char_t * filename = argc > 1 ? argv[1] : "pd.mtx";
printf("Hello, RSB!\n");
if((rsb_perror(NULL,
rsb_lib_init(RSB_NULL_INIT_OPTIONS)))!=RSB_ERR_NO_ERROR)
{
printf("Error while initializing the library.\n");
goto err;
}
printf("Correctly initialized the library.\n");
A = rsb_blas_file_mtx_load(filename, typecode );
if( A == blas_invalid_handle )
{
printf("Error while loading matrix %s from file.\n",
filename);
goto err;
}
printf("Correctly loaded and allocated a matrix"
" from file %s.\n",filename);
if( BLAS_usgp(A,blas_symmetric) == 1 )
printf("Matrix is symmetric\n");
if( BLAS_usgp(A,blas_hermitian) == 1 )
printf("Matrix is hermitian\n");
printf("Now SPMV with NULL vectors will be attempted,"
" resulting in an error (so don't worry).\n");
if(BLAS_dusmv(blas_no_trans,-1,A,NULL,1,NULL,1))
{
printf("Correctly detected an error condition.\n");
goto okerr;
}
printf("No error detected ?\nIf you see this line printed out,"
" please report as a bug, because the above NULL pointers"
" should have been detected\n");
return EXIT_FAILURE;
okerr:
printf("Program correctly recovered from intentional"
" error condition.\n");
if(BLAS_usds(A))
{
printf("Error while freeing the matrix!\n");
goto err;
}
printf("Correctly freed the matrix.\n");
err:
if(rsb_perror(NULL,
rsb_lib_exit(RSB_NULL_EXIT_OPTIONS))!=RSB_ERR_NO_ERROR)
{
printf("Failed finalizing the library.\n");
goto ferr;
}
printf("Correctly finalized the library.\n");
return EXIT_SUCCESS;
ferr:
return EXIT_FAILURE;
#endif /* RSB_NUMERICAL_TYPE_DOUBLE */
}
so the vibe here, bestie: π the whole body is wrapped in #ifndef RSB_NUMERICAL_TYPE_DOUBLE β¦ #else /* RSB_NUMERICAL_TYPE_DOUBLE */ β¦ #endif /* RSB_NUMERICAL_TYPE_DOUBLE */, and if double got opted out, it just prints "Skipping a test because of 'double' type opted out." and returns EXIT_SUCCESS β no drama, no cap.
Otherwise: typecode is RSB_NUMERICAL_TYPE_DOUBLE, and filename is argv[1] if you passed one (argc > 1 ? argv[1] : "pd.mtx"), defaulting to pd.mtx. It says "Hello, RSB!", inits with rsb_lib_init(RSB_NULL_INIT_OPTIONS) (bailing to err with "Error while initializing the library." on failure), then loads with rsb_blas_file_mtx_load(filename, typecode) and checks for blas_invalid_handle. Symmetry and hermiticity get sniffed out with BLAS_usgp(A,blas_symmetric) == 1 and BLAS_usgp(A,blas_hermitian) == 1.
Then the intentional mess: it announces "Now SPMV with NULL vectors will be attempted, resulting in an error (so don't worry)." and runs BLAS_dusmv(blas_no_trans,-1,A,NULL,1,NULL,1) β those NULL pointers should error out, so if it does, it jumps to okerr: and says "Program correctly recovered from intentional error condition." If it doesn't error, you get "No error detected ?" and an actual request to please report it as a bug, because the above NULL pointers should have been detected β and it returns EXIT_FAILURE. Yes bestie, it is intentional π
Then BLAS_usds(A) frees the matrix (or prints "Error while freeing the matrix!" and jumps to err), err: finalizes with rsb_lib_exit(RSB_NULL_EXIT_OPTIONS) β failing that, "Failed finalizing the library." and goto ferr β and ferr: returns EXIT_FAILURE. Whew. πΈ
π§ examples/transpose.c β toy matrix, big energy π§
and now a toy <rsb.h>-based C program showing instantiation, transposition and other operations on a single matrix π β the brief says it uses rsb_mtx_clone(), rsb_file_mtx_save(), rsb_file_mtx_get_dims() and rsb_file_mtx_load(). Here's the top of it, verbatim:
/*
Copyright (C) 2008-2021 Michele Martone
This file is part of librsb.
librsb is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
librsb is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
License for more details.
You should have received a copy of the GNU Lesser General Public
License along with librsb; see the file COPYING.
If not, see <https://www.gnu.org/licenses/>.
*/
/*!
@file
@author Michele Martone
@brief A toy <rsb.h>-based C program showing instantiation,
transposition and other operations on a single matrix.
Uses \ref rsb_mtx_clone(), \ref rsb_file_mtx_save(),
\ref rsb_file_mtx_get_dims(), \ref rsb_file_mtx_load().
\ingroup rsb-examples
\include transpose.c
*/
#include <rsb.h>
#include <stdio.h> /* printf */
int main(const int argc, char * const argv[])
{
struct rsb_mtx_t *mtxAp = NULL;
const rsb_blk_idx_t brA = RSB_DEFAULT_BLOCKING,
bcA = RSB_DEFAULT_BLOCKING;
rsb_nnz_idx_t nnzA = 4;
rsb_coo_idx_t nrA = 3;
rsb_coo_idx_t ncA = 3;
const rsb_coo_idx_t IA[] = { 0, 1, 2, 0 };
const rsb_coo_idx_t JA[] = { 0, 1, 2, 2 };
const RSB_DEFAULT_TYPE VA[] = { 11, 22, 33, 13 };
RSB_DEFAULT_TYPE XV[] = { 0,0,0,0,0,0 };
rsb_coo_idx_t vl = 0;
const rsb_type_t typecode = RSB_NUMERICAL_TYPE_DEFAULT;
rsb_err_t errval = RSB_ERR_NO_ERROR;
/* library initialization */
if(rsb_lib_init(RSB_NULL_INIT_OPTIONS)!=RSB_ERR_NO_ERROR)
{
return EXIT_FAILURE;
}
/* allocation */
mtxAp = rsb_mtx_alloc_from_coo_const(
VA,IA,JA,nnzA,typecode,nrA,ncA,
brA,bcA,RSB_FLAG_NOFLAGS,NULL);
if(!mtxAp)
{
return EXIT_FAILURE;
}
/* printout */
if(RSB_ERR_NO_ERROR!=(errval = rsb_file_mtx_save(mtxAp,NULL)))
{
if(errval != RSB_ERR_UNSUPPORTED_FEATURE)
goto err;
}
/* matrix transposition */
if( RSB_ERR_NO_ERROR != (errval =
rsb_mtx_clone(&mtxAp,RSB_NUMERICAL_TYPE_SAME_TYPE,
RSB_TRANSPOSITION_T,NULL,mtxAp,RSB_FLAG_IDENTICAL_FLAGS)))
{
goto err;
}
okay so the setup is adorable π
: blocking factors brA and bcA are both RSB_DEFAULT_BLOCKING, nnzA = 4, nrA = 3, ncA = 3, and the coordinate arrays are IA[] = { 0, 1, 2, 0 }, JA[] = { 0, 1, 2, 2 }, VA[] = { 11, 22, 33, 13 } β with XV[] = { 0,0,0,0,0,0 } and vl = 0 sitting there waiting, plus typecode = RSB_NUMERICAL_TYPE_DEFAULT and errval = RSB_ERR_NO_ERROR.
Then: rsb_lib_init(RSB_NULL_INIT_OPTIONS) (returns EXIT_FAILURE if it's not RSB_ERR_NO_ERROR), rsb_mtx_alloc_from_coo_const(VA,IA,JA,nnzA,typecode,nrA,ncA,brA,bcA,RSB_FLAG_NOFLAGS,NULL) for the allocation (and if mtxAp is null, EXIT_FAILURE again), a printout via rsb_file_mtx_save(mtxAp,NULL) where a RSB_ERR_UNSUPPORTED_FEATURE is forgiven and anything else jumps to err, and then the glow-up: transposition through rsb_mtx_clone(&mtxAp,RSB_NUMERICAL_TYPE_SAME_TYPE,RSB_TRANSPOSITION_T,NULL,mtxAp,RSB_FLAG_IDENTICAL_FLAGS).
and that's exactly where this chunk of the page cuts off bestie β the code keeps going, don't worry πΈ
β¦and here's the rest of that absolute moment of a source listing, bestie β pick up right where we left off, no recap needed π
/* printout */
if(RSB_ERR_NO_ERROR!=(errval = rsb_file_mtx_save(mtxAp,NULL)))
{
if(errval != RSB_ERR_UNSUPPORTED_FEATURE)
goto err;
}
rsb_mtx_free(mtxAp);
/* doing the same after load from file */
mtxAp = rsb_file_mtx_load("pd.mtx",
RSB_FLAG_NOFLAGS,typecode,NULL);
if(!mtxAp)
{
return EXIT_FAILURE;
}
/* printout */
if(RSB_ERR_NO_ERROR!=(errval = rsb_file_mtx_save(mtxAp,NULL)))
{
if(errval != RSB_ERR_UNSUPPORTED_FEATURE)
goto err;
}
/* one can see dimensions in advance, also */
if(RSB_ERR_NO_ERROR!=(errval =
rsb_file_mtx_get_dims("pd.mtx",&nrA,&ncA,&nnzA,NULL)))
{
if(errval != RSB_ERR_UNSUPPORTED_FEATURE)
goto err;
}
/* A matrix can be rendered to Postscript. */
{
if(RSB_ERR_NO_ERROR!=(errval =
rsb_mtx_rndr("pd.eps",mtxAp,512,512,RSB_MARF_EPS_B)))
goto err;
}
rsb_mtx_free(mtxAp);
/* also vectors can be loaded */
if(RSB_ERR_NO_ERROR!=(errval =
rsb_file_vec_load("vf.mtx",typecode,NULL,&vl )))
goto err;
/* we expect vf.mtx to be 6 rows long */
if( vl != 6 )
{
goto err;
}
if(RSB_ERR_NO_ERROR!=(errval =
rsb_file_vec_load("vf.mtx",typecode,XV, NULL )))
goto err;
/* matrices can be rendered from file to a pixelmap as well */
{
unsigned char pixmap[3*2*2];
if(RSB_ERR_NO_ERROR!=(errval =
rsb_file_mtx_rndr(pixmap,"pd.mtx",2,2,2,RSB_MARF_RGB)))
goto err;
}
if(RSB_ERR_NO_ERROR != rsb_lib_exit(RSB_NULL_EXIT_OPTIONS))
{
goto err;
}
return EXIT_SUCCESS;
err:
rsb_perror(NULL,errval);
return EXIT_FAILURE;
}
okay so like, saving the matrix, loading it from the file, peeking at the dims before you even touch it β and RSB_ERR_UNSUPPORTED_FEATURE gets a pass because sometimes the backend just isn't that girl and can't do the fancy export, and that's fine, we don't goto err over it. β¨ Then rsb_mtx_rndr("pd.eps",mtxAp,512,512,RSB_MARF_EPS_B) serves you the whole matrix as a 512Γ512 Postscript glow-up (.eps, iconic), and the pixelmap variant renders straight into an unsigned char pixmap[3*2*2] as RSB_MARF_RGB because we contain multitudes. Vectors load too β rsb_file_vec_load("vf.mtx",typecode,NULL,&vl ) first to learn it's 6 rows, then again into XV. Then rsb_lib_exit(RSB_NULL_EXIT_OPTIONS) to close the tab, and literally any hiccup along the way hits err: β rsb_perror(NULL,errval) and bails with EXIT_FAILURE. no cap. π«Ά
examples/power.c β the main character file, fr fr π
/*
Copyright (C) 2008-2021 Michele Martone
This file is part of librsb.
librsb is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
librsb is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
License for more details.
You should have received a copy of the GNU Lesser General Public
License along with librsb; see the file COPYING.
If not, see <https://www.gnu.org/licenses/>.
*/
/*!
@file
@author Michele Martone
@brief A toy <rsb.h>-based C program implementing the power
method for computing matrix eigenvalues. Uses #rsb_spmv().
\ingroup rsb-examples
\include power.c
*/
#include <stdio.h> // printf
#include <math.h> // sqrt
#include <stdlib.h> // calloc
#include <rsb.h>
int main(const int argc, char * const argv[])
{
const int WANT_VERBOSE = 0;
struct rsb_mtx_t *mtxAp = NULL;
const int bs = RSB_DEFAULT_BLOCKING;
const int br = bs, bc = bs; /* bs x bs blocked */
const rsb_nnz_idx_t nnzA = 4;
const rsb_coo_idx_t nrA = 3;
const rsb_coo_idx_t ncA = 3;
rsb_int_t it = 0;
const rsb_int_t maxit = 100;
const rsb_coo_idx_t IA[] = { 0, 1, 2, 0 };
const rsb_coo_idx_t JA[] = { 0, 1, 2, 2 };
const RSB_DEFAULT_POSSIBLY_FIRST_BLAS_TYPE VA[] = { 11, 22, 33, 13 };
const RSB_DEFAULT_POSSIBLY_FIRST_BLAS_TYPE ZERO = 0;
int i;
rsb_err_t errval = 0;
RSB_DEFAULT_POSSIBLY_FIRST_BLAS_TYPE norm = 0.0, /* nu */
oldnorm = 1.0, /* oldnorm */
*b1 = NULL, *b2 = NULL,
*bnow = NULL, *bnext = NULL;/* b1 and b2 aliases */
rsb_type_t typecode = RSB_NUMERICAL_TYPE_FIRST_BLAS;
size_t ds = 0;
/* tolerance */
const RSB_DEFAULT_POSSIBLY_FIRST_BLAS_TYPE tol = 1e-14;
/* library initialization */
if(rsb_lib_init(RSB_NULL_INIT_OPTIONS)!=RSB_ERR_NO_ERROR)
return EXIT_FAILURE;
/* allocation */
mtxAp = rsb_mtx_alloc_from_coo_const(VA,IA,JA,nnzA,
typecode,nrA,ncA,br,bc,RSB_FLAG_NOFLAGS,NULL);
if(!mtxAp)
return EXIT_FAILURE;
ds = (nrA)*sizeof(RSB_DEFAULT_POSSIBLY_FIRST_BLAS_TYPE);
b1 = calloc(1,ds);
b2 = calloc(1,ds);
if(! (b1 && b2))
{
errval = RSB_ERR_ENOMEM;
goto err;
}
for( i = 0; i < nrA; ++i )
b1[i] = 1;
bnow = b1, bnext = b2;/* b,b' */
while( fabs(norm-oldnorm) > tol && it<maxit )
{
++ it;
oldnorm = norm;
/* b'<-Ab */
if(( rsb_spmv(RSB_TRANSPOSITION_N,NULL,mtxAp,bnow,
1,&ZERO,bnext,1)) != RSB_ERR_NO_ERROR )
goto err;
/* nu<-||Ab||^2 */
norm = 0;
for(i=0;i<nrA;++i)
norm += bnext[i]*bnext[i];
/* nu<-||Ab|| */
norm = sqrt(norm);
norm = 1.0/norm;
/* b'<- Ab / ||Ab|| */
for(i=0;i<nrA;++i)
bnext[i] *= norm;
norm = 1.0/norm;
printf("it:%d norm:%lg norm diff:%lg\n",it,norm,norm-oldnorm);
{void *tmp=bnow;bnow=bnext;bnext=tmp;/* pointers swap */}
if(WANT_VERBOSE)
{
printf("norm:%lg\n",norm);
if(isinf(norm))
/* isinf is a C99 feature (need correct
* compilation flags) */
goto err;
for(i=0;i<2;++i)
printf("x[%d]=%lg\n",i,((double*)bnext)[i]);
}
}
/* the biggest eigenvalue should be in bnow */
rsb_mtx_free(mtxAp);
free(b1);
free(b2);
if(rsb_lib_exit(RSB_NULL_EXIT_OPTIONS)!=RSB_ERR_NO_ERROR)
goto err;
if( it == maxit )
{
printf("ERROR: hit iterations limit without convergence!");
errval=RSB_ERR_GENERIC_ERROR;
}
return EXIT_SUCCESS;
err:
rsb_perror(NULL,errval);
return EXIT_FAILURE;
}
okay so this is a toy <rsb.h>-based C program that does the power method for computing matrix eigenvalues, and it flexes #rsb_spmv() the whole way through. π It builds a 3Γ3, 4-nonzero matrix from COO arrays (IA, JA, VA = { 11, 22, 33, 13 }), blocks it bsΓbs where bs = RSB_DEFAULT_BLOCKING, and here's the thing β the arrays are const and the matrix is allocated with the _const flavor of rsb_mtx_alloc_from_coo_const(VA,IA,JA,nnzA,...), so librsb keeps pointers to your arrays instead of copying. Do not let those arrays dip, iykyk. π
Two calloc'd vectors (b1, b2, nrA worth of RSB_DEFAULT_POSSIBLY_FIRST_BLAS_TYPE each, aliased as bnow/bnext and swapped with a void *tmp dance every round π¦), a starting guess of all ones, and a loop that keeps rolling while fabs(norm-oldnorm) > tol (where tol = 1e-14) and it<maxit (maxit = 100). Each pass: b' <- Ab via rsb_spmv(RSB_TRANSPOSITION_N,NULL,mtxAp,bnow,1,&ZERO,bnext,1), then nu <- ||Ab||^2, then nu <- ||Ab||, then normalize, then printf("it:%d norm:%lg norm diff:%lg\n",it,norm,norm-oldnorm). π
If WANT_VERBOSE (it's 0, so chill) it prints norm:%lg\n and x[%d]=%lg\n and calls isinf(norm) β noted in the code that isinf is a C99 feature, so you need correct compilation flags for it, no cap. When the loop ends, the biggest eigenvalue should be in bnow. Then it's cleanup szn: rsb_mtx_free(mtxAp), free(b1), free(b2), rsb_lib_exit(RSB_NULL_EXIT_OPTIONS). And if you blasted all the way to it == maxit? That means you hit the iterations limit without convergence β it screams "ERROR: hit iterations limit without convergence!", sets errval=RSB_ERR_GENERIC_ERROR, and you know the rest: err: β rsb_perror(NULL,errval) β EXIT_FAILURE. periodt. π
okay and this next one is giving Fortran bestie energy, and we respect it π
!
! Copyright (C) 2008-2022 Michele Martone
!
! This file is part of librsb.
!
! librsb is free software; you can redistribute it and/or modify it
! under the terms of the GNU Lesser General Public License as published
! by the Free Software Foundation; either version 3 of the License, or
! (at your option) any later version.
!
! librsb is distributed in the hope that it will be useful, but WITHOUT
! ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
! FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
! License for more details.
!
! You should have received a copy of the GNU Lesser General Public
! License along with librsb; see the file COPYING.
! If not, see <https://www.gnu.org/licenses/>.
!
!> @file
!! @brief Sparse BLAS-based usage:
!! uscr_begin(), usgp(), ussp(), usmv(), ...
!! \include fortran.F90
β¦and we're right back in it, bestie β same Fortran file, zero skips, all the moves π β¨ here's the rest of the example, and yeah it's giving "trust me, this is what a real sparse BLAS demo looks like":
SUBROUTINE blas_sparse_mod_example(res)
USE blas_sparse
USE rsb ! For the second part of the example and RSB_IDX_KIND
USE iso_c_binding
IMPLICIT NONE
INTEGER :: i, j
INTEGER(KIND=RSB_BLAS_IDX_KIND) :: istat = 0, res
TYPE(C_PTR),TARGET :: mtxAp = c_null_ptr ! matrix pointer
INTEGER(C_INT) :: A
INTEGER,PARAMETER :: transn = blas_no_trans
INTEGER(KIND=RSB_IDX_KIND),PARAMETER :: incx = 1
INTEGER(KIND=RSB_IDX_KIND),PARAMETER :: incy = 1
REAL(KIND=8),parameter :: alpha = 3
! Symmetric (declared via lower triangle) matrix based example, e.g.:
! 1 0
! 1 1
! declaration of VA,IA,JA
!INTEGER,PARAMETER :: nr = 100
INTEGER(KIND=RSB_IDX_KIND),PARAMETER :: nr = 20
INTEGER(KIND=RSB_IDX_KIND),PARAMETER :: nc = nr
INTEGER(KIND=RSB_IDX_KIND),PARAMETER :: nnz = (nr*(nr+1))/2 ! half the square
INTEGER(KIND=RSB_IDX_KIND),PARAMETER :: nrhs = 1
INTEGER(KIND=RSB_IDX_KIND) :: nt = 0
INTEGER :: ic, ir
! For compatibility with (or compassion for) flang-13, we turn these three lines off:
! REAL(KIND=8),PARAMETER :: VA(nnz) = (/ ((1, ic=1,ir), ir=1,nr ) /) ! (/1, 1, 1/)
! INTEGER(KIND=RSB_IDX_KIND),PARAMETER :: IA(nnz) = (/ (((ir), ic=1,ir), ir=1,nr ) /) ! (/1, 2, 2/)
! INTEGER(KIND=RSB_IDX_KIND),PARAMETER :: JA(nnz) = (/ (((ic), ic=1,ir), ir=1,nr ) /) ! (/1, 1, 2/)
! and use these other ones instead, initializing VA,IA,JA later:
REAL(KIND=8) :: va(nnz)
INTEGER(KIND=RSB_IDX_KIND) :: IA(nnz)
INTEGER(KIND=RSB_IDX_KIND) :: JA(nnz)
REAL(KIND=8) :: x(nc,nrhs) = reshape((/((1), ic=1,nc*nrhs)/),[nc,nrhs]) ! reference x ! (/1, 1/)
REAL(KIND=8),parameter :: cy(nr,nrhs) = reshape((/((alpha+alpha*nr), ir=1,nr*nrhs)/),[nr,nrhs]) ! reference cy after ! (/9, 9/)
REAL(KIND=8) :: y(nr,nrhs) = reshape((/((alpha), ir=1,nr*nrhs)/),[nr,nrhs]) ! y will be overwritten ! (/3, 3/)
va(:) = (/ ((1, ic=1,ir), ir=1,nr ) /) ! (/1, 1, 1/)
ia(:) = (/ (((ir), ic=1,ir), ir=1,nr ) /) ! (/1, 2, 2/)
ja(:) = (/ (((ic), ic=1,ir), ir=1,nr ) /) ! (/1, 1, 2/)
! First example part: pure blas_sparse code.
res = 0
CALL duscr_begin(nr,nc,a,res)
IF (res.NE.0) GOTO 9999
CALL ussp(a,blas_lower_symmetric,istat)
IF (istat.NE.0) GOTO 9997
CALL ussp(a,blas_rsb_spmv_autotuning_on,istat) ! (experimental) turns auto-tuning + thread setting on
IF (istat.NE.0) print *,"autotuning returned nonzero:", istat &
&," ...did you enable autotuning ?"
!
! First style example
CALL uscr_insert_entries(a,nnz,va,ia,ja,istat)
IF (istat.NE.0) GOTO 9997
CALL uscr_end(a,istat)
IF (istat.NE.0) GOTO 9997
! CALL ussp(A,blas_rsb_duplicates_sum,istat)
! CALL uscr_insert_entries(A,nnz,VA,IA,JA,istat) ! uncomment this to activate add of coefficients to pattern
CALL usgp(a,blas_rsb_spmv_autotuning_on,nt) ! (experimental)
IF (nt.NE.0) print*,"autotuner chose ",nt," threads"
CALL ussp(a,blas_rsb_spmv_autotuning_off,istat) ! (experimental) turns auto-tuning + thread setting off
IF (istat.NE.0) GOTO 9997
DO j = 1, nrhs
CALL usmv(transn,alpha,a,x(:,j),incx,y(:,j),incy,istat)
END DO
IF (istat.NE.0) GOTO 9997
!
DO j = 1, nrhs
DO i = 1, nr
IF (y(i,j).NE.cy(i,j)) print *, "first check results are not ok"
IF (y(i,j).NE.cy(i,j)) GOTO 9997
END DO
END DO
!
y(:,:) = alpha ! reset
!
! Second style example
CALL ussp(a,blas_rsb_autotune_next_operation,istat) ! (experimental) turns auto-tuning + thread setting on
IF (istat.NE.0) GOTO 9997
DO j = 1, nrhs
CALL usmv(transn,alpha,a,x(:,j),incx,y(:,j),incy,istat)
END DO
CALL usmm(blas_colmajor,transn,nrhs, alpha,a,x,nr,y,nc,istat) ! Equivalent to the above (as long as incx=incy=1).
CALL usmm(blas_colmajor,transn,nrhs,-alpha,a,x,nr,y,nc,istat) ! Subtract the last usmm call contribution.
IF (istat.NE.0) GOTO 9997
!
DO j = 1, nrhs
DO i = 1, nr
IF (y(i,j).NE.cy(i,j)) print *,"second check results are not ok"
IF (y(i,j).NE.cy(i,j)) GOTO 9997
END DO
END DO
!
print *, "check results are ok"
! Second part of the example: access to the rsb.h interface via
! the ISO C Binding interface.
mtxap = rsb_blas_get_mtx(a) ! get pointer to rsb structure (as in the rsb.h API)
IF(nr.LT.5) istat = rsb_file_mtx_save(mtxap,c_null_char) ! write to stdout (only if matrix small enough)
GOTO 9998
9997 res = -1
9998 CONTINUE
CALL usds(a,istat)
IF (istat.NE.0) res = -1
9999 CONTINUE
end SUBROUTINE blas_sparse_mod_example
! Example loading a matrix from file and measuring SPMM.
SUBROUTINE blas_sparse_io_example(res)
USE blas_sparse
USE rsb ! For rsb_blas_file_mtx_load
USE iso_c_binding
IMPLICIT NONE
INTEGER(KIND=RSB_IDX_KIND) :: res ! note: same as RSB_BLAS_IDX_KIND
INTEGER :: j
INTEGER(KIND=RSB_BLAS_IDX_KIND) :: istat = 0
INTEGER :: A
INTEGER,PARAMETER :: transn = blas_no_trans
INTEGER(KIND=RSB_IDX_KIND),PARAMETER :: incx = 1
INTEGER(KIND=RSB_IDX_KIND),PARAMETER :: incy = 1
COMPLEX(KIND=8),PARAMETER :: alpha = 3
INTEGER(KIND=RSB_IDX_KIND) :: nr
INTEGER(KIND=RSB_IDX_KIND) :: nc
INTEGER(KIND=RSB_IDX_KIND) :: nz
INTEGER(KIND=RSB_IDX_KIND) :: st
INTEGER(KIND=RSB_IDX_KIND),PARAMETER :: nrhs = 4
COMPLEX(KIND=8), ALLOCATABLE, TARGET, DIMENSION(:,:) :: x
COMPLEX(KIND=8), ALLOCATABLE, TARGET, DIMENSION(:,:) :: y
CHARACTER(KIND=C_CHAR,LEN=7),TARGET :: filename = 'pd.mtx'//c_null_char
INTEGER(C_SIGNED_CHAR) :: typecode = rsb_numerical_type_double_complex
REAL(KIND=c_double) :: mvt,mmt,omt
INTEGER(KIND=C_INT),TARGET::IZERO=0
res = 0
a = rsb_blas_file_mtx_load(filename, typecode);
IF (a.EQ.blas_invalid_handle) GOTO 9997
CALL usgp(a,blas_num_rows,nr)
CALL usgp(a,blas_num_cols,nc)
CALL usgp(a,blas_num_nonzeros,nz)
print*,"Read matrix ",filename(1:6)," ",nr,"x",nc,":",nz
CALL usgp(a,blas_general,st)
IF (st .EQ. 1) print*,"Matrix has no symmetry"
CALL usgp(a,blas_upper_symmetric,st)
IF (st .EQ. 1) print*,"Matrix is upper symmetric"
CALL usgp(a,blas_upper_hermitian,st)
IF (st .EQ. 1) print*,"Matrix is upper hermitian"
! ...
IF (istat.NE.0) GOTO 9997
WRITE(*,'(a,i0)') "Using NRHS=",nrhs
ALLOCATE( x(nc,nrhs))
ALLOCATE( y(nr,nrhs))
x = 1.0
y = 0.0
mvt = -rsb_time()
DO j = 1, nrhs
CALL usmv(transn,alpha,a,x(:,j),incx,y(:,j),incy,istat)
END DO
IF (istat.NE.0) GOTO 9997
mvt = mvt + rsb_time()
WRITE(*,'(a,e12.4,a)') "Repeated USMV took ",mvt," s"
y = 0.0
mmt = -rsb_time()
CALL usmm(blas_colmajor,transn,nrhs, alpha,a,x,nr,y,nc,istat)
IF (istat.NE.0) GOTO 9997
mmt = mmt + rsb_time()
WRITE(*,'(a,e12.4,a)') "A single USMM took ",mmt," s"
WRITE(*,'(a,g11.4,a)')"USMM-to-USMV speed ratio is is ", mvt/mmt, "x"
print*,"Call auto-tuning routine.."
! Change IZERO value to 1 to get verbose tuning again.
res = rsb_lib_set_opt(rsb_io_want_verbose_tuning,c_loc(izero))
IF (res.NE.0) GOTO 9997
CALL ussp(a,blas_rsb_autotune_next_operation,istat) ! (experimental) turns auto-tuning + thread setting on
IF (istat.NE.0) GOTO 9997
CALL usmm(blas_colmajor,transn,nrhs, alpha,a,x,nr,y,nc,istat)
IF (istat.NE.0) GOTO 9997
print*,"Repeat measurement."
y = 0.0
omt = -rsb_time()
CALL usmm(blas_colmajor,transn,nrhs, alpha,a,x,nr,y,nc,istat)
IF (istat.NE.0) GOTO 9997
omt = omt + rsb_time()
WRITE(*,'(a,e12.4,a)') "Tuned USMM took ",omt," s"
WRITE(*,'(a,g11.4,a)')"Tuned-to-untuned speed ratio is is ",mmt/omt,"x"
GOTO 9998
9997 res = -1
9998 CONTINUE
CALL usds(a,istat)
IF (istat.NE.0) res = -1
end SUBROUTINE blas_sparse_io_example
PROGRAM main
USE rsb, ONLY: rsb_lib_init, rsb_lib_exit, c_ptr, c_null_ptr,&
& rsb_io_want_extra_verbose_interface,rsb_io_want_verbose_tuning,&
& rsb_lib_set_opt,rsb_idx_kind
! USE blas_sparse, only: RSB_BLAS_IDX_KIND ! only if using long indices
USE iso_c_binding
IMPLICIT NONE
INTEGER :: passed = 0, failed = 0
INTEGER(KIND=RSB_IDX_KIND) :: res ! note: same as RSB_BLAS_IDX_KIND
!TYPE(C_PTR),PARAMETER :: EO = RSB_NULL_EXIT_OPTIONS
!TYPE(C_PTR),PARAMETER :: IO = RSB_NULL_INIT_OPTIONS
! Note: using C_NULL_PTR instead of the previous lines because of http://gcc.gnu.org/bugzilla/show_bug.cgi?id=59411
TYPE(C_PTR),PARAMETER :: EO = c_null_ptr
TYPE(C_PTR),PARAMETER :: IO = c_null_ptr
INTEGER(KIND=C_INT),TARGET::IONE=1
res = rsb_lib_init(io)
res = rsb_lib_set_opt(rsb_io_want_verbose_tuning,c_loc(ione))
CALL blas_sparse_mod_example(res)
IF (res.LT.0) failed = failed + 1
IF (res.EQ.0) passed = passed + 1
CALL blas_sparse_io_example(res)
IF (res.LT.0) failed = failed + 1
IF (res.EQ.0) passed = passed + 1
res = rsb_lib_exit(eo)
print *, "FAILED:", failed
print *, "PASSED:", passed
IF (failed .GT. 0) THEN
stop 1
END IF
END PROGRAM
the way that PROGRAM main is just sitting at the bottom counting passed and failed like a lil scoreboard?? main character energy fr. and yes bestie, it literally has a whole comment about avoiding RSB_NULL_EXIT_OPTIONS / RSB_NULL_INIT_OPTIONS because of a gcc bug β http://gcc.gnu.org/bugzilla/show_bug.cgi?id=59411 β we love a documented workaround ππ
and it literally calls stop 1 if failed .GT. 0, so no silent L's around here, periodt.
next file up, and bestie this one opens with the legal-eagle era of the page, ngl β examples/fortran_rsb_fi.F90:
!
! Copyright (C) 2008-2022 Michele Martone
!
! This file is part of librsb.
!
! librsb is free software; you can redistribute it and/or modify it
! under the terms of the GNU Lesser General Public License as published
! by the Free Software Foundation; either version 3 of the License, or
! (at your option) any later version.
!
! librsb is distributed in the hope that it will be useful, but WITHOUT
! ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
! FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
! License for more details.
!
! You should have received a copy of the GNU Lesser General Public
! License along with librsb; see the file COPYING.
! If not, see <https://www.gnu.org/licenses/>.
!
and there's more of this file coming, so don't touch that dial ππ¦
Like, REAL(KIND=8) this, pointer that β bestie it's giving real code now. This is the file that gets pulled in by that \include line: fortran_rsb_fi.F90. And it starts with the lil doxygen intro of itself, iconic behaviour honestly:
π¦ The included file: fortran_rsb_fi.F90
!> @file.
!! @brief RSB.F90-based usage:
!! rsb_mtx_alloc_from_coo_const(),
!! rsb_tune_spmm(),
!! rsb_file_mtx_save(),
!! rsb_spmv(),
!! ...
!! \include fortran_rsb_fi.F90
So it's telling on itself: this file shows you how to use rsb_mtx_alloc_from_coo_const(), rsb_tune_spmm(), rsb_file_mtx_save(), rsb_spmv(), and then "..." because the list literally trails off. Periodt, it contains multitudes.
β¨ rsb_mod_example1 β COO matrix, autotuning, built ALL AT ONCE β¨
Unsymmetric matrix, specified as COO, autotuned, built in one shot. Here's every single line, no cap:
SUBROUTINE rsb_mod_example1(res)
! Example using an unsymmetric matrix specified as COO, and autotuning, built at once.
USE rsb
USE iso_c_binding
IMPLICIT NONE
INTEGER ::res
INTEGER,TARGET :: istat = 0, i
INTEGER :: transt = rsb_transposition_n !
INTEGER(KIND=RSB_IDX_KIND) :: incx = 1, incy = 1
REAL(KIND=8),target :: alpha = 3, beta = 1
! 1 1
! 1 1
! declaration of VA,IA,JA
INTEGER(KIND=RSB_IDX_KIND) :: nnz = 4
INTEGER(KIND=RSB_IDX_KIND) :: nr = 2
INTEGER(KIND=RSB_IDX_KIND) :: nc = 2
INTEGER(KIND=RSB_IDX_KIND) :: nrhs = 1
INTEGER :: order = rsb_flag_want_column_major_order ! rhs layout
INTEGER :: flags = rsb_flag_noflags
INTEGER(KIND=RSB_IDX_KIND),TARGET :: IA(4) = (/0, 1, 1,0/)
INTEGER(KIND=RSB_IDX_KIND),TARGET :: JA(4) = (/0, 0, 1,1/)
REAL(KIND=8),target :: va(4) = (/1,1,1,1/)
REAL(KIND=8),target :: x(2) = (/1, 1/)! reference x
REAL(KIND=8),target :: cy(2) = (/9, 9/)! reference cy after
REAL(KIND=8),target :: y(2) = (/3, 3/)! y will be overwritten
TYPE(C_PTR),TARGET :: mtxAp = c_null_ptr ! matrix pointer
REAL(KIND=8) :: tmax = 2.0 ! tuning max time
INTEGER :: titmax = 2 ! tuning max iterations
INTEGER,TARGET :: ont = 0 ! optimal number of threads
!TYPE(C_PTR),PARAMETER :: EO = RSB_NULL_EXIT_OPTIONS
!TYPE(C_PTR),PARAMETER :: IO = RSB_NULL_INIT_OPTIONS
! Note: using C_NULL_PTR instead of the previous lines because of http://gcc.gnu.org/bugzilla/show_bug.cgi?id=59411
TYPE(C_PTR),PARAMETER :: EO = c_null_ptr
TYPE(C_PTR),PARAMETER :: IO = c_null_ptr
INTEGER,TARGET :: errval
res = 0
errval = rsb_lib_init(io)
IF (errval.NE.rsb_err_no_error) GOTO 9997
mtxap = rsb_mtx_alloc_from_coo_const(c_loc(va),c_loc(ia),c_loc(ja)&
&,nnz,&
& rsb_numerical_type_double,nr,nc,1,1,flags,c_loc(istat))
IF (istat.NE.rsb_err_no_error) GOTO 9997
istat = rsb_file_mtx_save(mtxap,c_null_char)
! Structure autotuning:
istat = rsb_tune_spmm(c_loc(mtxap),c_null_ptr,c_null_ptr,titmax,&
& tmax,&
& transt,c_loc(alpha),c_null_ptr,nrhs,order,c_loc(x),nr,&
& c_loc(beta),c_loc(y),nc)
IF (istat.NE.rsb_err_no_error) GOTO 9997
! Thread count autotuning:
istat = rsb_tune_spmm(c_null_ptr,c_null_ptr,c_loc(ont),titmax,&
& tmax,&
& transt,c_loc(alpha),mtxap,nrhs,order,c_loc(x),nr,c_loc(beta),&
& c_loc(y),nc)
print *, "Optimal number of threads:", ont
y(:) = (/3, 3/)! restoring reference y (rsb_tune_spmm has overwritten it)
IF (istat.NE.rsb_err_no_error) GOTO 9997
istat = rsb_file_mtx_save(mtxap,c_null_char)
IF (istat.NE.rsb_err_no_error) GOTO 9997
istat = rsb_spmv(transt,c_loc(alpha),mtxap,c_loc(x),incx,&
& c_loc(beta),c_loc(y),incy)
IF (istat.NE.rsb_err_no_error) GOTO 9997
DO i = 1, 2
IF (y(i).NE.cy(i)) print *, "type=d dims=2x2 sym=g diag=g &
&blocks=1x1 usmv alpha= 3 beta= 1 incx=1 incy=1 trans=n is not ok"
IF (y(i).NE.cy(i)) GOTO 9997
END DO
print*,"type=d dims=2x2 sym=g diag=g blocks=1x1 usmv alpha= 3&
& beta= 1 incx=1 incy=1 trans=n is ok"
IF ( res .NE.rsb_err_no_error) GOTO 9997
GOTO 9998
9997 res = -1
9998 CONTINUE
mtxap = rsb_mtx_free(mtxap)
IF (istat.NE.rsb_err_no_error) res = -1
! 9999 CONTINUE
errval=rsb_lib_exit(eo) ! librsb finalization
IF (errval.NE.rsb_err_no_error)&
& stop "error calling rsb_lib_exit"
print *, "rsb module fortran test is ok"
istat = rsb_perror(c_null_ptr,istat)
end SUBROUTINE rsb_mod_example1
Ok so the tea β here: the matrix is that little ! 1 1 / ! 1 1 situation β IA(4) = (/0, 1, 1,0/), JA(4) = (/0, 0, 1,1/), va(4) = (/1,1,1,1/), with nnz = 4, nr = 2, nc = 2, nrhs = 1, flags = rsb_flag_noflags and order = rsb_flag_want_column_major_order because that's the rhs layout, bestie, respect the layout.
The way it literally inits with rsb_lib_init(io), makes the matrix with rsb_mtx_alloc_from_coo_const(...), saves it, then does structure autotuning with rsb_tune_spmm() (passing c_loc(mtxap), titmax, tmax), then does thread count autotuning with a second rsb_tune_spmm() (passing c_null_ptr for the first arg and c_loc(ont) for the threads out-param)β¦ and yes it prints "Optimal number of threads:", because we love a little reveal β¨.
Also deadline: rsb_tune_spmm has overwritten y, so the code does y(:) = (/3, 3/) to restore the reference. Not a bug, just the cost of autotuning being messy.
Lowkey important if you're copying this:EOandIOarec_null_ptrand notRSB_NULL_EXIT_OPTIONS/RSB_NULL_INIT_OPTIONS, and the code comments the reason β that GCC bughttp://gcc.gnu.org/bugzilla/show_bug.cgi?id=59411. The commented-out lines are still right there. I did not touch them. π
And the check is y(i).NE.cy(i) against cy(2) = (/9, 9/), with prints for both the "is not ok" and "is ok" outcomes, then GOTO 9997 / 9998 labels, rsb_mtx_free(mtxap), rsb_lib_exit(eo) with a stop "error calling rsb_lib_exit", print *, "rsb module fortran test is ok", and last but never least rsb_perror(c_null_ptr,istat). Chef's kiss π
πΈ rsb_mod_example2 β COO, autotuning, built PIECEWISE πΈ
Same vibe, different construction method: rsb_mtx_alloc_from_coo_begin() β rsb_mtx_set_vals() β rsb_mtx_alloc_from_coo_end(). Here it is, all of it:
SUBROUTINE rsb_mod_example2(res)
! Example using an unsymmetric matrix specified as COO, and autotuning, built piecewise.
USE rsb
USE iso_c_binding
IMPLICIT NONE
INTEGER,TARGET :: errval
INTEGER :: res
INTEGER :: transt = rsb_transposition_n ! no transposition
INTEGER(KIND=RSB_IDX_KIND) :: incX = 1, incb = 1 ! X, B vectors increment
REAL(KIND=8),target :: alpha = 3,beta = 1
INTEGER(KIND=RSB_IDX_KIND) :: nnzA = 4, nra = 3, nca = 3 ! nonzeroes, rows, columns of matrix A
INTEGER(KIND=RSB_IDX_KIND),TARGET :: IA(4) = (/1, 2, 3, 3/) ! row indices
INTEGER(KIND=RSB_IDX_KIND),TARGET :: JA(4) = (/1, 2, 1, 3/) ! column indices
INTEGER(C_SIGNED_CHAR) :: typecode = rsb_numerical_type_double
INTEGER :: flags =rsb_flag_default_matrix_flags+rsb_flag_symmetric
REAL(KIND=8),target :: va(4) = (/11.0, 22.0, 13.0, 33.0/) ! coefficients
REAL(KIND=8),target :: x(3) = (/ 0, 0, 0/)
REAL(KIND=8),target :: b(3) = (/-1.0, -2.0, -2.0/)
TYPE(C_PTR),TARGET :: mtxAp = c_null_ptr
TYPE(C_PTR) :: mtxApp = c_null_ptr
REAL(KIND=8),target :: etime = 0.0
!TYPE(C_PTR),PARAMETER :: EO = RSB_NULL_EXIT_OPTIONS
!TYPE(C_PTR),PARAMETER :: IO = RSB_NULL_INIT_OPTIONS
! Note: using C_NULL_PTR instead of the previous lines because of http://gcc.gnu.org/bugzilla/show_bug.cgi?id=59411
TYPE(C_PTR),PARAMETER :: EO = c_null_ptr
TYPE(C_PTR),PARAMETER :: IO = c_null_ptr
errval = rsb_lib_init(io) ! librsb initialization
IF (errval.NE.rsb_err_no_error) &
& stop "error calling rsb_lib_init"
#if defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ < 5)
#define RSB_SKIP_BECAUSE_OLD_COMPILER 1
#endif
#ifndef RSB_SKIP_BECAUSE_OLD_COMPILER
mtxap = rsb_mtx_alloc_from_coo_begin(nnza,typecode,nra,nca,flags,&
& c_loc(errval)) ! begin matrix creation
errval = rsb_mtx_set_vals(mtxap,&
& c_loc(va),c_loc(ia),c_loc(ja),nnza,flags) ! insert some nonzeroes
mtxapp = c_loc(mtxap) ! Old compilers like e.g.: Gfortran 4.4.7 will NOT compile this.
IF (errval.NE.rsb_err_no_error) &
& stop "error calling rsb_mtx_set_vals"
errval = rsb_mtx_alloc_from_coo_end(mtxapp) ! end matrix creation
IF (errval.NE.rsb_err_no_error) &
& stop "error calling rsb_mtx_alloc_from_coo_end"
errval = rsb_spmv(transt,c_loc(alpha),mtxap,c_loc(x),&
& incx,c_loc(beta),c_loc(b),incb) ! X := X + (3) * A * B
IF (errval.NE.rsb_err_no_error)&
& stop "error calling rsb_spmv"
mtxap = rsb_mtx_free(mtxap) ! destroy matrix
! The following is optional and depends on configure options, so it is allowed to fail
errval = rsb_lib_get_opt(rsb_io_want_librsb_etime,c_loc(etime))
IF (errval.EQ.rsb_err_no_error)&
& print*,"Time spent in librsb is:",etime
! IF (errval.NE.0)STOP "error calling rsb_lib_get_opt"
errval = rsb_err_no_error
IF (errval.NE.rsb_err_no_error) &
& stop "error calling rsb_mtx_free"
#else
print*,"You have an old Fortran compiler not supporting C_LOC."
print*,"Skipping a part of the test"
#endif
errval=rsb_lib_exit(eo) ! librsb finalization
IF (errval.NE.rsb_err_no_error)&
& stop "error calling rsb_lib_exit"
print *, "rsb module fortran test is ok"
res = errval
end SUBROUTINE rsb_mod_example2
The deets: nnzA = 4, nra = 3, nca = 3, IA(4) = (/1, 2, 3, 3/) are the row indices, JA(4) = (/1, 2, 1, 3/) are the column indices, typecode = rsb_numerical_type_double, and flags = rsb_flag_default_matrix_flags+rsb_flag_symmetric. The coefficients are va(4) = (/11.0, 22.0, 13.0, 33.0/), with x(3) all zeroes and b(3) = (/-1.0, -2.0, -2.0/). Note the capital letters: incX and incb here, bestie, they didn't have to be like the other example and they simply weren't. The spmv comment literally says ! X := X + (3) * A * B.
The compiler-clownery section, fr fr: ifdefined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ < 5)then it#define RSB_SKIP_BECAUSE_OLD_COMPILER 1, and under#ifndef RSB_SKIP_BECAUSE_OLD_COMPILERit runs the whole matrix build. The linemtxapp = c_loc(mtxap)carries the warning "Old compilers like e.g.: Gfortran 4.4.7 will NOT compile this." In the#elsebranch you getprint*,"You have an old Fortran compiler not supporting C_LOC."andprint*,"Skipping a part of the test". So no crashing at you, just gentle exclusion π«Ά
And the etime bit is explicitly optional: rsb_lib_get_opt(rsb_io_want_librsb_etime,c_loc(etime)) is ! The following is optional and depends on configure options, so it is allowed to fail, and even the "stop on error" line for it is commented out (! IF (errval.NE.0)STOP "error calling rsb_lib_get_opt") with errval = rsb_err_no_error right after. It prints "Time spent in librsb is:" only when errval.EQ.rsb_err_no_error. And res = errval at the end. Slay.
π rsb_mod_example3 β symmetric CSR, autotuning, built at once π
SUBROUTINE rsb_mod_example3(res)
! Example using a symmetric matrix specified as CSR, and autotuning, built at once.
USE rsb
USE iso_c_binding
IMPLICIT NONE
INTEGER ::res
INTEGER,TARGET :: istat = 0, i
INTEGER :: transt = rsb_transposition_n !
INTEGER(KIND=RSB_IDX_KIND) :: incx = 1, incy = 1
REAL(KIND=8),target :: alpha = 4, beta = 1
! 11 21
! 21 22
! declaration of VA,IP,JA
INTEGER(KIND=RSB_IDX_KIND), PARAMETER :: nnz = 3
INTEGER(KIND=RSB_IDX_KIND), PARAMETER :: nr = 2
INTEGER(KIND=RSB_IDX_KIND), PARAMETER :: nc = 2
INTEGER(KIND=RSB_IDX_KIND), PARAMETER :: nrhs = 1
INTEGER :: order = rsb_flag_want_column_major_order ! rhs layout
INTEGER :: flags = rsb_flag_noflags + rsb_flag_symmetric + &
& rsb_flag_fortran_indices_interface ! optional flags
INTEGER(KIND=RSB_IDX_KIND),TARGET :: IP(3) = (/1, 2, 4/)
INTEGER(KIND=RSB_IDX_KIND),TARGET :: JA(3) = (/1, 1, 2/)
REAL(KIND=8),target :: va(3) = (/11,21,22/) ! lower triangle coefficients
REAL(KIND=8),target :: x(2) = (/1, 2/)! reference x
REAL(KIND=8),target :: cy(2) = (/215.0, 264.0/)! reference cy after
REAL(KIND=8),target :: y(2) = (/3, 4/)! y will be overwritten
TYPE(C_PTR),TARGET :: mtxAp = c_null_ptr ! matrix pointer
REAL(KIND=8) :: tmax = 2.0 ! tuning max time
INTEGER :: titmax = 2 ! tuning max iterations
INTEGER,TARGET :: ont = 0 ! optimal number of threads
TYPE(C_PTR),PARAMETER :: EO = c_null_ptr
TYPE(C_PTR),PARAMETER :: IO = c_null_ptr
INTEGER,TARGET :: errval
errval = rsb_lib_init(io) ! librsb initialization
IF (errval.NE.rsb_err_no_error) &
& stop "error calling rsb_lib_init"
res = 0
mtxap = rsb_mtx_alloc_from_csr_const(c_loc(va),c_loc(ip),c_loc(ja)&
&,nnz,rsb_numerical_type_double,nr,nc,1,1,flags,c_loc(istat))
IF (istat.NE.rsb_err_no_error) GOTO 9997
istat = rsb_file_mtx_save(mtxap,c_null_char)
! Structure autotuning:
istat = rsb_tune_spmm(c_loc(mtxap),c_null_ptr,c_null_ptr,titmax,&
& tmax,&
& transt,c_loc(alpha),c_null_ptr,nrhs,order,c_loc(x),nr,&
& c_loc(beta),c_loc(y),nc)
IF (istat.NE.rsb_err_no_error) GOTO 9997
! Thread count autotuning:
istat = rsb_tune_spmm(c_null_ptr,c_null_ptr,c_loc(ont),titmax,&
& tmax,&
& transt,c_loc(alpha),mtxap,nrhs,order,c_loc(x),nr,c_loc(beta),&
& c_loc(y),nc)
print *, "Optimal number of threads:", ont
y(:) = (/3, 4/)! restoring reference y (rsb_tune_spmm has overwritten it)
IF (istat.NE.rsb_err_no_error) GOTO 9997
istat = rsb_file_mtx_save(mtxap,c_null_char)
IF (istat.NE.rsb_err_no_error) GOTO 9997
So this one's actually symmetric and lives as CSR, with alpha = 4, beta = 1, nnz = 3, nr = 2, nc = 2, nrhs = 1, IP(3) = (/1, 2, 4/), JA(3) = (/1, 1, 2/) and va(3) = (/11,21,22/) β the lower triangle coefficients of that ! 11 21 / ! 21 22 matrix. It's PARAMETER everywhere in this one, and the flags stack is rsb_flag_noflags + rsb_flag_symmetric + rsb_flag_fortran_indices_interface (comment says ! optional flags). rsb_mtx_alloc_from_csr_const() is the builder, the reference cy(2) = (/215.0, 264.0/), and x(2) = (/1, 2/) while y starts at (/3, 4/) and again gets y(:) = (/3, 4/) restored after autotuning ruined it π
And that's exactly where this chunk of the file cuts off, mid-routine, right after that last rsb_file_mtx_save error check β so don't panic that rsb_mod_example3 has no ending yet. It does. Just not in this part π
...so the Fortran test wraps up its spmv check and the whole PROGRAM main tallies up how many of the three examples passed, bestie. Watch it go π
istat = rsb_spmv(transt,c_loc(alpha),mtxap,c_loc(x),incx,&
& c_loc(beta),c_loc(y),incy)
print *, y
IF (istat.NE.rsb_err_no_error) GOTO 9997
DO i = 1, 2
IF (y(i).NE.cy(i)) print *, "type=d dims=2x2 sym=s diag=g &
&blocks=1x1 usmv alpha= 4 beta= 1 incx=1 incy=1 trans=n is not ok"
IF (y(i).NE.cy(i)) GOTO 9997
END DO
print*,"type=d dims=2x2 sym=s diag=g blocks=1x1 usmv alpha= 4&
& beta= 1 incx=1 incy=1 trans=n is ok"
GOTO 9998
errval=rsb_lib_exit(eo) ! librsb finalization
IF (errval.NE.rsb_err_no_error)&
& stop "error calling rsb_lib_exit"
print *, "rsb module fortran test is ok"
9997 res = -1
9998 CONTINUE
mtxap = rsb_mtx_free(mtxap)
IF (istat.NE.rsb_err_no_error) res = -1
! 9999 CONTINUE
istat = rsb_perror(c_null_ptr,istat)
end SUBROUTINE rsb_mod_example3
PROGRAM main
USE rsb
IMPLICIT NONE
INTEGER :: res = rsb_err_no_error, passed = 0, failed = 0
CALL rsb_mod_example1(res)
IF (res.LT.0) failed = failed + 1
IF (res.EQ.0) passed = passed + 1
CALL rsb_mod_example2(res)
IF (res.LT.0) failed = failed + 1
IF (res.EQ.0) passed = passed + 1
CALL rsb_mod_example3(res)
IF (res.LT.0) failed = failed + 1
IF (res.EQ.0) passed = passed + 1
print *, "FAILED:", failed
print *, "PASSED:", passed
IF (failed.GT.0) THEN
stop 1
END IF
END PROGRAM
Okay so what's the vibe here? Every subroutine reports back through res β if it's < 0 that's a fail, if it's 0 that's a pass, and the main program just keeps score. Then it prints "FAILED:" and "PASSED:" with the tallies, and if anything failed it literally hits you with stop 1. No chill. Valid. π
Also note the ! librsb finalization comment on the rsb_lib_exit(eo) line, the ! 9999 CONTINUE line that's commented out, and that rsb_perror(c_null_ptr,istat) gets called at the very end of rsb_mod_example3. Nothing skipped, bestie, we see all of it. π¦
π examples/backsolve.c π
Copyright (C) 2008-2021 Michele Martone β and this file is part of librsb, which is free software under the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. librsb is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with librsb; see the file COPYING. If not, see <https://www.gnu.org/licenses/>.
It's a @file, @author Michele Martone, and the @brief is a C triangular solve example program. It uses #rsb_spsv(), #rsb_tune_spsm(), and it's based on <rsb.h>. Also \include hello.c β periodt. π
/*
Copyright (C) 2008-2021 Michele Martone
This file is part of librsb.
librsb is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
librsb is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
License for more details.
You should have received a copy of the GNU Lesser General Public
License along with librsb; see the file COPYING.
If not, see <https://www.gnu.org/licenses/>.
*/
/*!
\ingroup rsb-examples
@file
@author Michele Martone
@brief C triangular solve example program.
Uses #rsb_spsv(),#rsb_tune_spsm().
Based on <rsb.h>.
\include hello.c
*/
#include <rsb.h> /* librsb header to include */
#include <stdio.h> /* printf() */
int main(const int argc, char * const argv[])
{
/*!
A Hello-RSB program.
This program shows how to use the rsb.h interface correctly to:
- initialize the library using #rsb_lib_init()
- allocate (build) a single sparse matrix in the RSB format
using #rsb_mtx_alloc_from_coo_const(), with implicit diagonal
- print information obtained via #rsb_mtx_get_info_str()
- multiply the triangular matrix using #rsb_spmv()
- autotune the matrix for rsb_spsv with #rsb_tune_spsm()
- solve the triangular system using #rsb_spsv()
- deallocate the matrix using #rsb_mtx_free()
- finalize the library using #rsb_lib_exit()
In this example, we use #RSB_DEFAULT_TYPE as matrix type.
This type depends on what was configured at library build time.
* */
const int bs = RSB_DEFAULT_BLOCKING;
const int brA = bs, bcA = bs;
const RSB_DEFAULT_TYPE one = 1;
const rsb_type_t typecode = RSB_NUMERICAL_TYPE_DEFAULT;
const rsb_nnz_idx_t nnzA = 7; /* matrix nonzeroes count */
const rsb_coo_idx_t nrA = 6; /* matrix rows count */
const rsb_coo_idx_t ncA = 6; /* matrix columns count */
/* nonzero row indices coordinates: */
const rsb_coo_idx_t IA[] = {1,2,3,4,5,6,1};
/* nonzero column indices coordinates: */
const rsb_coo_idx_t JA[] = {1,2,3,4,5,6,6};
const RSB_DEFAULT_TYPE VA[] = {1,1,1,1,1,1,1};/*values of nonzeroes*/
RSB_DEFAULT_TYPE X[] = { 0,0,0,0,0,0 }; /* X vector's array */
const RSB_DEFAULT_TYPE B[] = { 1,1,1,1,1,1 }; /* B */
struct rsb_mtx_t *mtxAp = NULL; /* matrix structure pointer */
char ib[200];
int i;
rsb_err_t errval = RSB_ERR_NO_ERROR;
const rsb_int_t wvat = 1; /* want verbose autotuning; see
documentation of RSB_IO_WANT_VERBOSE_TUNING */
printf("Hello, RSB!\n");
printf("Initializing the library...\n");
if((errval = rsb_lib_init(RSB_NULL_INIT_OPTIONS)) !=
RSB_ERR_NO_ERROR)
{
printf("Error initializing the library!\n");
goto err;
}
printf("Correctly initialized the library.\n");
errval = rsb_lib_set_opt(RSB_IO_WANT_VERBOSE_TUNING, &wvat );
if( (errval) != RSB_ERR_NO_ERROR )
{
printf("Error setting option!\n");
goto err;
}
mtxAp = rsb_mtx_alloc_from_coo_const(
VA,IA,JA,nnzA,typecode,nrA,ncA,brA,bcA,
RSB_FLAG_DEFAULT_RSB_MATRIX_FLAGS /* force rsb */
| RSB_FLAG_DUPLICATES_SUM/* sum dups */
| RSB_FLAG_UNIT_DIAG_IMPLICIT/* ask diagonal implicit */
| RSB_FLAG_TRIANGULAR /* need triangle for spsv */
| RSB_FLAG_FORTRAN_INDICES_INTERFACE /* treat indices as 1-based */
, &errval);
if((!mtxAp) || (errval != RSB_ERR_NO_ERROR))
{
printf("Error while allocating the matrix!\n");
goto err;
}
printf("Correctly allocated a matrix with %ld nonzeroes.\n",
(long int)nnzA);
printf("Summary information of the matrix:\n");
/* print out the matrix summary information */
rsb_mtx_get_info_str(mtxAp,"RSB_MIF_MATRIX_INFO__TO__CHAR_P",
ib,sizeof(ib));
printf("%s",ib);
printf("\nMatrix printout:\n");
rsb_file_mtx_save(mtxAp, NULL);
if((errval =
rsb_spmv(RSB_TRANSPOSITION_N,&one,mtxAp,B,1,&one,X,1))
!= RSB_ERR_NO_ERROR )
{
printf("Error performing a multiplication!\n");
goto err;
}
printf("\nWe have a unitary vector:\n");
rsb_file_vec_save(NULL, typecode, B, nrA);
printf("\nMultiplying matrix by unitary vector we get:\n");
rsb_file_vec_save(NULL, typecode, X, nrA);
errval = rsb_tune_spsm(&mtxAp, NULL, NULL, 0, 0, RSB_TRANSPOSITION_N,
&one, NULL, 1, RSB_FLAG_WANT_COLUMN_MAJOR_ORDER, NULL, nrA,
NULL, NULL, nrA);
if( (errval) != RSB_ERR_NO_ERROR )
{
printf("Error performing autotuning!\n");
goto err;
}
if((errval = rsb_spsv(RSB_TRANSPOSITION_N,&one,mtxAp,X,1,X,1))
!= RSB_ERR_NO_ERROR )
{
printf("Error performing triangular solve!\n");
goto err;
}
printf("\nBacksolving we should get a unitary vector:\n");
rsb_file_vec_save(NULL, typecode, X, nrA);
for(i=0;i<nrA;++i)
if(X[i]!=one)
{
printf("Warning! Result vector not unitary!:\n");
errval = RSB_ERR_INTERNAL_ERROR;
goto err;
}
printf("All done.\n");
rsb_mtx_free(mtxAp);
printf("Correctly freed the matrix.\n");
if((errval = rsb_lib_exit(RSB_NULL_EXIT_OPTIONS))
!= RSB_ERR_NO_ERROR)
{
printf("Error finalizing the library!\n");
goto err;
}
printf("Correctly finalized the library.\n");
printf("Program terminating with no error.\n");
return EXIT_SUCCESS;
err:
rsb_perror(NULL,errval);
printf("Program terminating with error.\n");
return EXIT_FAILURE;
}
Bestie the little checklist in that comment block is giving syllabus energy β¨ and it's all there: init with #rsb_lib_init(), allocate with #rsb_mtx_alloc_from_coo_const() (implicit diagonal, hello), print info with #rsb_mtx_get_info_str(), multiply with #rsb_spmv(), autotune with #rsb_tune_spsm(), solve with #rsb_spsv(), free with #rsb_mtx_free(), finalize with #rsb_lib_exit(). Iconic. π
Little details we are NOT skipping fr fr:
- It's a 6Γ6 matrix with
nnzA = 7nonzeroes,brA/bcAboth coming fromRSB_DEFAULT_BLOCKINGviabs. - The
IA[]row coords are{1,2,3,4,5,6,1}and theJA[]col coords are{1,2,3,4,5,6,6}β both 1-based, henceRSB_FLAG_FORTRAN_INDICES_INTERFACE. VA[]is seven1s,X[]starts as six0s,B[]is six1s.- The alloc flags are
RSB_FLAG_DEFAULT_RSB_MATRIX_FLAGS(force rsb),RSB_FLAG_DUPLICATES_SUM(sum dups),RSB_FLAG_UNIT_DIAG_IMPLICIT(ask diagonal implicit),RSB_FLAG_TRIANGULAR(need triangle for spsv), andRSB_FLAG_FORTRAN_INDICES_INTERFACE(treat indices as 1-based). wvat = 1means "want verbose autotuning" β see documentation ofRSB_IO_WANT_VERBOSE_TUNING, says the comment, we are just relaying the message πibis achar[200]buffer and the info string key is"RSB_MIF_MATRIX_INFO__TO__CHAR_P".
And the flow? It prints the matrix via rsb_file_mtx_save(mtxAp, NULL), does the spmv, saves B and X with rsb_file_vec_save, autotunes, solves, saves X again, and then β iconic behavior β loops over the result and if any X[i] != one it yells "Warning! Result vector not unitary!:\n", sets errval = RSB_ERR_INTERNAL_ERROR, and bails. πΈ
Exit statuses, noted: happy path returns EXIT_SUCCESS after printing "Program terminating with no error.\n". The err: label runs rsb_perror(NULL,errval), prints "Program terminating with error.\n", and returns EXIT_FAILURE. Clean. No notes. π
πΈ examples/bench.sh πΈ
Now Copyright (C) 2008-2026 Michele Martone β same LGPL v3-or-later deal, same COPYING file, same <https://www.gnu.org/licenses/> link. It's a \ingroup rsb-examples, @file, @author Michele Martone, @brief Benchmark invocation from shell script. π¦
#!/bin/bash
#
# Copyright (C) 2008-2026 Michele Martone
#
# This file is part of librsb.
#
# librsb is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# librsb is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with librsb; see the file COPYING.
# If not, see <https://www.gnu.org/licenses/>.
# Benchmark rsbench and postprocess results.
# \ingroup rsb-examples
# @file
# @author Michele Martone
# @brief Benchmark invocation from shell script.
#
set -e
set -x
which rsbench
BRF=test.rpr
# invoke rsbench and produce a performance record, using all types and one thread
rsbench -oa -Ob --bench --lower 100 --as-symmetric --types ':' -n 1 --notranspose --compare-competitors --verbose --verbose --write-performance-record=${BRF}
# examine tuning renderings (produced by --verbose --verbose)
ls -ltr ${BRF/.rpr/}-tuning*
# convert the performance record to text form
rsbench --read-performance-record ${BRF} > ${BRF/rpr/txt}
ls -ltr ${BRF/rpr/txt}
# convert the performance record to LaTeX table document form
RSB_PR_WLTC=2 RSB_PR_SR=0 rsbench --read-performance-record ${BRF} > ${BRF/rpr/tex}
which latex || exit 0 # to compile LaTeX document
which kpsepath || exit 0 # to check LaTeX packages
find `kpsepath tex | sed 's/!!//g;s/:/\n/g;'` -name sciposter.cls || exit 0 # need sciposter class, usually in texlive-science
find `kpsepath tex | sed 's/!!//g;s/:/\n/g;'` -name translator.sty || exit 0 # need sciposter class, usually in texlive-latex-recommended
# convert the LaTeX table into a DVI (may as well use pdflatex for PDF)
latex -interaction=batchmode -file-line-error ${BRF/rpr/tex}
Okay bestie let's break this down because it's genuinely so much π
set -eandset -xare up top β so it bails on error and traces everything it does. Main character debugging energy.which rsbenchβ gotta make sure the tool exists, obviously.BRF=test.rprβ the performance record file name, saved in a variable so it can be reused with substitutions everywhere. Smart.- Then the big one:
rsbench -oa -Ob --bench --lower 100 --as-symmetric --types ':' -n 1 --notranspose --compare-competitors --verbose --verbose --write-performance-record=${BRF}β the comment says it's "invoke rsbench and produce a performance record, using all types and one thread" π ls -ltr ${BRF/.rpr/}-tuning*to peek at the tuning renderings, which the comment credits to--verbose --verbose.rsbench --read-performance-record ${BRF} > ${BRF/rpr/txt}turns the record into text form, thenls -ltr ${BRF/rpr/txt}shows it off.RSB_PR_WLTC=2 RSB_PR_SR=0 rsbench --read-performance-record ${BRF} > ${BRF/rpr/tex}makes the LaTeX table document form πΈ- Then a whole guard-clause era:
which latex || exit 0(to compile LaTeX document),which kpsepath || exit 0(to check LaTeX packages), and twofindcalls over`kpsepath tex | sed 's/!!//g;s/:/\n/g;'`β one for-name sciposter.cls(# need sciposter class, usually in texlive-science) and one for-name translator.sty(# need sciposter class, usually in texlive-latex-recommended), both|| exit 0. - Finally
latex -interaction=batchmode -file-line-error ${BRF/rpr/tex}β comment says "convert the LaTeX table into a DVI (may as well use pdflatex for PDF)". π
So the whole script is basically: bench it, look at the tuning output, dump text, dump LaTeX, bail gracefully if LaTeX tooling isn't installed, otherwise compile. Chef's kiss. β¨
Heads up bestie: those|| exit 0guards mean it exits successfully even when LaTeX stuff is missing, andset -emeans any other unguarded failure kills the whole script. Don't come for me, that's just what the script says. π
# convert the performance record to GNUPLOT plots
which gnuplot || exit 0
RSB_PRD_STYLE_PLT_PFN=${BRF/rpr/} RSB_PRD_STYLE_PLT_FMT=1 RSB_PR_SR=2 rsbench --read-performance-record ${BRF} > ${BRF/rpr/gnu}
# convert the GNUPLOT plots into PDF
ls -ltr ${BRF/rpr/gnu}
gnuplot ${BRF/rpr/gnu}
# convert the performance record to GNUPLOT plots, different way
RSB_PRD_STYLE_PLT_PFN=${BRF/rpr/} RSB_PRD_STYLE_PLT_FMT='' RSB_PR_SR=2 rsbench --read-performance-record ${BRF} > ${BRF/rpr/gnu}
gnuplot ${BRF/rpr/gnu}
ls -ltr ${BRF/.rpr/}*.png
ls -ltr ${BRF/.rpr/}*.eps
ls -ltr ${BRF} ${BRF/rpr/tex} ${BRF/rpr/dvi} ${BRF/rpr/txt}
# clean up
rm ${BRF/rpr/}{aux,log,out,gnu}
rm ${BRF/.rpr/}*{.png,.eps}
rm ${BRF} ${BRF/rpr/tex} ${BRF/rpr/dvi} ${BRF/rpr/txt}
exit
okay so that's the whole pipeline bestie β plot it, PDF it, hoard the .png and .eps and .tex and .dvi and .txt like the little collector you are, and then clean up after yourself like the queen you are π§Ήβ¨
Most of the snippets in the documentation come from examples/snippets.c. (yes really, all that lore lives in one humble little file, iconic behavior fr fr π
)
π AUTHOR π
librsb was written by Michele Martone; this documentation has been generated by Doxygen. shoutout to the algorithm that typed all of this out for us, no cap π
π SEE ALSO π
rsb-examplesrsb-spblas.hrsb.hrsb.hpp
go read those next bestie, they're literally right there, the way they're just sitting in SEE ALSO waiting for u π¦
and that's a wrap on the whole page, periodt π this lil' beauty lives in librsb, Version 1.3.0.3, and it's filed under rsb-examples(3) π ty for reading the whole thing, ur literally a scholar now, main character energy only πβ¨
π¦ where this page comes from
- ubuntu stonking: librsb-dev 1.3.0.3+dfsg-1 Β· LGPL-3.0-or-later
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms