1/*
2 * Browser benchmark entry point for libflame+BLIS WASM.
3 * Exposes run_bench(routine, n, nthreads) for calling from JavaScript;
4 * returns GFLOP/s (or -1 on error) and printf's a detail line (captured
5 * by the page via Module.print).
6 *
7 * Built two ways by build-web.sh:
8 * single-threaded: BLIS st + libflame.a
9 * threaded: -pthread -DBLIS_MT, BLIS pthreads + libflame-mt.a
10 */
17extern int dgemm_( char* transa, char* transb, int* m, int* n, int* k,
18 double* alpha, double* a, int* lda, double* b, int* ldb,
19 double* beta, double* c, int* ldc );
20extern int dpotrf_( char* uplo, int* n, double* a, int* lda, int* info );
21extern int dgetrf_( int* m, int* n, double* a, int* lda, int* ipiv, int* info );
24/* dim_t is int32 in this BLIS build (--int-size=32). */
25extern void bli_thread_set_num_threads( int n_threads );
28static double now_sec( void )
29{
30 struct timespec ts;
31 clock_gettime( CLOCK_MONOTONIC, &ts );
32 return ts.tv_sec + 1e-9 * ts.tv_nsec;
33}
35static unsigned long long rng_state = 12345;
36static double frand( void )
37{
38 rng_state = rng_state * 6364136223846793005ULL + 1442695040888963407ULL;
39 return ( ( rng_state >> 33 ) & 0xffffff ) / (double) 0x1000000 - 0.5;
40}
42static void fill_random( double* a, int n2 )
43{
44 int i;
45 rng_state = 12345;
46 for ( i = 0; i < n2; i++ ) a[i] = frand();
47}
49static void fill_spd( double* a, int n )
50{
51 int i, j;
52 rng_state = 12345;
53 for ( j = 0; j < n; j++ )
54 for ( i = 0; i < n; i++ )
55 a[ j*n + i ] = ( i == j ) ? n : 0.5 * frand();
56 for ( j = 0; j < n; j++ )
57 for ( i = 0; i < j; i++ ) {
58 double v = 0.5 * ( a[ j*n + i ] + a[ i*n + j ] );
59 a[ j*n + i ] = a[ i*n + j ] = v;
60 }
61}
63static double bench_dgemm( int n )
64{
65 double *a = malloc( (size_t)n*n*sizeof(double) );
66 double *b = malloc( (size_t)n*n*sizeof(double) );
67 double *c = malloc( (size_t)n*n*sizeof(double) );
68 double alpha = 1.0, beta = 0.0, t, gflops;
69 char tr = 'N';
71 if ( !a || !b || !c ) { free(a); free(b); free(c); return -1.0; }
72 fill_random( a, n*n );
73 fill_random( b, n*n );
74 memset( c, 0, (size_t)n*n*sizeof(double) );
76 t = now_sec();
77 dgemm_( &tr, &tr, &n, &n, &n, &alpha, a, &n, b, &n, &beta, c, &n );
78 t = now_sec() - t;
80 gflops = 2.0 * n * (double)n * n / t / 1e9;
81 printf( "dgemm n=%5d %8.3f s %7.2f GFLOP/s (check c[0]=%.6f)\n",
82 n, t, gflops, c[0] );
83 free( a ); free( b ); free( c );
84 return gflops;
85}
87static double bench_dpotrf( int n )
88{
89 double *a = malloc( (size_t)n*n*sizeof(double) );
90 double t, gflops;
91 char lo = 'L';
92 int info = 0;
94 if ( !a ) return -1.0;
95 fill_spd( a, n );
96 t = now_sec();
97 dpotrf_( &lo, &n, a, &n, &info );
98 t = now_sec() - t;
100 gflops = ( (double)n * n * n / 3.0 ) / t / 1e9;
101 printf( "dpotrf n=%5d %8.3f s %7.2f GFLOP/s (info=%d, L00=%.6f)\n",
102 n, t, gflops, info, a[0] );
103 free( a );
104 return info == 0 ? gflops : -1.0;
105}
107static double bench_dgetrf( int n )
108{
109 double *a = malloc( (size_t)n*n*sizeof(double) );
110 int *ipiv = malloc( (size_t)n*sizeof(int) );
111 double t, gflops;
112 int info = 0;
114 if ( !a || !ipiv ) { free(a); free(ipiv); return -1.0; }
115 fill_random( a, n*n );
116 t = now_sec();
117 dgetrf_( &n, &n, a, &n, ipiv, &info );
118 t = now_sec() - t;
120 gflops = ( 2.0 * n * (double)n * n / 3.0 ) / t / 1e9;
121 printf( "dgetrf n=%5d %8.3f s %7.2f GFLOP/s (info=%d)\n",
122 n, t, gflops, info );
123 free( a ); free( ipiv );
124 return info == 0 ? gflops : -1.0;
125}
127EMSCRIPTEN_KEEPALIVE
128double run_bench( int routine, int n, int nthreads )
129{
130 if ( n < 2 || n > 8000 ) return -1.0;
133 if ( nthreads > 0 ) bli_thread_set_num_threads( nthreads );
135 (void) nthreads;
138 switch ( routine ) {
139 case 0: return bench_dgemm( n );
140 case 1: return bench_dpotrf( n );
141 case 2: return bench_dgetrf( n );
142 default: return -1.0;
143 }
144}