Skip to content
Merged
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
1503aa8
init commit
cascade812 Jan 29, 2026
aba2525
first version works
cascade812 Feb 3, 2026
a070a1f
implement ngram using sa
cascade812 Feb 5, 2026
84c8963
pre-commit
cascade812 Feb 5, 2026
fb6da64
remove python fallback and update ngram test
cascade812 Feb 5, 2026
7274a59
remove 3 deprecated parameters
cascade812 Feb 5, 2026
be5be52
one model for ngram
cascade812 Feb 9, 2026
40ffa20
dynamic seq length
cascade812 Feb 10, 2026
1412a49
precommit
cascade812 Feb 10, 2026
e1771d1
remove unnecessary files
cascade812 Feb 10, 2026
82f835b
remove redundancy in sa manager and refactor mtp
cascade812 Feb 10, 2026
c4aad50
merge main branch
cascade812 Feb 11, 2026
39502e1
address comments
cascade812 Feb 12, 2026
5415e1a
revert remove 3 dreprecated parameters
cascade812 Feb 12, 2026
c0c1631
split sa as standalone technique
cascade812 Feb 12, 2026
294eaab
update tests
cascade812 Feb 13, 2026
a3c596e
restore ngram related
cascade812 Feb 13, 2026
60c714c
pre-commit
cascade812 Feb 13, 2026
1dbf519
address comments
cascade812 Feb 18, 2026
097e769
remove SampleStateTensorsMTP
cascade812 Feb 19, 2026
b24e159
fix test error
cascade812 Feb 19, 2026
0cedc79
Merge branch 'main' into guiju/at
cascade812 Feb 19, 2026
d8f449e
fix failed test after merge
cascade812 Feb 20, 2026
35f2851
Merge branch 'main' into guiju/at
cascade812 Feb 20, 2026
0260785
fix merge issue
cascade812 Feb 21, 2026
448733a
fix failed test
cascade812 Feb 22, 2026
88a0cc6
fix illegal memory access
cascade812 Feb 24, 2026
d35a301
add accuracy tests
cascade812 Feb 24, 2026
20529dd
merge branch main
cascade812 Feb 24, 2026
2ee99e8
Merge main
cascade812 Feb 27, 2026
ec4eb98
pre-commit
cascade812 Feb 28, 2026
daf4bd4
fix merge error
cascade812 Mar 2, 2026
8da1525
Merge branch 'main' into guiju/at
cascade812 Mar 2, 2026
8743318
update license
cascade812 Mar 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1030,6 +1030,7 @@ common-files: &common_files |
tests/unittest/_torch/speculative/test_kv_cache_reuse.py |
tests/unittest/_torch/speculative/test_mtp.py |
tests/unittest/_torch/speculative/test_ngram.py |
tests/unittest/_torch/speculative/test_sa.py |
tests/unittest/_torch/speculative/test_save_state.py |
tests/unittest/_torch/speculative/test_spec_gate.py |
tests/unittest/_torch/speculative/test_torch_rejection_sampling.py |
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,294 @@
/*
* SPDX-FileCopyrightText: Copyright (c) 2022-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Adapted from Baseten's sa_spec library (Apache-2.0)
* https://github.com/basetenlabs/sa_spec
*/

#pragma once

#include <cassert>
#include <cstddef>
#include <cstring>
#include <type_traits>

#include "saCudaCallable.h"

#include "tensorrt_llm/common/config.h"

TRTLLM_NAMESPACE_BEGIN

