Skip to content

Commit 03151f6

Browse files
committed
[Python STABLE ABI] Port cuctc extension to libtorch stable ABI
1 parent c9b37e9 commit 03151f6

4 files changed

Lines changed: 91 additions & 73 deletions

File tree

src/libtorchaudio/cuctc/CMakeLists.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,10 @@ torchaudio_library(
3434

3535
if (BUILD_TORCHAUDIO_PYTHON_EXTENSION)
3636
torchaudio_extension(
37-
pybind11_prefixctc
37+
torchaudio_prefixctc
3838
src/python_binding.cpp
3939
"${CMAKE_CURRENT_SOURCE_DIR}"
40-
"libctc_prefix_decoder;${additional_libs}"
40+
"libctc_prefix_decoder;torch;${additional_libs}"
4141
""
4242
)
4343
endif()

src/libtorchaudio/cuctc/src/python_binding.cpp

Lines changed: 72 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@
2424
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
2525
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2626

27+
#include <torch/csrc/stable/library.h>
28+
#include <torch/csrc/stable/ops.h>
29+
#include <torch/csrc/stable/tensor.h>
30+
#include <torch/headeronly/core/ScalarType.h>
31+
2732
// Stuff in third_party/ is considered non-ABI-stable
2833
// (https://github.com/pytorch/pytorch/issues/169893), but
2934
// it is safe to temporarily disable TORCH_TARGET_VERSION for pybind11
@@ -44,23 +49,43 @@
4449
#include <vector>
4550
#include "include/ctc_prefix_decoder.h"
4651

47-
namespace py = pybind11;
52+
using torch::headeronly::ScalarType;
53+
using torch::stable::Tensor;
4854

49-
std::tuple<size_t, std::vector<std::vector<std::pair<float, std::vector<int>>>>>
50-
ctc_prefix_decoder_batch_wrapper(
55+
std::tuple<size_t, Tensor, Tensor, Tensor> ctc_prefix_decoder_batch_wrapper(
5156
std::uintptr_t n_inter_data,
52-
std::uintptr_t buff_ptr,
53-
size_t buff_size,
54-
std::uintptr_t pp,
55-
std::uintptr_t seq_len_ptr,
56-
const std::vector<int>& pp_sizes,
57-
const std::vector<int>& pp_strides,
57+
Tensor buff,
58+
Tensor log_prob,
59+
Tensor encoder_out_lens,
5860
int beam,
5961
int blid,
6062
int spid,
6163
float thresold) {
62-
using SCORE_TYPE =
63-
std::vector<std::vector<std::pair<float, std::vector<int>>>>;
64+
STD_TORCH_CHECK(
65+
encoder_out_lens.scalar_type() == ScalarType::Int,
66+
"encoder_out_lens must be torch.int32");
67+
STD_TORCH_CHECK(
68+
log_prob.scalar_type() == ScalarType::Float,
69+
"log_prob must be torch.float32");
70+
STD_TORCH_CHECK(log_prob.is_cuda(), "log_prob must be cuda tensor");
71+
STD_TORCH_CHECK(
72+
encoder_out_lens.is_cuda(), "encoder_out_lens must be cuda tensor");
73+
STD_TORCH_CHECK(
74+
log_prob.get_device_index() == encoder_out_lens.get_device_index(),
75+
"log_prob and encoder_out_lens must be on the same device");
76+
STD_TORCH_CHECK(log_prob.is_contiguous(), "log_prob must be contiguous");
77+
STD_TORCH_CHECK(
78+
encoder_out_lens.is_contiguous(), "encoder_out_lens must be contiguous");
79+
if (buff.numel() > 0) {
80+
STD_TORCH_CHECK(buff.is_cuda(), "buff must be cuda tensor");
81+
STD_TORCH_CHECK(
82+
log_prob.get_device_index() == buff.get_device_index(),
83+
"log_prob and buff must be on the same device");
84+
}
85+
auto pp_sizes_ = log_prob.sizes();
86+
auto pp_strides_ = log_prob.strides();
87+
std::vector<int> pp_sizes(std::begin(pp_sizes_), std::end(pp_sizes_));
88+
std::vector<int> pp_strides(std::begin(pp_strides_), std::end(pp_strides_));
6489
cu_ctc::InternalData* inter_data = (cu_ctc::InternalData*)(n_inter_data);
6590
auto [require_size, max_select_seq_len] =
6691
cu_ctc::calculate_require_buff_and_init_internal_data(
@@ -69,46 +94,52 @@ ctc_prefix_decoder_batch_wrapper(
6994
pp_sizes[1],
7095
pp_sizes[2],
7196
beam,
72-
buff_ptr,
73-
buff_size,
74-
(float*)pp,
75-
(int*)seq_len_ptr,
97+
reinterpret_cast<std::uintptr_t>(buff.data_ptr()),
98+
buff.size(0),
99+
log_prob.mutable_data_ptr<float>(),
100+
encoder_out_lens.mutable_data_ptr<int>(),
76101
pp_sizes,
77102
pp_strides,
78103
blid,
79104
thresold);
80105
if (require_size > 0) {
81-
return std::make_tuple(require_size, SCORE_TYPE{});
106+
return std::make_tuple(require_size, Tensor{}, Tensor{}, Tensor{});
82107
}
83108
int batch_size = pp_sizes[0];
84-
std::vector<int> list_data(batch_size * beam * max_select_seq_len);
85-
std::vector<int> len_data(batch_size * beam);
86-
std::vector<float> score(batch_size * beam);
109+
Tensor list_data = torch::stable::empty(
110+
{batch_size, beam, max_select_seq_len}, ScalarType::Int);
111+
Tensor len_data = torch::stable::empty({batch_size, beam}, ScalarType::Int);
112+
Tensor score = torch::stable::empty({batch_size, beam}, ScalarType::Float);
87113
cu_ctc::ctc_beam_search_decoder_batch_gpu(
88-
inter_data, blid, spid, list_data.data(), len_data.data(), score.data());
89-
SCORE_TYPE score_hyps{};
90-
score_hyps.reserve(batch_size);
91-
for (int b = 0; b < batch_size; b++) {
92-
score_hyps.push_back(std::vector<std::pair<float, std::vector<int>>>{});
93-
score_hyps.back().reserve(beam);
94-
for (int beam_id = 0; beam_id < beam; beam_id++) {
95-
int len = len_data[b * beam + beam_id];
96-
int offset = b * beam * max_select_seq_len + beam_id * max_select_seq_len;
97-
std::vector<int> clist(
98-
list_data.data() + offset, list_data.data() + offset + len);
99-
score_hyps.back().push_back(
100-
std::pair{score[b * beam + beam_id], std::move(clist)});
101-
}
102-
}
103-
return std::make_tuple(require_size, std::move(score_hyps));
114+
inter_data,
115+
blid,
116+
spid,
117+
list_data.mutable_data_ptr<int>(),
118+
len_data.mutable_data_ptr<int>(),
119+
score.mutable_data_ptr<float>());
120+
return std::make_tuple(
121+
require_size,
122+
std::move(list_data),
123+
std::move(len_data),
124+
std::move(score));
104125
}
105126

106-
PYBIND11_MODULE(pybind11_prefixctc, m) {
107-
m.doc() = "none";
127+
PYBIND11_MODULE(torchaudio_prefixctc, m) {}
128+
129+
STABLE_TORCH_LIBRARY_FRAGMENT(torchaudio_prefixctc, m) {
108130
m.def(
131+
"ctc_beam_search_decoder_batch_gpu_v2(int interal_data_ptr, Tensor memory, Tensor log_prob, Tensor encoder_out_lens, int beam, int blid, int spid, float thresold) -> (int, Tensor, Tensor, Tensor)");
132+
m.def("prefixCTC_alloc(int stream) -> int");
133+
m.def("prefixCTC_free(int interal_data_ptr) -> ()");
134+
}
135+
136+
STABLE_TORCH_LIBRARY_IMPL(torchaudio_prefixctc, CompositeExplicitAutograd, m) {
137+
m.impl("prefixCTC_alloc", TORCH_BOX(&cu_ctc::prefixCTC_alloc));
138+
m.impl("prefixCTC_free", TORCH_BOX(&cu_ctc::prefixCTC_free));
139+
}
140+
141+
STABLE_TORCH_LIBRARY_IMPL(torchaudio_prefixctc, CUDA, m) {
142+
m.impl(
109143
"ctc_beam_search_decoder_batch_gpu_v2",
110-
&ctc_prefix_decoder_batch_wrapper,
111-
"ctc prefix decoder v2 computing on GPU");
112-
m.def("prefixCTC_alloc", &cu_ctc::prefixCTC_alloc, "allocate internal data");
113-
m.def("prefixCTC_free", &cu_ctc::prefixCTC_free, "free internal data");
144+
TORCH_BOX(&ctc_prefix_decoder_batch_wrapper));
114145
}

src/torchaudio/models/decoder/_cuda_ctc_decoder.py

Lines changed: 16 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88
import torchaudio
99

1010
torchaudio._extension._load_lib("libctc_prefix_decoder")
11-
import torchaudio.lib.pybind11_prefixctc as cuctc
11+
import torchaudio.lib.torchaudio_prefixctc
12+
13+
cuctc = torch.ops.torchaudio_prefixctc
1214

1315

1416
__all__ = ["CUCTCHypothesis", "CUCTCDecoder", "cuda_ctc_decoder"]
@@ -102,51 +104,36 @@ def __call__(self, log_prob: torch.Tensor, encoder_out_lens: torch.Tensor):
102104
List[List[CUCTCHypothesis]]:
103105
List of sorted best hypotheses for each audio sequence in the batch.
104106
"""
105-
if not encoder_out_lens.dtype == torch.int32:
106-
raise AssertionError("encoder_out_lens must be torch.int32")
107-
if not log_prob.dtype == torch.float32:
108-
raise AssertionError("log_prob must be torch.float32")
109-
if not (log_prob.is_cuda and encoder_out_lens.is_cuda):
110-
raise AssertionError("inputs must be cuda tensors")
111-
if not (log_prob.is_contiguous() and encoder_out_lens.is_contiguous()):
112-
raise AssertionError("input tensors must be contiguous")
113-
required_size, score_hyps = cuctc.ctc_beam_search_decoder_batch_gpu_v2(
107+
required_size, list_data, len_data, score = cuctc.ctc_beam_search_decoder_batch_gpu_v2(
114108
self.internal_data,
115-
self.memory.data_ptr(),
116-
self.memory.size(0),
117-
log_prob.data_ptr(),
118-
encoder_out_lens.data_ptr(),
119-
log_prob.size(),
120-
log_prob.stride(),
109+
self.memory,
110+
log_prob,
111+
encoder_out_lens,
121112
self.beam_size,
122113
self.blank_id,
123114
self.space_id,
124115
self.blank_skip_threshold,
125116
)
126117
if required_size > 0:
127-
self.memory = torch.empty(required_size, dtype=torch.int8, device=log_prob.device).contiguous()
128-
_, score_hyps = cuctc.ctc_beam_search_decoder_batch_gpu_v2(
118+
self.memory = torch.empty(required_size, dtype=torch.int8, device=log_prob.device)
119+
required_size, list_data, len_data, score = cuctc.ctc_beam_search_decoder_batch_gpu_v2(
129120
self.internal_data,
130-
self.memory.data_ptr(),
131-
self.memory.size(0),
132-
log_prob.data_ptr(),
133-
encoder_out_lens.data_ptr(),
134-
log_prob.size(),
135-
log_prob.stride(),
121+
self.memory,
122+
log_prob,
123+
encoder_out_lens,
136124
self.beam_size,
137125
self.blank_id,
138126
self.space_id,
139127
self.blank_skip_threshold,
140128
)
141-
batch_size = len(score_hyps)
142129
hypos = []
143-
for i in range(batch_size):
130+
for i in range(log_prob.shape[0]):
144131
hypos.append(
145132
[
146133
CUCTCHypothesis(
147-
tokens=score_hyps[i][j][1],
148-
words=[self.vocab_list[word_id] for word_id in score_hyps[i][j][1]],
149-
score=score_hyps[i][j][0],
134+
tokens=list_data[i][j][: len_data[i][j]],
135+
words=[self.vocab_list[word_id] for word_id in list_data[i][j][: len_data[i][j]]],
136+
score=score[i][j],
150137
)
151138
for j in range(self.nbest)
152139
]

tools/setup_helpers/extension.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ def get_ext_modules():
5252
modules.extend(
5353
[
5454
Extension(name="torchaudio.lib.libctc_prefix_decoder", sources=[]),
55-
Extension(name="torchaudio.lib.pybind11_prefixctc", sources=[]),
55+
Extension(name="torchaudio.lib.torchaudio_prefixctc", sources=[]),
5656
]
5757
)
5858
return modules

0 commit comments

Comments
 (0)