@@ -208,6 +208,7 @@ __launch_bounds__(BLOCK_SIZE) __global__ void beamStage3Kernel(
208208 __shared__ float smemCumLogProbs[PBM ];
209209 __shared__ int smemSeqLen[PBM ];
210210 __shared__ KVPair smemTopKV[(IS_V2 ) ? 1 : PBM * 2 ]; // Just a placeholder in V2 workflow
211+ __shared__ int smemNBeamForNextStep;
211212
212213 if (bh.numBeamsCBA != nullptr )
213214 {
@@ -217,14 +218,11 @@ __launch_bounds__(BLOCK_SIZE) __global__ void beamStage3Kernel(
217218 // Initialize worst score in the first call
218219 bh.minNormedScoresCBA [slot] = 0 .0f ; // logProbs is in range (-inf, 0]
219220 }
220- else if (earlyStopping == 1 && bh.numBeamsCBA [slot] == nBM
221- || earlyStopping != 1 && bh.finished [slot * nBM].isFinished ())
221+ else if (earlyStopping == 1 && bh.numBeamsCBA [slot] >= nBM || earlyStopping != 1 && bh.batchDones [slot])
222222 {
223223 // Condition of early return:
224224 // 1. In EarlyStopping mode, and we have got enough beams
225225 // 2. In NonEarlyStopping mode, and this batch has been marked as done
226- // TODO: improve the condition like below
227- // earlyStopping == 1 && bh.numBeamsCBA[slot] == nBM || earlyStopping != 1 && bh.batchDones[slot]
228226 return ;
229227 }
230228 }
@@ -296,6 +294,11 @@ __launch_bounds__(BLOCK_SIZE) __global__ void beamStage3Kernel(
296294 }
297295 __syncthreads ();
298296
297+ // Timestep at which each next-step destination beam's token is stored in the work tree.
298+ // This is the parent beam's sequence length (the true generation step), which may differ
299+ // from the destination slot's own (possibly stale) length when a finished slot is reused.
300+ __shared__ int smemWriteStep[PBM ];
301+
299302 if (tid == 0 )
300303 {
301304 int nBeamForNextStep{0 };
@@ -324,10 +327,14 @@ __launch_bounds__(BLOCK_SIZE) __global__ void beamStage3Kernel(
324327 {
325328 // Condition of this branch:
326329 // This token is end-token and belongs to top nBM range in Beam search mode
327- int const nSeqLen = bh.sequenceLengths [slot * nBM + i] + 1 - bh.inputLengths [slot * nBM + i];
330+ // Use the actual parent beam index (topId / nV) % nBM, not the candidate rank i,
331+ // to look up the correct sequenceLength and inputLength for length-penalty scoring.
332+ int const parentBeam = (topId / nV) % nBM;
333+ int const nSeqLen
334+ = bh.sequenceLengths [slot * nBM + parentBeam] + 1 - bh.inputLengths [slot * nBM + parentBeam];
328335 float const score = applyLengthPenalty (topLogProb, nSeqLen, lengthPenalty);
329336 int nCBA = bh.numBeamsCBA [slot];
330- if (nCBA = = nBM)
337+ if (nCBA > = nBM)
331338 {
332339 // There are already nBM beams
333340 if (score < bh.minNormedScoresCBA [slot])
@@ -407,13 +414,18 @@ __launch_bounds__(BLOCK_SIZE) __global__ void beamStage3Kernel(
407414 // 1. bh.numBeamsCBA == nullptr && i < nBM, i.e., beam search is disable
408415 // 2. bh.numBeamsCBA != nullptr && i < nBM && isEndToken == false, i.e., add token at the end
409416 // 3. bh.numBeamsCBA != nullptr && i >= nBM && isEndToken == false, i.e., add token at the end
410- int const step = bh.sequenceLengths [slot * nBM + nBeamForNextStep];
417+ // Write at the parent beam's sequence length (the actual generation step),
418+ // not the destination slot's length, which can be stale if the slot was
419+ // previously finished and is now being reused for a new continuation.
420+ int const parentBeam = topId / nV % nBM;
421+ int const step = bh.sequenceLengths [slot * nBM + parentBeam];
422+ smemWriteStep[nBeamForNextStep] = step;
411423 // Copy the selected token to work tree
412424 bh.outputIdsPtr [slot][nBeamForNextStep * nMSL + step] = topId;
413425 if (bh.logProbsTiled != nullptr )
414426 {
415427 int const index = step * nMBS * nBM + slot * nBM + nBeamForNextStep;
416- int const indexBeam = topId / nV % nBM ;
428+ int const indexBeam = parentBeam ;
417429 bh.logProbsTiled [index] = (float ) topLogProb - smemCumLogProbs[indexBeam];
418430 }
419431 bh.cumLogProbs [slot * nBM + nBeamForNextStep] = (float ) topLogProb;
@@ -437,6 +449,7 @@ __launch_bounds__(BLOCK_SIZE) __global__ void beamStage3Kernel(
437449 break ;
438450 }
439451 }
452+ smemNBeamForNextStep = nBeamForNextStep;
440453 }
441454
442455 // Update bh.batchDones
@@ -481,23 +494,40 @@ __launch_bounds__(BLOCK_SIZE) __global__ void beamStage3Kernel(
481494 if (tid < nBMOut)
482495 {
483496 int const indexBatchBeam = slot * nBM + tid;
484- int const step = smemSeqLen[tid];
485- if (!bh.finished [indexBatchBeam].isFinished ())
497+ if (tid < smemNBeamForNextStep)
486498 {
487- smemSeqLen[tid]++;
499+ // This slot received a valid next-step token from the selection phase.
500+ // Use the timestep recorded by the selection phase (the parent beam's length),
501+ // which matches the position where the encoded token was stored.
502+ int const step = smemWriteStep[tid];
503+ int const newId = bh.outputIdsPtr [slot][tid * nMSL + step];
504+ int const newBeamId = (newId / nV) % nBM;
505+ int const newTokenId = newId % nV;
506+ int const indexParentBeam = slot * nBM + newBeamId;
507+ int const parentSeqLen = smemSeqLen[newBeamId];
508+ bh.sequenceLengths [indexBatchBeam] = parentSeqLen + (!bh.finished [indexParentBeam].isFinished () ? 1 : 0 );
509+ if (newTokenId == bh.endIds [slot])
510+ {
511+ bh.finished [indexBatchBeam].setFinishedEOS ();
512+ }
513+ else
514+ {
515+ // Reset any stale finished state: this slot may have been marked finished in a
516+ // previous step and is now reused for a valid non-EOS beam; otherwise it would be
517+ // wrongly skipped downstream.
518+ bh.finished [indexBatchBeam] = FinishedState::empty ();
519+ }
520+ bh.parentIdsPtr [slot][tid * nMSL + step] = newBeamId;
521+ bh.outputIdsPtr [slot][tid * nMSL + step] = newTokenId;
488522 }
489- int const newId = bh.outputIdsPtr [slot][tid * nMSL + step];
490- int const newBeamId = (newId / nV) % nBM;
491- int const newTokenId = newId % nV;
492- bh.sequenceLengths [indexBatchBeam] = smemSeqLen[newBeamId];
493- if (newTokenId == bh.endIds [slot])
523+ else
494524 {
495- bh.finished [indexBatchBeam].setFinishedEOS ();
525+ // No valid next-step token for this slot: all top candidates went to CBA.
526+ // Mark as finished so downstream stages (cache indirection, next decode) skip it.
527+ bh.finished [indexBatchBeam].setFinished ();
496528 }
497- bh.parentIdsPtr [slot][tid * nMSL + step] = newBeamId;
498- bh.outputIdsPtr [slot][tid * nMSL + step] = newTokenId;
499529
500- if ((earlyStopping == 1 ) && (bh.numBeamsCBA != nullptr && bh.numBeamsCBA [slot] = = nBM)
530+ if ((earlyStopping == 1 ) && (bh.numBeamsCBA != nullptr && bh.numBeamsCBA [slot] > = nBM)
501531 || (earlyStopping != 1 ) && bh.batchDones [slot])
502532 {
503533 bh.batchDones [slot] = true ;
@@ -631,7 +661,7 @@ void beamSearchKernelLauncher(
631661 sync_check_cuda_error (stream);
632662
633663 int nThread = min (roundUp (nBMIn * nBMOut * 2 , 32 ), 1024 );
634- addCumLogProbs<<<nBS, nThread, 0 , stream>>>(pStage1LogProbs, bh.cumLogProbs , bh.finished , bh.endIds ,
664+ addCumLogProbs<<<nBS, nThread, 0 , stream>>>(pStage1LogProbs, pStage1Ids, bh.cumLogProbs , bh.finished , bh.endIds ,
635665 bh.diversityRates , bh.batchSlots , nBS, nBMIn, nBMOut, nBM);
636666 sync_check_cuda_error (stream);
637667
0 commit comments