namespace kernels::speculative_decoding::suffix_automaton
{

/**
* @brief A fixed-capacity buffer that uses external memory (pointer-based).
*
* This is a view into externally-managed memory. The buffer does not own
* the memory and does not perform any allocation/deallocation.
*
* This design enables:
* - Runtime-configurable capacity (no compile-time template parameter)
* - Trivially copyable (can be memcpy'd between host and GPU)
* - CUDA graph compatible (fixed memory addresses after initialization)
*
* @tparam T Element type (must be trivially copyable)
* @tparam IndexT Index type (default size_t)
*/
template <typename T, typename IndexT = size_t>
struct SABuffer
{
T* mData{nullptr};
size_t mCapacity{0};

T const& at(IndexT, IndexT) const = delete;
T& at(IndexT, IndexT) = delete;

SABuffer() = default;

SA_CUDA_CALLABLE void init(T* data, size_t capacity)
{
mData = data;
mCapacity = capacity;
}

SA_CUDA_CALLABLE T const& at(IndexT row) const
{
assert(static_cast<size_t>(+row) < mCapacity);
return mData[+row];
}

SA_CUDA_CALLABLE T& at(IndexT row)
{
assert(static_cast<size_t>(+row) < mCapacity);
return mData[+row];
}

struct Iterator
{
SABuffer const& buffer;
IndexT index;

SA_CUDA_CALLABLE Iterator(SABuffer const& buffer, IndexT index)
: buffer(buffer)
, index(index)
{
}

SA_CUDA_CALLABLE T const& operator*() const
{
return buffer.at(index);
}

SA_CUDA_CALLABLE Iterator& operator++()
{
index = IndexT(+index + 1);
return *this;
}

SA_CUDA_CALLABLE bool operator==(Iterator const& other) const
{
return index == other.index;
}

SA_CUDA_CALLABLE bool operator!=(Iterator const& other) const
{
return index != other.index;
}
};

SA_CUDA_CALLABLE Iterator begin() const
{
return Iterator(*this, IndexT(0));
}

SA_CUDA_CALLABLE Iterator end() const
{
return Iterator(*this, IndexT(mCapacity));
}

SA_CUDA_CALLABLE size_t size() const
{
return mCapacity;
}

SA_CUDA_CALLABLE size_t capacity() const
{
return mCapacity;
}

void clear()
{
if (mData && mCapacity > 0)
{
memset(static_cast<void*>(mData), 0, mCapacity * sizeof(T));
}
}

SA_CUDA_CALLABLE T* data()
{
return mData;
}

SA_CUDA_CALLABLE T const* data() const
{
return mData;
}

/**
* @brief Relocate the data pointer by a given delta.
*
* Used when copying between host and GPU memory to adjust pointers.
*/
void relocate(ptrdiff_t delta)
{
if (mData)
{
mData = reinterpret_cast<T*>(reinterpret_cast<uint8_t*>(mData) + delta);
}
}

static_assert(std::is_trivially_copyable<T>::value, "T must be trivially copyable");
};

/**
* @brief A dynamic buffer with runtime-configurable capacity using external memory.
*
* Like SABuffer, but tracks current length separately from capacity.
* Supports push/pop operations up to the capacity limit.
*
* @tparam T Element type (must be trivially copyable)
* @tparam IndexT Index type (default size_t)
*/
template <typename T, typename IndexT = size_t>
struct SADynamicBuffer
{
T* mData{nullptr};
size_t mCapacity{0};
IndexT mLength{0};

SADynamicBuffer() = default;

SA_CUDA_CALLABLE void init(T* data, size_t capacity)
{
mData = data;
mCapacity = capacity;
mLength = IndexT(0);
}

SA_CUDA_CALLABLE void clear()
{
mLength = IndexT(0);
}

SA_CUDA_CALLABLE IndexT size() const
{
return mLength;
}

SA_CUDA_CALLABLE size_t capacity() const
{
return mCapacity;
}

SA_CUDA_CALLABLE bool empty() const
{
return +size() == 0;
}

SA_CUDA_CALLABLE void extend(size_t n)
{
mLength = IndexT(+mLength + n);
assert(static_cast<size_t>(+mLength) <= mCapacity);
}

SA_CUDA_CALLABLE T& pushBack(T const& value)
{
assert(static_cast<size_t>(+mLength) < mCapacity);

T& result = mData[+mLength];
result = value;
mLength = IndexT(+mLength + 1);
return result;
}

SA_CUDA_CALLABLE T& pushBack(T&& value)
{
assert(static_cast<size_t>(+mLength) < mCapacity);
T& result = mData[+mLength];
result = std::move(value);
mLength = IndexT(+mLength + 1);
return result;
}

SA_CUDA_CALLABLE T& popBack()
{
assert(!empty());
T& result = mData[+mLength - 1];
mLength = IndexT(+mLength - 1);
return result;
}

SA_CUDA_CALLABLE T const& at(IndexT row) const
{
assert(row < mLength);
return mData[+row];
}

SA_CUDA_CALLABLE T& at(IndexT row)
{
assert(row < mLength);
return mData[+row];
}

SA_CUDA_CALLABLE T* data()
{
return mData;
}

SA_CUDA_CALLABLE T const* data() const
{
return mData;
}

SA_CUDA_CALLABLE bool hasCapacity() const
{
return static_cast<size_t>(+mLength) < mCapacity;
}

/**
* @brief Relocate the data pointer by a given delta.
*
* Used when copying between host and GPU memory to adjust pointers.
*/
void relocate(ptrdiff_t delta)
{
if (mData)
{
mData = reinterpret_cast<T*>(reinterpret_cast<uint8_t*>(mData) + delta);
}
}

static_assert(std::is_trivially_copyable<T>::value, "T must be trivially copyable");
};

// Verify that our buffer types are trivially copyable (required for GPU memcpy)
static_assert(std::is_trivially_copyable<SABuffer<int>>::value, "SABuffer must be trivially copyable");
static_assert(std::is_trivially_copyable<SADynamicBuffer<int>>::value, "SADynamicBuffer must be trivially copyable");

} // namespace kernels::speculative_decoding::suffix_automaton

TRTLLM_NAMESPACE_END
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* SPDX-FileCopyrightText: Copyright (c) 2022-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Adapted from Baseten's sa_spec library (Apache-2.0)
* https://github.com/basetenlabs/sa_spec
*/

#pragma once

#ifdef __CUDACC__
#include <cuda_runtime.h>
#define SA_CUDA_CALLABLE __host__ __device__ __forceinline__
#else
#define SA_CUDA_CALLABLE
// Provide a placeholder type for cudaStream_t when not compiling with CUDA.
// Only define if not already defined to avoid conflicts with cuda_runtime_api.h.
#if !defined(cudaStream_t)
#define cudaStream_t int
Comment thread
cascade812 marked this conversation as resolved.
#endif
#endif // __CUDACC__
Loading