@@ -1141,33 +1141,36 @@ __global__ void merge_chunks_kernel(
11411141 // Step 2: Intra-warp reduction via warp shuffle
11421142 // Each warp has 2 ty values: ty_low at lanes 0-15, ty_high at lanes 16-31
11431143 // Merge ty_high into ty_low using shuffle
1144- {
1145- // Determine the partner ty in the same warp
1146- // ty_low = ty & ~1, ty_high = ty | 1
1147- const int partner_lane = lane_id ^ 16 ; // flip bit 4 to swap low/high ty
1148- const float m_partner = __shfl_sync (0xffffffff , m, partner_lane);
1149- const float d_partner = __shfl_sync (0xffffffff , d, partner_lane);
1150- LoadT partner_vec;
1144+ const int partner_lane = lane_id ^ 16 ; // flip bit 4 to swap low/high ty
1145+ const float m_partner = __shfl_sync (0xffffffff , m, partner_lane);
1146+ const float d_partner = __shfl_sync (0xffffffff , d, partner_lane);
1147+ // Pack adjacent 16-bit pairs into 32-bit for efficient shuffle.
1148+ // AlignedVector alignment >= 4 bytes, so uint32 reinterpret is safe
1149+ // — no OOB read, no type confusion. This halves shuffle count vs
1150+ // per-element memcpy for bf16/fp16.
1151+ constexpr int PACKED_SIZE = vec_size * sizeof (T) / sizeof (unsigned );
1152+ const unsigned * packed_res = reinterpret_cast <const unsigned *>(&res_vec);
1153+ unsigned packed_partner[PACKED_SIZE ];
1154+ #pragma unroll
1155+ for (int j = 0 ; j < PACKED_SIZE ; j++) {
1156+ packed_partner[j] = __shfl_sync (0xffffffff , packed_res[j], partner_lane);
1157+ }
1158+ LoadT partner_vec;
1159+ memcpy (&partner_vec, packed_partner, sizeof (partner_vec));
1160+
1161+ // Merge partner into self (only the "low ty" keeps the result)
1162+ float m_new = max (m, m_partner);
1163+ const float scale1 = __expf (m - m_new);
1164+ const float scale2 = __expf (m_partner - m_new);
1165+ float d_new = d * scale1 + d_partner * scale2;
1166+ if ((ty & 1 ) == 0 ) { // low ty keeps merged result
1167+ m = m_new;
1168+ d = d_new;
1169+ const T scale1_T = static_cast <T>(scale1);
1170+ const T scale2_T = static_cast <T>(scale2);
11511171#pragma unroll
11521172 for (int j = 0 ; j < vec_size; j++) {
1153- partner_vec[j] = __shfl_sync (
1154- 0xffffffff , reinterpret_cast <unsigned &>(res_vec[j]), partner_lane);
1155- }
1156-
1157- // Merge partner into self (only the "low ty" keeps the result)
1158- float m_new = max (m, m_partner);
1159- const float scale1 = __expf (m - m_new);
1160- const float scale2 = __expf (m_partner - m_new);
1161- float d_new = d * scale1 + d_partner * scale2;
1162- if ((ty & 1 ) == 0 ) { // low ty keeps merged result
1163- m = m_new;
1164- d = d_new;
1165- const T scale1_T = static_cast <T>(scale1);
1166- const T scale2_T = static_cast <T>(scale2);
1167- #pragma unroll
1168- for (int j = 0 ; j < vec_size; j++) {
1169- res_vec[j] = res_vec[j] * scale1_T + partner_vec[j] * scale2_T;
1170- }
1173+ res_vec[j] = res_vec[j] * scale1_T + partner_vec[j] * scale2_T;
11711174 }
11721175 }
11731176
0 commit comments