/** * embedded3 item 3: the f64 conjugate gradient's maps on the device over * the active cells (index list), with FIXED-ORDER reductions: every block * reduces its 256 lanes by the same shared-memory tree and writes one * partial; `e3_cg_reduce` sums the partials in index order on one thread. * Run-to-run bit-identical by construction (no atomics). */ #define NONE 0xFFFFFFFFu /* The seven-point neighbour sum plus the cell's off-stencil links (CSR: * link_ptr[g] .. link_ptr[g + 1]; the virtually merged small cells). */ __device__ __forceinline__ double nb_sum3d( int g, int nx, const double* ae, const double* aw, const double* an, const double* as_, const double* at, const double* ab, const unsigned int* top, const unsigned int* bot, const unsigned int* link_ptr, const unsigned int* link_idx, const double* link_coef, const double* x) { double s = 0.0; double e = ae[g]; if (e != 0.0) s += e * x[g + 1]; double w = aw[g]; if (w != 0.0) s += w * x[g - 1]; double nn = an[g]; if (nn != 0.0) s += nn * x[g + nx]; double ss = as_[g]; if (ss != 0.0) s += ss * x[g - nx]; double t = at[g]; if (t != 0.0) s += t * x[top[g]]; double b = ab[g]; if (b != 0.0) s += b * x[bot[g]]; for (unsigned int l = link_ptr[g]; l < link_ptr[g + 1]; ++l) s += link_coef[l] * x[link_idx[l]]; return s; } __device__ __forceinline__ double block_reduce(double v) { __shared__ double sh[256]; int t = threadIdx.x; sh[t] = v; __syncthreads(); for (int s = 128; s > 0; s >>= 1) { if (t < s) sh[t] += sh[t + s]; __syncthreads(); } return sh[0]; } /* q = A d on the cells. */ extern "C" __global__ void e3_cg_spmv( int n_cells, const unsigned int* __restrict__ cells, const double* __restrict__ ae, const double* __restrict__ aw, const double* __restrict__ an, const double* __restrict__ as_, const double* __restrict__ at, const double* __restrict__ ab, const unsigned int* __restrict__ top, const unsigned int* __restrict__ bot, const unsigned int* __restrict__ link_ptr, const unsigned int* __restrict__ link_idx, const double* __restrict__ link_coef, const double* __restrict__ ap, const double* __restrict__ d, double* __restrict__ q, int nx) { int t = blockIdx.x * blockDim.x + threadIdx.x; if (t >= n_cells) return; int g = cells[t]; q[g] = ap[g] * d[g] - nb_sum3d(g, nx, ae, aw, an, as_, at, ab, top, bot, link_ptr, link_idx, link_coef, d); } /* r = b − A p on the cells; partial[block] = Σ |r| over the block's cells. */ extern "C" __global__ void e3_cg_residual( int n_cells, const unsigned int* __restrict__ cells, const double* __restrict__ ae, const double* __restrict__ aw, const double* __restrict__ an, const double* __restrict__ as_, const double* __restrict__ at, const double* __restrict__ ab, const unsigned int* __restrict__ top, const unsigned int* __restrict__ bot, const unsigned int* __restrict__ link_ptr, const unsigned int* __restrict__ link_idx, const double* __restrict__ link_coef, const double* __restrict__ ap, const double* __restrict__ b, const double* __restrict__ p, double* __restrict__ r, double* __restrict__ partial, int nx) { int t = blockIdx.x * blockDim.x + threadIdx.x; double v = 0.0; if (t < n_cells) { int g = cells[t]; v = b[g] - (ap[g] * p[g] - nb_sum3d(g, nx, ae, aw, an, as_, at, ab, top, bot, link_ptr, link_idx, link_coef, p)); r[g] = v; v = fabs(v); } double s = block_reduce(v); if (threadIdx.x == 0) partial[blockIdx.x] = s; } /* partial[block] = Σ a·b over the block's cells (b = a for a norm). */ extern "C" __global__ void e3_cg_dot_partial( int n_cells, const unsigned int* __restrict__ cells, const double* __restrict__ a, const double* __restrict__ b, double* __restrict__ partial) { int t = blockIdx.x * blockDim.x + threadIdx.x; double v = 0.0; if (t < n_cells) { int g = cells[t]; v = a[g] * b[g]; } double s = block_reduce(v); if (threadIdx.x == 0) partial[blockIdx.x] = s; } /* partial[block] = Σ |a| over the block's cells. */ extern "C" __global__ void e3_cg_l1_partial( int n_cells, const unsigned int* __restrict__ cells, const double* __restrict__ a, double* __restrict__ partial) { int t = blockIdx.x * blockDim.x + threadIdx.x; double v = 0.0; if (t < n_cells) v = fabs(a[cells[t]]); double s = block_reduce(v); if (threadIdx.x == 0) partial[blockIdx.x] = s; } /* partial[block] = Σ a over the block's cells. */ extern "C" __global__ void e3_cg_sum_partial( int n_cells, const unsigned int* __restrict__ cells, const double* __restrict__ a, double* __restrict__ partial) { int t = blockIdx.x * blockDim.x + threadIdx.x; double v = 0.0; if (t < n_cells) v = a[cells[t]]; double s = block_reduce(v); if (threadIdx.x == 0) partial[blockIdx.x] = s; } /* out[0] = Σ partial[0..n) in index order, one thread. */ extern "C" __global__ void e3_cg_reduce(int n, const double* __restrict__ partial, double* __restrict__ out) { if (blockIdx.x * blockDim.x + threadIdx.x != 0) return; double s = 0.0; for (int i = 0; i < n; ++i) s += partial[i]; out[0] = s; } /* p += alpha d; r -= alpha q. */ extern "C" __global__ void e3_cg_axpy2( int n_cells, const unsigned int* __restrict__ cells, double alpha, const double* __restrict__ d, const double* __restrict__ q, double* __restrict__ p, double* __restrict__ r) { int t = blockIdx.x * blockDim.x + threadIdx.x; if (t >= n_cells) return; int g = cells[t]; p[g] += alpha * d[g]; r[g] -= alpha * q[g]; } /* y += alpha x over the cells (PERF-3 P2: the projected initial guess). */ extern "C" __global__ void e3_cg_axpy( int n_cells, const unsigned int* __restrict__ cells, double alpha, const double* __restrict__ x, double* __restrict__ y) { int t = blockIdx.x * blockDim.x + threadIdx.x; if (t >= n_cells) return; int g = cells[t]; y[g] += alpha * x[g]; } /* d = z + beta d. */ extern "C" __global__ void e3_cg_xpay( int n_cells, const unsigned int* __restrict__ cells, double beta, const double* __restrict__ z, double* __restrict__ d) { int t = blockIdx.x * blockDim.x + threadIdx.x; if (t >= n_cells) return; int g = cells[t]; d[g] = z[g] + beta * d[g]; } /* dst = src (copy over the cells). */ extern "C" __global__ void e3_cg_copy( int n_cells, const unsigned int* __restrict__ cells, const double* __restrict__ src, double* __restrict__ dst) { int t = blockIdx.x * blockDim.x + threadIdx.x; if (t >= n_cells) return; int g = cells[t]; dst[g] = src[g]; } /* v -= s over the cells. */ extern "C" __global__ void e3_cg_shift( int n_cells, const unsigned int* __restrict__ cells, double s, double* __restrict__ v) { int t = blockIdx.x * blockDim.x + threadIdx.x; if (t >= n_cells) return; v[cells[t]] -= s; } /* f32 b0 = (float) r over the cells; f64 z = (double) x0 over the cells. */ extern "C" __global__ void e3_cg_gather_f32( int n_cells, const unsigned int* __restrict__ cells, const double* __restrict__ r, float* __restrict__ b0) { int t = blockIdx.x * blockDim.x + threadIdx.x; if (t >= n_cells) return; int g = cells[t]; b0[g] = (float) r[g]; } extern "C" __global__ void e3_cg_scatter_f64( int n_cells, const unsigned int* __restrict__ cells, const float* __restrict__ x0, double* __restrict__ z) { int t = blockIdx.x * blockDim.x + threadIdx.x; if (t >= n_cells) return; int g = cells[t]; z[g] = (double) x0[g]; } /* P1-5 (c): scatter compact (index, value) lists into persistent buffers. */ extern "C" __global__ void e3_scat_f64(int n, const unsigned int* __restrict__ idx, const double* __restrict__ val, double* __restrict__ dst) { int t = blockIdx.x * blockDim.x + threadIdx.x; if (t < n) dst[idx[t]] = val[t]; } extern "C" __global__ void e3_scat_f32(int n, const unsigned int* __restrict__ idx, const float* __restrict__ val, float* __restrict__ dst) { int t = blockIdx.x * blockDim.x + threadIdx.x; if (t < n) dst[idx[t]] = val[t]; } extern "C" __global__ void e3_scat_u32(int n, const unsigned int* __restrict__ idx, const unsigned int* __restrict__ val, unsigned int* __restrict__ dst) { int t = blockIdx.x * blockDim.x + threadIdx.x; if (t < n) dst[idx[t]] = val[t]; } extern "C" __global__ void e3_scat_i32(int n, const unsigned int* __restrict__ idx, const int* __restrict__ val, int* __restrict__ dst) { int t = blockIdx.x * blockDim.x + threadIdx.x; if (t < n) dst[idx[t]] = val[t]; }