Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
166 changes: 131 additions & 35 deletions frame/1/bli_l1v_tapi.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,133 @@
// #included from files that define the typed API macros.
#ifdef BLIS_ENABLE_TAPI

// -- Optional OpenMP threading for memory-bound level-1v operations ----------
// BLIS runs level-1/2 single-threaded, which leaves most of the memory
// bandwidth on the table for large vectors (a single GB10 X925 core sees
// ~45 GB/s vs ~118 GB/s aggregate). When BLIS_ENABLE_L1_OPENMP is defined,
// large contiguous-enough level-1v calls are split across OpenMP threads.
#ifdef BLIS_ENABLE_L1_OPENMP
#include <omp.h>
#ifndef BLIS_L1_MT_THRESHOLD
#define BLIS_L1_MT_THRESHOLD 200000 // min elements to bother threading
#endif
#ifndef BLIS_L1_MT_MAX
#define BLIS_L1_MT_MAX 256 // cap on partial-sum buffer / threads
#endif
#ifndef BLI_L1V_MT_HELPERS
#define BLI_L1V_MT_HELPERS
BLIS_INLINE bool bli_l1v_mt_ok( dim_t n )
{
// Large enough, not already nested in a parallel region, and within the
// fixed partial-sum buffer size used by the reduction path.
return ( n >= ( dim_t )BLIS_L1_MT_THRESHOLD ) &&
( omp_get_active_level() == 0 ) &&
( omp_get_max_threads() <= BLIS_L1_MT_MAX );
}
// Contiguous [start,len) sub-range for the calling thread -- a plain EQUAL
// split. Even on heterogeneous machines (mixed fast/slow cores) equal work per
// thread is the right split for these ops: they are pure streaming with no data
// reuse, so they are memory-bandwidth bound and every core makes memory
// progress at nearly the same rate regardless of its compute width (e.g. on
// GB10 the fast-core:slow-core per-core throughput is ~1.0-1.1 for axpy). This
// is unlike compute-bound level-3 GEMM, whose data reuse would justify giving
// the faster cores proportionally more work.
BLIS_INLINE void bli_l1v_range( dim_t n, dim_t* start, dim_t* len )
{
const dim_t nt = omp_get_num_threads();
const dim_t tid = omp_get_thread_num();
const dim_t base = n / nt;
const dim_t rem = n % nt;
*start = tid * base + ( tid < rem ? tid : rem );
*len = base + ( tid < rem ? 1 : 0 );
}
#endif

// axpyv/scal2v: disjoint output; split the range and call the kernel per chunk.
#define bli_l1v_axpyv_kercall( ch, ctype, f, conjx, n, alpha, x, incx, y, incy, cntx ) \
{ \
if ( bli_l1v_mt_ok( n ) ) \
{ \
_Pragma( "omp parallel" ) \
{ \
dim_t s_, l_; bli_l1v_range( (n), &s_, &l_ ); \
if ( l_ > 0 ) \
f( conjx, l_, ( ctype* )(alpha), ( ctype* )(x) + s_*(incx), (incx), \
(y) + s_*(incy), (incy), ( cntx_t* )(cntx) ); \
} \
} \
else \
f( conjx, (n), ( ctype* )(alpha), ( ctype* )(x), (incx), (y), (incy), ( cntx_t* )(cntx) ); \
}

