Commit 978c5193 authored by Andrey Filippov's avatar Andrey Filippov

CLAUDE: DP spike PASS - CDP2 device-side pose-LMA step bit-identical on Blackwell

pose_lma_dp_step<<<1,1>>> tail-launches all six resident-step stages
(cudaStreamTailLaunch, CDP2) with zero host round trips; the only new
device work is pose_lma_dp_candidate_vector (replaces two host
cudaMemcpyAsync). NVRTC-only (#ifdef __CUDACC_RTC__): the production
module's --extensible-whole-program + libcudadevrt link is exactly the
CDP2 requirement; direct nvcc test builds never see device-side launches.

Feasibility verdict (the spike's deliverable): CDP2 JIT+link+run WORKS in
the production NVRTC module on CC 12.0 - no flag changes needed.

test_pose_lma_dp_jna: two TpProc instances from one module driven to the
identical pre-step resident state (the step mutates state on accept, so
one-instance host-then-DP would compare different starting states); host
prepared-resident path vs DP chain memcmp-equal on packed[0..24] for
accept (device set flip exercised under DP), reject, and post-flip reject;
multi-block child grid (300 tiles -> 2 blocks) + 3-partial reduce.
Wall/step always-reject microbench: host 132 us, DP 118 us (1.12x; the
real win is removing 6 JNA launch/sync round trips per step in production).

Gates: test_pose_lma_dp_jna PASS bit-identical; regressions PASS:
pose_lma_resident, pose_fx_jacobian, lma_products, pose_lma_step (all
NVRTC), pose_corr captured-data @tol 0.

Production notes for the DP milestone: the DP entry reuses the resident
gpu_pose_lma_vector (anchor) - the per-cycle vector update after accept
must move device-side (from result[16..18]) before multi-step DP chains.
Co-Authored-By: 's avatarClaude Fable 5 <noreply@anthropic.com>
parent a13d1661
This diff is collapsed.
......@@ -57,6 +57,7 @@ static const char* KERNELS[] = {
"pose_lma_step", // fixed 3-angle float products + direct solve in one thread // By Codex on 07/15/2026
"pose_lma_prepare_products","pose_lma_reduce_step","pose_lma_reduce_rms", // resident prepare/solve + candidate RMS
"pose_lma_assemble","pose_lma_prepare_norm","pose_lma_prepare_finish", // rung C1 resident prepareLMA // By Claude on 07/17/2026
"pose_lma_dp_step","pose_lma_dp_candidate_vector", // DP spike: device-side step chain // By Claude on 07/16/2026
"lma_normal_products"}; // deterministic double normal-equation products // By Codex on 07/14/2026
static const int N_KERNELS = sizeof(KERNELS)/sizeof(KERNELS[0]);
......@@ -698,6 +699,7 @@ struct TpProc {
int pose_prep_ready;
int pose_prep_centers_tiles; // tile count the resident centers were uploaded for // By Claude on 07/17/2026
int pose_prep_frozen_tiles; // rung C3: tile count of the frozen conditioning (0 = no full prepare yet) // By Claude on 07/17/2026
PoseDpChain *gpu_pose_dp_chain; // DP spike: device copy of the chain descriptor // By Claude on 07/16/2026
};
extern "C" {
......@@ -751,6 +753,7 @@ TpProc* tp_proc_create(TpModule* m){
p->gpu_pose_lma_candidate_partials=nullptr; // By Codex on 07/15/2026
p->gpu_pose_lma_prep=nullptr; // By Claude on 07/17/2026
p->pose_prep_ready=0; p->pose_prep_centers_tiles=0; p->pose_prep_frozen_tiles=0; // By Claude on 07/17/2026 (rungs C2/C3)
p->gpu_pose_dp_chain=nullptr; // By Claude on 07/16/2026 (DP spike)
return p;
}
......@@ -1690,6 +1693,65 @@ int tp_proc_exec_pose_lma_prepare_resident(TpProc* p,
return 0;
}
// DP spike (design 2026-07-16): the resident step as ONE device-side launch.
// Preconditions: a resident prepare (weights/y/eigen/vector) + valid raw pose
// state at this tile count are live (same as a prepared-resident step with NULL
// H2D). Fills the chain descriptor host-side, copies it once, launches
// pose_lma_dp_step<<<1,1>>>; the parent tail-launches all six stages with no
// host round trip. D2H = the 25-float result + the 4-byte current_set (rung B),
// exactly as the host-driven step. This is the feasibility spike, not yet the
// per-scene production entry. By Claude on 07/16/2026.
int tp_proc_exec_pose_lma_dp_step(TpProc* p,
float lambda,
float pure_weight,
float rms_diff,
int num_tiles,
float* result){
if(!p||!result||(num_tiles<=0)||!std::isfinite(pure_weight)||!(pure_weight>0.0f)||
!std::isfinite(rms_diff)){
seterr("exec_pose_lma_dp_step: bad args nt=%d",num_tiles); return -1; }
if((p->pose_raw_num_tiles!=num_tiles)||!p->pose_raw_has_jacobian||
(p->pose_lma_value_capacity<POSE_NUM_COMPONENTS*num_tiles+POSE_NUM_PARAMS)||
!p->gpu_pose_lma_weights||!p->gpu_pose_lma_y||!p->gpu_pose_lma_eigen||
!p->gpu_pose_lma_vector||!p->gpu_pose_current_set){
seterr("exec_pose_lma_dp_step: resident prepared state not ready (nt=%d)",num_tiles); return -2; }
cuCtxSetCurrent(p->mod->ctx);
const int num_partials=(num_tiles+POSE_LMA_REDUCE_THREADS-1)/POSE_LMA_REDUCE_THREADS;
if(!p->gpu_pose_dp_chain &&
cudaMalloc((void**)&p->gpu_pose_dp_chain,sizeof(PoseDpChain))!=cudaSuccess){
seterr("exec_pose_lma_dp_step: chain alloc failed"); return -3; }
PoseDpChain h;
h.num_tiles=num_tiles; h.num_partials=num_partials;
h.lambda=lambda; h.pure_weight=pure_weight; h.rms_diff=rms_diff;
h.cameras=p->gpu_pose_cameras;
h.ref_radial=p->gpu_pose_radial[0]; h.ref_rbr=p->gpu_pose_rbr[0]; h.ref_ers=p->gpu_pose_ers[0];
h.scene_radial=p->gpu_pose_radial[1];h.scene_rbr=p->gpu_pose_rbr[1]; h.scene_ers=p->gpu_pose_ers[1];
h.centers=p->gpu_pose_centers; h.selection=p->gpu_pose_selection;
h.cur_vectors=p->cur_pose_vectors(); h.cur_fx=p->cur_pose_fx();
h.cur_jt=p->cur_pose_jt(); h.cur_valid=p->cur_pose_valid();
h.cand_vectors=p->cand_pose_vectors();h.cand_fx=p->cand_pose_fx();
h.cand_jt=p->cand_pose_jt(); h.cand_valid=p->cand_pose_valid();
h.y=p->gpu_pose_lma_y; h.weights=p->gpu_pose_lma_weights; h.eigen=p->gpu_pose_lma_eigen;
h.vector=p->gpu_pose_lma_vector;
h.jt_out=p->gpu_pose_lma_jt; h.ymfx_out=p->gpu_pose_lma_ymfx; h.partials=p->gpu_pose_lma_partials;
h.cand_jt_out=p->gpu_pose_lma_candidate_jt; h.cand_ymfx_out=p->gpu_pose_lma_candidate_ymfx;
h.cand_partials=p->gpu_pose_lma_candidate_partials;
h.result=p->gpu_pose_lma_result; h.current_set=p->gpu_pose_current_set;
if(cudaMemcpy(p->gpu_pose_dp_chain,&h,sizeof(PoseDpChain),cudaMemcpyHostToDevice)!=cudaSuccess){
seterr("exec_pose_lma_dp_step: chain HtoD failed"); return -4; }
CUfunction dp=getfun(p->mod,"pose_lma_dp_step");
if(!dp){ seterr("pose_lma_dp_step missing (DP kernel not in module)"); return -5; }
void* da[]={ &p->gpu_pose_dp_chain };
if(launch1(dp,1,1,1,1,1,1,da,"pose_lma_dp_step")) return -6;
if(cudaMemcpy(result,p->gpu_pose_lma_result,
POSE_LMA_RESIDENT_RESULT*sizeof(float),cudaMemcpyDeviceToHost)!=cudaSuccess){
seterr("exec_pose_lma_dp_step: result DtoH failed"); return -7; }
if(cudaMemcpy(&p->pose_current_set,p->gpu_pose_current_set,sizeof(int),
cudaMemcpyDeviceToHost)!=cudaSuccess){
seterr("exec_pose_lma_dp_step: current_set DtoH failed"); return -7; }
return 0;
}
int tp_proc_exec_pose_lma_resident_step(TpProc* p,
float lambda,
const float* weights,
......@@ -2105,6 +2167,7 @@ void tp_proc_destroy(TpProc* p){
cudaFree(p->gpu_pose_lma_candidate_jt); cudaFree(p->gpu_pose_lma_candidate_ymfx);
cudaFree(p->gpu_pose_lma_candidate_partials); // By Codex on 07/15/2026
cudaFree(p->gpu_pose_lma_prep); // By Claude on 07/17/2026
cudaFree(p->gpu_pose_dp_chain); // By Claude on 07/16/2026 (DP spike)
delete p;
}
......
......@@ -955,6 +955,68 @@ extern "C" __global__ void pose_lma_prepare_finish(
prep_result[POSE_LMA_PREP_VALID] = (isfinite(s) && (full > 0.0f)) ? 1.0f : 0.0f;
}
// ── DP spike: the resident step as ONE device-side chain ─────────────────────
// See tp_lma.h. NVRTC-only: the production JIT path (--extensible-whole-program
// + libcudadevrt link) is CDP2-capable; direct nvcc test builds are not -rdc
// and must not see device-side launches. By Claude on 07/16/2026.
#ifdef __CUDACC_RTC__
// Candidate pose-vector build: ref rows copied from the CURRENT set (pose-
// independent), scene rows overwritten with the solved candidate (result[16..18]).
// Replaces the two host cudaMemcpyAsync of the host-driven step.
extern "C" __global__ void pose_lma_dp_candidate_vector(
const float * cur_vectors,
const float * result,
float * cand_vectors)
{
if ((blockIdx.x != 0) || (threadIdx.x != 0)) return;
for (int i = 0; i < 4 * POSE_NUM_PARAMS; i++) {
cand_vectors[i] = cur_vectors[i];
}
for (int par = 0; par < POSE_NUM_PARAMS; par++) {
cand_vectors[3 * POSE_NUM_PARAMS + par] = result[16 + par];
}
}
#ifndef cudaStreamTailLaunch
#define cudaStreamTailLaunch ((cudaStream_t)0x3) // CDP2, cuda_device_runtime_api.h
#endif
extern "C" __global__ void pose_lma_dp_step(const PoseDpChain * c)
{
if ((blockIdx.x != 0) || (threadIdx.x != 0)) return;
const int pose_threads = 256;
const int pose_blocks = (c->num_tiles + pose_threads - 1) / pose_threads;
const float * candidate_vector = c->cand_vectors + 3 * POSE_NUM_PARAMS;
// All six stages go into THIS grid's tail stream: they start after this
// parent exits and run serialized in enqueue order - the whole step with
// zero host round trips.
pose_lma_prepare_products<<<c->num_partials, POSE_LMA_REDUCE_THREADS, 0, cudaStreamTailLaunch>>>(
c->num_tiles, c->cur_fx, c->cur_jt, c->cur_valid,
c->y, c->weights, c->eigen, c->vector,
c->jt_out, c->ymfx_out, c->partials);
pose_lma_reduce_step<<<1, 1, 0, cudaStreamTailLaunch>>>(
c->num_partials, c->lambda, c->pure_weight,
c->partials, c->vector, c->result);
pose_lma_dp_candidate_vector<<<1, 1, 0, cudaStreamTailLaunch>>>(
c->cur_vectors, c->result, c->cand_vectors);
pose_fx_jacobian<<<pose_blocks, pose_threads, 0, cudaStreamTailLaunch>>>(
c->num_tiles, 1, c->cameras,
c->ref_radial, c->ref_rbr, c->ref_ers,
c->scene_radial, c->scene_rbr, c->scene_ers,
c->cand_vectors, c->centers, c->selection,
c->cand_fx, c->cand_jt, c->cand_valid);
pose_lma_prepare_products<<<c->num_partials, POSE_LMA_REDUCE_THREADS, 0, cudaStreamTailLaunch>>>(
c->num_tiles, c->cand_fx, c->cand_jt, c->cand_valid,
c->y, c->weights, c->eigen, candidate_vector,
c->cand_jt_out, c->cand_ymfx_out, c->cand_partials);
pose_lma_reduce_rms<<<1, 1, 0, cudaStreamTailLaunch>>>(
c->num_partials, c->pure_weight, c->rms_diff,
c->cand_partials, c->result, c->current_set);
}
#endif // __CUDACC_RTC__
extern "C" __global__ void lma_normal_products(
int num_params,
int num_values,
......
......@@ -194,6 +194,37 @@ extern "C" __global__ void pose_lma_prepare_finish(
float * weights,
float * prep_result); // [POSE_LMA_PREP_RESULT]
// ── DP spike (design 2026-07-16, the DP endgame seed) ────────────────────────
// One device-side parent enqueues the WHOLE resident step into its tail-launch
// stream (CDP2: tail launches from one thread serialize in enqueue order after
// the parent exits) - no intermediate CPU-GPU synchronization. The payload
// kernels are the UNMODIFIED validated ones; the only new device work is the
// 15-float candidate-vector build that used to be two host cudaMemcpyAsync.
// NVRTC-only (#ifdef __CUDACC_RTC__ in tp_lma.cu): the production module
// compiles --extensible-whole-program and links libcudadevrt, which is exactly
// the CDP2 requirement; direct nvcc test builds stay non-rdc and bit-stable.
// By Claude on 07/16/2026.
typedef struct {
int num_tiles;
int num_partials;
float lambda;
float pure_weight;
float rms_diff;
const PoseCameraMeta * cameras;
const float * ref_radial; const float * ref_rbr; const float * ref_ers;
const float * scene_radial;const float * scene_rbr;const float * scene_ers;
const float * centers; const unsigned char * selection;
const float * cur_vectors; const float * cur_fx;
const float * cur_jt; const unsigned char * cur_valid;
float * cand_vectors; float * cand_fx; float * cand_jt; unsigned char * cand_valid;
const float * y; const float * weights; const float * eigen;
const float * vector; // current 3-parameter vector (gpu_pose_lma_vector)
float * jt_out; float * ymfx_out; float * partials;
float * cand_jt_out; float * cand_ymfx_out; float * cand_partials;
float * result; // [POSE_LMA_RESIDENT_RESULT]
int * current_set; // device-authoritative accept flip (rung B)
} PoseDpChain;
extern "C" __global__ void lma_normal_products(
int num_params,
int num_values,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment