[Perf] Accumulate Conformer attention scores with baddbmm (#55062)

Signed-off-by: levius <2114377220@qq.com>
Co-authored-by: Codex <codex@openai.com>
This commit is contained in:
Sun
2026-09-03 04:17:52 -07:00
committed by GitHub
co-authored by Codex
parent 758c79e1c5
commit 848ab131bc
2 changed files with 124 additions and 3 deletions
@@ -0,0 +1,111 @@
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
import pytest
import torch
from vllm.distributed.parallel_state import (
destroy_distributed_environment,
destroy_model_parallel,
init_distributed_environment,
initialize_model_parallel,
)
from vllm.model_executor.models.conformer_encoder import RelPosMultiHeadAttention
pytestmark = pytest.mark.cpu_test
class _ReferenceRelPosMultiHeadAttention(RelPosMultiHeadAttention):
"""Pre-baddbmm relative attention implementation."""
def forward(
self,
q: torch.Tensor,
k: torch.Tensor,
v: torch.Tensor,
pos_emb: torch.Tensor,
mask: torch.Tensor | None = None,
) -> tuple[torch.Tensor, torch.Tensor]:
batch_size, query_len = q.size(0), q.size(1)
residual = q
q, k, v = self.forward_qkv(q, k, v)
q = q.transpose(1, 2)
position_batch = pos_emb.size(0)
p = self.linear_pos(pos_emb)[0].view(position_batch, -1, self.n_head, self.d_k)
p = p.transpose(1, 2)
q_with_bias_u = (q + self.pos_bias_u).transpose(1, 2)
q_with_bias_v = (q + self.pos_bias_v).transpose(1, 2)
matrix_ac = torch.matmul(q_with_bias_u, k.transpose(-2, -1))
matrix_bd = torch.matmul(q_with_bias_v, p.transpose(-2, -1))
matrix_bd = self._reference_rel_shift(matrix_bd)
attention_scores = matrix_ac + matrix_bd
attention_scores.mul_(self.scale)
output, attention = self.forward_attention(attention_scores, v, mask=mask)
output = self.forward_output(output, residual, batch_size, query_len)
return output, attention
@staticmethod
def _reference_rel_shift(x: torch.Tensor) -> torch.Tensor:
batch, heads, query_len, position_len = x.shape
zero_pad = torch.zeros(
(batch, heads, query_len, 1), device=x.device, dtype=x.dtype
)
x = torch.cat([zero_pad, x], dim=-1)
x = x.view(batch, heads, position_len + 1, query_len)
x = x[:, :, 1:].view(batch, heads, query_len, position_len)
return x[:, :, :, : position_len // 2 + 1]
@pytest.fixture
def tp1_cpu(tmp_path, default_vllm_config):
"""Initialize a temporary single-rank CPU tensor-parallel group."""
try:
init_distributed_environment(
world_size=1,
rank=0,
distributed_init_method=f"file://{tmp_path / 'distributed'}",
local_rank=0,
backend="gloo",
)
initialize_model_parallel(tensor_model_parallel_size=1)
yield
finally:
destroy_model_parallel()
destroy_distributed_environment()
@pytest.mark.parametrize("sequence", [2, 8])
def test_relative_attention_matches_reference(tp1_cpu, sequence: int) -> None:
torch.manual_seed(7)
batch, heads, model_dim = 2, 4, 32
optimized = RelPosMultiHeadAttention(heads, model_dim)
reference = _ReferenceRelPosMultiHeadAttention(heads, model_dim)
with torch.no_grad():
for parameter in optimized.parameters():
parameter.uniform_(-0.2, 0.2)
reference.load_state_dict(optimized.state_dict())
query = torch.randn(batch, sequence, model_dim)
key = torch.randn(batch, sequence, model_dim)
value = torch.randn(batch, sequence, model_dim)
pos_emb = torch.randn(1, 2 * sequence - 1, model_dim)
mask = torch.ones(batch, 1, sequence, dtype=torch.bool)
mask[:, :, -1] = False
expected_output, expected_attention = reference(
query, key, value, pos_emb, mask=mask
)
output, attention = optimized(query, key, value, pos_emb, mask=mask)
torch.testing.assert_close(output, expected_output)
torch.testing.assert_close(attention, expected_attention)
assert output.shape == (batch, sequence, model_dim)
assert attention.shape == (batch, heads, sequence, sequence)
assert output.is_contiguous()
assert attention.is_contiguous()
@@ -206,12 +206,22 @@ class RelPosMultiHeadAttention(EncoderMultiHeadAttention):
q_with_bias_u = (q + self.pos_bias_u).transpose(1, 2)
q_with_bias_v = (q + self.pos_bias_v).transpose(1, 2)
matrix_ac = torch.matmul(q_with_bias_u, k.transpose(-2, -1))
matrix_bd = torch.matmul(q_with_bias_v, p.transpose(-2, -1))
matrix_bd = self._rel_shift(matrix_bd)
attn_scores = matrix_ac + matrix_bd
attn_scores.mul_(self.scale)
batch_size, num_heads, query_len, key_len = matrix_bd.shape
attn_scores = torch.baddbmm(
matrix_bd.reshape(-1, query_len, key_len),
q_with_bias_u.reshape(-1, query_len, self.d_k),
k.transpose(-2, -1).reshape(-1, self.d_k, key_len),
beta=self.scale,
alpha=self.scale,
).view(
batch_size,
num_heads,
query_len,
key_len,
)
output, attn = self.forward_attention(attn_scores, v, mask=mask)
output = self.forward_output(output, residual, sz_b, len_q)