// dotv: reduction; each thread computes a partial dot, combined afterward.
#define bli_l1v_dotv_kercall( ch, ctype, f, conjx, conjy, n, x, incx, y, incy, rho, cntx ) \
{ \
if ( bli_l1v_mt_ok( n ) ) \
{ \
ctype parts_[ BLIS_L1_MT_MAX ]; \
dim_t nt_ = 1; \
_Pragma( "omp parallel" ) \
{ \
const dim_t tid_ = omp_get_thread_num(); \
dim_t s_, l_; bli_l1v_range( (n), &s_, &l_ ); \
ctype pr_; bli_tset0s( ch, pr_ ); \
if ( tid_ == 0 ) nt_ = omp_get_num_threads(); \
if ( l_ > 0 ) \
f( conjx, conjy, l_, ( ctype* )(x) + s_*(incx), (incx), \
( ctype* )(y) + s_*(incy), (incy), &pr_, ( cntx_t* )(cntx) ); \
parts_[ tid_ ] = pr_; \
} \
ctype acc_; bli_tset0s( ch, acc_ ); \
for ( dim_t t_ = 0; t_ < nt_; ++t_ ) bli_tadds( ch, ch, ch, parts_[ t_ ], acc_ ); \
*(rho) = acc_; \
} \
else \
f( conjx, conjy, (n), ( ctype* )(x), (incx), ( ctype* )(y), (incy), (rho), ( cntx_t* )(cntx) ); \
}

// copyv/addv/subv: two-vector, disjoint output; split the range.
#define bli_l1v_copyv_kercall( ch, ctype, f, conjx, n, x, incx, y, incy, cntx ) \
{ \
if ( bli_l1v_mt_ok( n ) ) \
{ \
_Pragma( "omp parallel" ) \
{ \
dim_t s_, l_; bli_l1v_range( (n), &s_, &l_ ); \
if ( l_ > 0 ) \
f( conjx, l_, ( ctype* )(x) + s_*(incx), (incx), (y) + s_*(incy), (incy), ( cntx_t* )(cntx) ); \
} \
} \
else \
f( conjx, (n), ( ctype* )(x), (incx), (y), (incy), ( cntx_t* )(cntx) ); \
}

// scalv/invscalv/setv: single in-place vector, disjoint; split the range.
#define bli_l1v_scalv_kercall( ch, ctype, f, conjalpha, n, alpha, x, incx, cntx ) \
{ \
if ( bli_l1v_mt_ok( n ) ) \
{ \
_Pragma( "omp parallel" ) \
{ \
dim_t s_, l_; bli_l1v_range( (n), &s_, &l_ ); \
if ( l_ > 0 ) \
f( conjalpha, l_, ( ctype* )(alpha), (x) + s_*(incx), (incx), ( cntx_t* )(cntx) ); \
} \
} \
else \
f( conjalpha, (n), ( ctype* )(alpha), (x), (incx), ( cntx_t* )(cntx) ); \
}
#else
#define bli_l1v_axpyv_kercall( ch, ctype, f, conjx, n, alpha, x, incx, y, incy, cntx ) \
f( conjx, (n), ( ctype* )(alpha), ( ctype* )(x), (incx), (y), (incy), ( cntx_t* )(cntx) )
#define bli_l1v_dotv_kercall( ch, ctype, f, conjx, conjy, n, x, incx, y, incy, rho, cntx ) \
f( conjx, conjy, (n), ( ctype* )(x), (incx), ( ctype* )(y), (incy), (rho), ( cntx_t* )(cntx) )
#define bli_l1v_copyv_kercall( ch, ctype, f, conjx, n, x, incx, y, incy, cntx ) \
f( conjx, (n), ( ctype* )(x), (incx), (y), (incy), ( cntx_t* )(cntx) )
#define bli_l1v_scalv_kercall( ch, ctype, f, conjalpha, n, alpha, x, incx, cntx ) \
f( conjalpha, (n), ( ctype* )(alpha), (x), (incx), ( cntx_t* )(cntx) )
#endif

//
// Define BLAS-like interfaces with typed operands.
//
Expand Down Expand Up @@ -63,14 +190,7 @@ void PASTEMAC(ch,opname,EX_SUF) \
\
PASTECH(opname,_ker_ft) f = bli_cntx_get_ukr_dt( dt, kerid, cntx ); \
\
f \
( \
conjx, \
n, \
( ctype* )x, incx, \
y, incy, \
( cntx_t* )cntx \
); \
bli_l1v_copyv_kercall( ch, ctype, f, conjx, n, x, incx, y, incy, cntx ); \
}

