RESULT-CHANGING: ModelScaleGPU: J^T J and J^T r summed in float, the R and the robust residuals' scale in float
scale_sums accumulated every Levenberg-Marquardt slot in double, which on a card with little double throughput (T4, consumer cards) is most of a step. J^T J and J^T r now go in float, per point, per thread and through the warp's shuffle tree, into the double sums across warps and blocks; they only set the direction of the next step, which the WSSR - still in double - accepts or not. MODE_R and robust_residuals take the anisotropic scale in float as the WSSR already does, and robust_weights its 1 / sqrt(1 + q^2) in float. Fixed launch shape and order as before: deterministic. cytochrome C refine run (50k reflections) on an RTX 5080 under nsys: scale_sums<3> 59 -> 38 us per launch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HVeDQqN9qRD57vnShzTC37
This commit is contained in:
1 parent
c5b7f18f67
commit
fdafc009bc
1 file changed
+39
-26
@@ -69,6 +69,12 @@ __global__ void scale_sums(const int *__restrict__ hkl, const double *__restrict
|
||||
double acc[NS];
|
||||
for (int s = 0; s < NS; s++)
|
||||
acc[s] = 0;
|
||||
// MODE_MATRICES' J^T J and J^T r, summed in float: a card with little double throughput spends most of a step
|
||||
// on these otherwise, and they only set the direction of the next step, which the WSSR (in double) then accepts
|
||||
// or not
|
||||
float acc_f[NS];
|
||||
for (int s = 0; s < NS; s++)
|
||||
acc_f[s] = 0;
|
||||
|
||||
for (int i = blockIdx.x * THREADS + threadIdx.x; i < n; i += BLOCKS * THREADS) {
|
||||
const float f_abs = f_abs_fit[i];
|
||||
@@ -88,18 +94,6 @@ __global__ void scale_sums(const int *__restrict__ hkl, const double *__restrict
|
||||
continue;
|
||||
}
|
||||
|
||||
if (f.mode == MODE_R) {
|
||||
const double hx = hkl[3 * i], hy = hkl[3 * i + 1], hz = hkl[3 * i + 2];
|
||||
const double *u = f.b_star;
|
||||
const double r_u_r = hx * hx * u[0] + hy * hy * u[1] + hz * hz * u[2]
|
||||
+ 2 * (hx * hy * u[3] + hx * hz * u[4] + hy * hz * u[5]);
|
||||
// compute_value(): |F| times the scale cast to float, in float
|
||||
const float value = f_abs * static_cast<float>(f.k_overall * exp(-0.25 * r_u_r));
|
||||
acc[0] += fabs(fo - value);
|
||||
acc[1] += fo;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Below, the anisotropic factor is taken in float rather than in double as gemmi takes it: double
|
||||
// arithmetic is most of the cost of a step on a card with little double throughput. In
|
||||
// compute_value() gemmi casts the scale to float anyway, so the WSSR moves by the last bit of some
|
||||
@@ -109,6 +103,13 @@ __global__ void scale_sums(const int *__restrict__ hkl, const double *__restrict
|
||||
const float r_u_r_f = fx * fx * uf[0] + fy * fy * uf[1] + fz * fz * uf[2]
|
||||
+ 2 * (fx * fy * uf[3] + fx * fz * uf[4] + fy * fz * uf[5]);
|
||||
const float k_aniso = expf(-0.25f * r_u_r_f);
|
||||
if (f.mode == MODE_R) {
|
||||
// compute_value(): |F| times the scale cast to float, in float
|
||||
const float value = f_abs * static_cast<float>(f.k_overall * k_aniso);
|
||||
acc[0] += fabs(fo - value);
|
||||
acc[1] += fo;
|
||||
continue;
|
||||
}
|
||||
// the robust fit's weight (ModelScaling.cpp, ComputeLMMatrices and ComputeWSSR multiply by it)
|
||||
const double w = f.weighted ? weight_all[static_cast<size_t>(f.column) * n + i] : 1.0;
|
||||
if (f.mode == MODE_WSSR) {
|
||||
@@ -118,12 +119,12 @@ __global__ void scale_sums(const int *__restrict__ hkl, const double *__restrict
|
||||
continue;
|
||||
}
|
||||
|
||||
// compute_value_and_derivatives() with k_sol and b_sol fixed, per point in float, summed in double
|
||||
// compute_value_and_derivatives() with k_sol and b_sol fixed, per point in float
|
||||
const float fe = f_abs * k_aniso;
|
||||
const float y = static_cast<float>(f.k_overall) * fe;
|
||||
const float du[6] = {-0.25f * y * (fx * fx), -0.25f * y * (fy * fy), -0.25f * y * (fz * fz),
|
||||
-0.5f * y * (fx * fy), -0.5f * y * (fx * fz), -0.5f * y * (fy * fz)};
|
||||
double dy_da[NA];
|
||||
float dy_da[NA];
|
||||
dy_da[0] = fe;
|
||||
for (int j = 1; j < NA; j++) {
|
||||
const float *r = c.row[j - 1];
|
||||
@@ -131,12 +132,13 @@ __global__ void scale_sums(const int *__restrict__ hkl, const double *__restrict
|
||||
}
|
||||
const double dy = fo - y;
|
||||
acc[0] += dy * dy * w;
|
||||
const float dy_f = static_cast<float>(dy), w_f = static_cast<float>(w);
|
||||
int s = 1;
|
||||
for (int j = 0; j < NA; j++)
|
||||
for (int k = 0; k <= j; k++)
|
||||
acc[s++] += dy_da[j] * dy_da[k] * w;
|
||||
acc_f[s++] += dy_da[j] * dy_da[k] * w_f;
|
||||
for (int j = 0; j < NA; j++)
|
||||
acc[s++] += dy * dy_da[j] * w;
|
||||
acc_f[s++] += dy_f * dy_da[j] * w_f;
|
||||
}
|
||||
|
||||
// The block's sum of each slot the mode fills: a fixed shuffle tree within each warp, then the warps
|
||||
@@ -145,9 +147,17 @@ __global__ void scale_sums(const int *__restrict__ hkl, const double *__restrict
|
||||
__shared__ double warp_sum[THREADS / 32][NS];
|
||||
const int lane = threadIdx.x % 32, warp = threadIdx.x / 32;
|
||||
for (int s = 0; s < used; s++) {
|
||||
double v = acc[s];
|
||||
for (int offset = 16; offset > 0; offset /= 2)
|
||||
v += __shfl_down_sync(0xffffffffu, v, offset);
|
||||
double v;
|
||||
if (f.mode == MODE_MATRICES && s > 0) {
|
||||
float vf = acc_f[s];
|
||||
for (int offset = 16; offset > 0; offset /= 2)
|
||||
vf += __shfl_down_sync(0xffffffffu, vf, offset);
|
||||
v = vf;
|
||||
} else {
|
||||
v = acc[s];
|
||||
for (int offset = 16; offset > 0; offset /= 2)
|
||||
v += __shfl_down_sync(0xffffffffu, v, offset);
|
||||
}
|
||||
if (lane == 0)
|
||||
warp_sum[warp][s] = v;
|
||||
}
|
||||
@@ -167,12 +177,14 @@ __global__ void robust_residuals(const int *__restrict__ hkl, const float *__res
|
||||
const ModelScaleFitState *__restrict__ fits, float *__restrict__ residual) {
|
||||
const ModelScaleFitState f = fits[blockIdx.y];
|
||||
const float *f_abs_fit = f_abs_all + static_cast<size_t>(f.column) * n;
|
||||
float uf[6];
|
||||
for (int k = 0; k < 6; k++)
|
||||
uf[k] = static_cast<float>(f.b_star[k]);
|
||||
for (int i = blockIdx.x * THREADS + threadIdx.x; i < n; i += BLOCKS * THREADS) {
|
||||
const double hx = hkl[3 * i], hy = hkl[3 * i + 1], hz = hkl[3 * i + 2];
|
||||
const double *u = f.b_star;
|
||||
const double r_u_r = hx * hx * u[0] + hy * hy * u[1] + hz * hz * u[2]
|
||||
+ 2 * (hx * hy * u[3] + hx * hz * u[4] + hy * hz * u[5]);
|
||||
const float value = f_abs_fit[i] * static_cast<float>(f.k_overall * exp(-0.25 * r_u_r));
|
||||
const float fx = hkl[3 * i], fy = hkl[3 * i + 1], fz = hkl[3 * i + 2];
|
||||
const float r_u_r_f = fx * fx * uf[0] + fy * fy * uf[1] + fz * fz * uf[2]
|
||||
+ 2 * (fx * fy * uf[3] + fx * fz * uf[4] + fy * fz * uf[5]);
|
||||
const float value = f_abs_fit[i] * static_cast<float>(f.k_overall * expf(-0.25f * r_u_r_f));
|
||||
residual[static_cast<size_t>(f.column) * n + i] = fabsf(fobs[i] - value);
|
||||
}
|
||||
}
|
||||
@@ -184,9 +196,10 @@ __global__ void robust_weights(float *residual, int n, const ModelScaleFitState
|
||||
if (!(f.c > 0))
|
||||
return;
|
||||
const size_t row = static_cast<size_t>(f.column) * n;
|
||||
const float c = static_cast<float>(f.c);
|
||||
for (int i = blockIdx.x * THREADS + threadIdx.x; i < n; i += BLOCKS * THREADS) {
|
||||
const double q = residual[row + i] / f.c;
|
||||
residual[row + i] = static_cast<float>(1.0 / sqrt(1.0 + q * q));
|
||||
const float q = residual[row + i] / c;
|
||||
residual[row + i] = 1.0f / sqrtf(1.0f + q * q);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user