INSERT_GENTFUNC_BASIC( addv, BLIS_ADDV_KER )
Expand Down Expand Up @@ -177,15 +297,7 @@ void PASTEMAC(ch,opname,EX_SUF) \
\
PASTECH(opname,_ker_ft) f = bli_cntx_get_ukr_dt( dt, kerid, cntx ); \
\
f \
( \
conjx, \
n, \
( ctype* )alpha, \
( ctype* )x, incx, \
y, incy, \
( cntx_t* )cntx \
); \
bli_l1v_axpyv_kercall( ch, ctype, f, conjx, n, alpha, x, incx, y, incy, cntx ); \
}

INSERT_GENTFUNC_BASIC( axpyv, BLIS_AXPYV_KER )
Expand Down Expand Up @@ -217,16 +329,7 @@ void PASTEMAC(ch,opname,EX_SUF) \
\
PASTECH(opname,_ker_ft) f = bli_cntx_get_ukr_dt( dt, kerid, cntx ); \
\
f \
( \
conjx, \
conjy, \
n, \
( ctype* )x, incx, \
( ctype* )y, incy, \
rho, \
( cntx_t* )cntx \
); \
bli_l1v_dotv_kercall( ch, ctype, f, conjx, conjy, n, x, incx, y, incy, rho, cntx ); \
}

INSERT_GENTFUNC_BASIC( dotv, BLIS_DOTV_KER )
Expand Down Expand Up @@ -331,14 +434,7 @@ void PASTEMAC(ch,opname,EX_SUF) \
\
PASTECH(opname,_ker_ft) f = bli_cntx_get_ukr_dt( dt, kerid, cntx ); \
\
f \
( \
conjalpha, \
n, \
( ctype* )alpha, \
x, incx, \
( cntx_t* )cntx \
); \
bli_l1v_scalv_kercall( ch, ctype, f, conjalpha, n, alpha, x, incx, cntx ); \
}

INSERT_GENTFUNC_BASIC( invscalv, BLIS_INVSCALV_KER )
Expand Down
85 changes: 61 additions & 24 deletions frame/2/gemv/bli_gemv_unf_var1.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,62 @@

#include "blis.h"

// -- Optional OpenMP output-parallel dotxf loop for gemv (transpose) ---------
// var1 computes each output element independently (dotxf: y1 = beta*y1 +
// alpha*A1*x over f output entries), so the output loop is disjoint and can be
// split directly across threads -- no reduction. Each thread owns a contiguous
// band [i0,i1) of output indices; f is clamped to the band. Beta is applied by
// dotxf per output element. Enabled via BLIS_ENABLE_L1_OPENMP.
#ifdef BLIS_ENABLE_L1_OPENMP
#include <omp.h>
#ifndef BLIS_L2_MT_THRESHOLD
#define BLIS_L2_MT_THRESHOLD 262144
#endif
#define BLI_GEMV_V1_DOTXF_LOOP( kfp_df, conja, conjx, n_elem, n_iter, b_fuse, \
alpha, a, rs_at, cs_at, x, incx, beta, y, incy, cntx, mn ) \
{ \
if ( ( uint64_t )(mn) >= ( uint64_t )BLIS_L2_MT_THRESHOLD && \
omp_get_active_level() == 0 && omp_get_max_threads() > 1 ) \
{ \
_Pragma( "omp parallel" ) \
{ \
const dim_t nt_ = omp_get_num_threads(), tid_ = omp_get_thread_num(); \
const dim_t bs_ = (n_iter) / nt_, rm_ = (n_iter) % nt_; \
const dim_t i0_ = tid_ * bs_ + ( tid_ < rm_ ? tid_ : rm_ ); \
const dim_t i1_ = i0_ + bs_ + ( tid_ < rm_ ? 1 : 0 ); \
dim_t i_, f_; \
for ( i_ = i0_; i_ < i1_; i_ += f_ ) { \
f_ = bli_determine_blocksize_dim_f( i_, i1_, (b_fuse) ); \
kfp_df( (conja), (conjx), (n_elem), f_, (alpha), \
(a) + i_*(rs_at), (cs_at), (rs_at), (x), (incx), \
(beta), (y) + i_*(incy), (incy), (cntx) ); \
} \
} \
} \
else { \
dim_t i_, f_; \
for ( i_ = 0; i_ < (n_iter); i_ += f_ ) { \
f_ = bli_determine_blocksize_dim_f( i_, (n_iter), (b_fuse) ); \
kfp_df( (conja), (conjx), (n_elem), f_, (alpha), \
(a) + i_*(rs_at), (cs_at), (rs_at), (x), (incx), \
(beta), (y) + i_*(incy), (incy), (cntx) ); \
} \
} \
}
#else
#define BLI_GEMV_V1_DOTXF_LOOP( kfp_df, conja, conjx, n_elem, n_iter, b_fuse, \
alpha, a, rs_at, cs_at, x, incx, beta, y, incy, cntx, mn ) \
{ \
dim_t i_, f_; \
for ( i_ = 0; i_ < (n_iter); i_ += f_ ) { \
f_ = bli_determine_blocksize_dim_f( i_, (n_iter), (b_fuse) ); \
kfp_df( (conja), (conjx), (n_elem), f_, (alpha), \
(a) + i_*(rs_at), (cs_at), (rs_at), (x), (incx), \
(beta), (y) + i_*(incy), (incy), (cntx) ); \
} \
}
#endif

#undef GENTFUNC
#define GENTFUNC( ctype, ch, varname ) \
\
Expand Down Expand Up @@ -72,30 +128,11 @@ void PASTEMAC(ch,varname) \
dotxf_ker_ft kfp_df = bli_cntx_get_ukr_dt( dt, BLIS_DOTXF_KER, cntx ); \
b_fuse = bli_cntx_get_blksz_def_dt( dt, BLIS_DF, cntx ); \
\
for ( i = 0; i < n_iter; i += f ) \
{ \
f = bli_determine_blocksize_dim_f( i, n_iter, b_fuse ); \
\
A1 = a + (i )*rs_at + (0 )*cs_at; \
x1 = x + (0 )*incy; \
y1 = y + (i )*incy; \
\
/* y1 = beta * y1 + alpha * A1 * x; */ \
kfp_df \
( \
conja, \
conjx, \
n_elem, \
f, \
alpha, \
A1, cs_at, rs_at, \
x1, incx, \
beta, \
y1, incy, \
cntx \
); \
\
} \
/* y = beta*y + alpha * op(A) * x, output-parallel when enabled. */ \
( void )A1; ( void )x1; ( void )y1; ( void )i; ( void )f; \
BLI_GEMV_V1_DOTXF_LOOP( kfp_df, conja, conjx, n_elem, n_iter, b_fuse, \
alpha, a, rs_at, cs_at, x, incx, beta, y, incy, cntx, \
( uint64_t )m * ( uint64_t )n ); \
}

INSERT_GENTFUNC_BASIC( gemv_unf_var1 )
Expand Down
85 changes: 63 additions & 22 deletions frame/2/gemv/bli_gemv_unf_var2.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,64 @@

#include "blis.h"

// -- Optional OpenMP row-parallel axpyf loop for gemv (no-transpose) ---------
// gemv accumulates A's columns into y, so we cannot split the column loop
// without a reduction. Instead we split y's rows (n_elem): each thread runs
// the full column loop over its own contiguous band of rows, with offset A/y
// pointers -- disjoint outputs, no reduction. Beta-scaling of y already
// happened (serially) before this loop. Enabled via BLIS_ENABLE_L1_OPENMP.
#ifdef BLIS_ENABLE_L1_OPENMP
#include <omp.h>
#ifndef BLIS_L2_MT_THRESHOLD
#define BLIS_L2_MT_THRESHOLD 262144 // min m*n to thread
#endif
#define BLI_GEMV_V2_AXPYF_LOOP( kfp_af, conja, conjx, n_elem, n_iter, b_fuse, \
alpha, a, rs_at, cs_at, x, incx, y, incy, cntx, mn ) \
{ \
if ( ( uint64_t )(mn) >= ( uint64_t )BLIS_L2_MT_THRESHOLD && \
omp_get_active_level() == 0 && omp_get_max_threads() > 1 ) \
{ \
_Pragma( "omp parallel" ) \
{ \
const dim_t nt_ = omp_get_num_threads(), tid_ = omp_get_thread_num(); \
const dim_t bs_ = (n_elem) / nt_, rm_ = (n_elem) % nt_; \
const dim_t r0_ = tid_ * bs_ + ( tid_ < rm_ ? tid_ : rm_ ); \
const dim_t rl_ = bs_ + ( tid_ < rm_ ? 1 : 0 ); \
if ( rl_ > 0 ) { \
dim_t i_, f_; \
for ( i_ = 0; i_ < (n_iter); i_ += f_ ) { \
f_ = bli_determine_blocksize_dim_f( i_, (n_iter), (b_fuse) ); \
kfp_af( (conja), (conjx), rl_, f_, (alpha), \
(a) + r0_*(rs_at) + i_*(cs_at), (rs_at), (cs_at), \
(x) + i_*(incx), (incx), (y) + r0_*(incy), (incy), (cntx) ); \
} \
} \
} \
} \
else { \
dim_t i_, f_; \
for ( i_ = 0; i_ < (n_iter); i_ += f_ ) { \
f_ = bli_determine_blocksize_dim_f( i_, (n_iter), (b_fuse) ); \
kfp_af( (conja), (conjx), (n_elem), f_, (alpha), \
(a) + i_*(cs_at), (rs_at), (cs_at), \
(x) + i_*(incx), (incx), (y), (incy), (cntx) ); \
} \
} \
}
#else
#define BLI_GEMV_V2_AXPYF_LOOP( kfp_af, conja, conjx, n_elem, n_iter, b_fuse, \
alpha, a, rs_at, cs_at, x, incx, y, incy, cntx, mn ) \
{ \
dim_t i_, f_; \
for ( i_ = 0; i_ < (n_iter); i_ += f_ ) { \
f_ = bli_determine_blocksize_dim_f( i_, (n_iter), (b_fuse) ); \
kfp_af( (conja), (conjx), (n_elem), f_, (alpha), \
(a) + i_*(cs_at), (rs_at), (cs_at), \
(x) + i_*(incx), (incx), (y), (incy), (cntx) ); \
} \
}
#endif

#undef GENTFUNC
#define GENTFUNC( ctype, ch, varname ) \
\
Expand Down Expand Up @@ -101,28 +159,11 @@ void PASTEMAC(ch,varname) \
axpyf_ker_ft kfp_af = bli_cntx_get_ukr_dt( dt, BLIS_AXPYF_KER, cntx ); \
b_fuse = bli_cntx_get_blksz_def_dt( dt, BLIS_AF, cntx ); \
\
for ( i = 0; i < n_iter; i += f ) \
{ \
f = bli_determine_blocksize_dim_f( i, n_iter, b_fuse ); \
\
A1 = a + (0 )*rs_at + (i )*cs_at; \
x1 = x + (i )*incx; \
y1 = y + (0 )*incy; \
\
/* y = y + alpha * A1 * x1; */ \
kfp_af \
( \
conja, \
conjx, \
n_elem, \
f, \
alpha, \
A1, rs_at, cs_at, \
x1, incx, \
y1, incy, \
cntx \
); \
} \
/* y = y + alpha * A * x, row-parallel when enabled (see macro above). */ \
( void )A1; ( void )x1; ( void )y1; ( void )i; ( void )f; \
BLI_GEMV_V2_AXPYF_LOOP( kfp_af, conja, conjx, n_elem, n_iter, b_fuse, \
alpha, a, rs_at, cs_at, x, incx, y, incy, cntx, \
( uint64_t )m * ( uint64_t )n ); \
}

INSERT_GENTFUNC_BASIC( gemv_unf_var2 )
Expand Down
Loading