项目文件夹

文件
wehub-resource-sync 59a0a3844c
PR Test AMD / cancel-on-close (push) Has been skipped
PR Test NVIDIA ARM / scan (push) Has been skipped
PR Test NVIDIA / cancel-on-close (push) Has been skipped
PR Test AMD / scan (push) Has been skipped
PR Test NVIDIA ARM / cancel-on-close (push) Has been skipped
PR Test NVIDIA / scan (push) Has been skipped
Release Docker Images / build (cu129-torch-2.11.0) (push) Has been skipped
Release Docker Images / build (cu130-torch-2.11.0) (push) Has been skipped
Release PyPI / publish (push) Has been skipped
Scheduler Python Test / test (push) Successful in 27m19s
Docs / build (push) Successful in 28m8s
Scheduler C++ Test / test (push) Successful in 28m19s
Scheduler C++ Test / test-flat (push) Successful in 28m18s
Docs / deploy (push) Has been cancelled
PR Test AMD / finish (push) Has been cancelled
PR Test NVIDIA / finish (push) Has been cancelled
PR Test NVIDIA ARM / finish (push) Has been cancelled
PR Test NVIDIA ARM / ${{ matrix.name }} (${{ matrix.runner }}) (push) Has been cancelled
PR Test AMD / ${{ matrix.name }} (${{ matrix.runner }}) (push) Has been cancelled
PR Test NVIDIA / ${{ matrix.name }} (${{ matrix.runner }}) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:32:31 +08:00

189 行
8.5 KiB
C++

// Copyright (c) 2026 LightSeek Foundation
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
#pragma once
// Shared fixture for HybridPrefixCache + two-group paged-cache tests.
#include <gtest/gtest.h>
#include <cstdint>
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
#include "resource/allocator/owned_pages.h"
#include "resource/allocator/page_allocator.h"
#include "resource/allocator/paged_cache_group.h"
#include "resource/hybrid_prefix_cache/hybrid_prefix_cache.h"
#include "resource/kv_prefix_cache/kv_prefix_cache.h"
#include "resource/radix_tree/paged_cache_snapshot.h"
#include "resource/radix_tree/radix_tree.h"
#include "resource/radix_tree/tree_node.h"
#include "resource/types.h"
#include "unit_test_helper.h"
namespace tokenspeed::test {
struct PagedCacheFixtureParams {
std::int32_t page_size;
std::int32_t device_pages;
std::int32_t lcm_raw_tokens;
std::int32_t sliding_window_tokens;
std::int32_t fh_rows_per_page;
std::int32_t fh_stride;
std::int32_t swa_rows_per_page;
std::int32_t swa_stride;
std::int32_t group_total_pages;
};
template <PagedCacheFixtureParams kParams>
class PagedCacheTestFixtureT : public ::testing::Test {
protected:
static constexpr std::int32_t kPageSize = kParams.page_size;
static constexpr std::int32_t kDevicePages = kParams.device_pages;
static constexpr std::int32_t kLcm = kParams.lcm_raw_tokens;
static constexpr std::int32_t kSlidingWindow = kParams.sliding_window_tokens;
void SetUp() override {
device_alloc_ = std::make_unique<PageAllocator>(kPageSize, kDevicePages);
kv_cache_ = std::make_unique<KVPrefixCache>(device_alloc_.get(), /*host=*/nullptr);
auto fh_owner = std::make_unique<PagedCacheGroupAllocator>(MakeGroupConfig(
"fh", kParams.fh_rows_per_page, kParams.fh_stride, PagedCacheGroupConfig::Retention::FullHistory,
/*window=*/0, PagedCacheGroupFamily::History));
auto swa_owner = std::make_unique<PagedCacheGroupAllocator>(MakeGroupConfig(
"swa", kParams.swa_rows_per_page, kParams.swa_stride, PagedCacheGroupConfig::Retention::SlidingWindow,
kSlidingWindow, PagedCacheGroupFamily::State));
fh_alloc_ = fh_owner.get();
swa_alloc_ = swa_owner.get();
hybrid_ = std::make_unique<HybridPrefixCache>(*kv_cache_, /*mamba=*/nullptr,
/*mamba_chunk_size=*/0);
hybrid_->RegisterPagedCacheGroup(std::move(fh_owner));
hybrid_->RegisterPagedCacheGroup(std::move(swa_owner));
std::unordered_map<std::string, std::int32_t> sliding{{"swa", kSlidingWindow}};
hybrid_->EnablePagedCacheAdjunct(/*required=*/{"fh", "swa"}, std::move(sliding));
kv_cache_->GetDeviceManager().SetEvictionCallback([this](TreeNode* node) { hybrid_->OnKVEvict(node); });
}
// Insert pages from `start_node` (nullptr=root); returns terminal node.
TreeNode* InsertDevicePages(std::int32_t num_pages, token_t token_start, TreeNode* start_node = nullptr) {
auto tokens = MakeAlignedTokens(num_pages, kPageSize, token_start);
OwnedPages pages = device_alloc_->Allocate(num_pages);
auto res = kv_cache_->Insert<ResourceType::Device>(tokens, /*prefix_pages=*/{}, std::move(pages),
/*page_hashes=*/{}, start_node);
return res.last_node;
}
// Build a complete snapshot covering one LCM segment ending at prefix_len_tokens.
std::unique_ptr<PagedCacheSnapshot> MakeCompleteSnapshot(std::int32_t prefix_len_tokens,
std::int32_t swa_base_logical_page = 0) {
auto snap = std::make_unique<PagedCacheSnapshot>();
snap->prefix_len_tokens = prefix_len_tokens;
snap->groups.emplace("fh", BuildGroupSnap(fh_alloc_, prefix_len_tokens,
/*base=*/0, /*sliding=*/false));
snap->groups.emplace("swa",
BuildGroupSnap(swa_alloc_, prefix_len_tokens, swa_base_logical_page, /*sliding=*/true));
return snap;
}
// History-only snapshot (state group omitted); used for fallback tests.
std::unique_ptr<PagedCacheSnapshot> MakeHistoryOnlySnapshot(std::int32_t prefix_len_tokens) {
auto snap = std::make_unique<PagedCacheSnapshot>();
snap->prefix_len_tokens = prefix_len_tokens;
snap->groups.emplace("fh", BuildGroupSnap(fh_alloc_, prefix_len_tokens,
/*base=*/0, /*sliding=*/false));
return snap;
}
// Detach and reattach without the state group; re-attach recomputes
// `complete_families` and leaves only History present.
void DowngradeSnapshotToHistoryOnly(TreeNode* node) {
auto snap = hybrid_->DetachPagedCacheSnapshotFromNode(node);
ASSERT_NE(snap, nullptr);
snap->groups.erase("swa");
hybrid_->AttachPagedCacheSnapshotToNode(node, std::move(snap));
}
std::unique_ptr<PageAllocator> device_alloc_;
std::unique_ptr<KVPrefixCache> kv_cache_;
PagedCacheGroupAllocator* fh_alloc_{nullptr};
PagedCacheGroupAllocator* swa_alloc_{nullptr};
std::unique_ptr<HybridPrefixCache> hybrid_;
protected:
static PagedCacheGroupConfig MakeGroupConfig(std::string group_id, std::int32_t rows_per_page, std::int32_t stride,
PagedCacheGroupConfig::Retention retention, std::int32_t window,
PagedCacheGroupFamily family) {
PagedCacheGroupConfig cfg{};
cfg.group_id = std::move(group_id);
cfg.rows_per_page = rows_per_page;
cfg.entry_stride_tokens = stride;
cfg.total_pages = kParams.group_total_pages;
cfg.retention = retention;
cfg.sliding_window_tokens = window;
cfg.family = family;
return cfg;
}
private:
PagedCacheGroupSnapshot BuildGroupSnap(PagedCacheGroupAllocator* alloc, std::int32_t prefix_len_tokens,
std::int32_t base_logical_page, bool sliding) {
PagedCacheGroupTable t{alloc};
t.Acquire(kLcm);
// Caller chooses absolute base; fresh table commits at 0.
auto committed = sliding ? t.CheckpointStateToSnapshot(kLcm) : t.CommitHistoryToSnapshot(kLcm);
PagedCacheGroupSnapshot g{};
g.pages = std::move(committed.pages);
g.base_logical_page = base_logical_page;
g.raw_token_cursor = prefix_len_tokens;
g.sliding = sliding;
return g;
}
};
inline constexpr PagedCacheFixtureParams kSmallFixtureParams{
/*page_size=*/2, /*device_pages=*/8,
/*lcm_raw_tokens=*/4, /*sliding_window_tokens=*/8,
/*fh_rows_per_page=*/4, /*fh_stride=*/1,
/*swa_rows_per_page=*/2, /*swa_stride=*/1,
/*group_total_pages=*/16,
};
using PagedCacheSmallFixture = PagedCacheTestFixtureT<kSmallFixtureParams>;
inline constexpr PagedCacheFixtureParams kLargeFixtureParams{
/*page_size=*/64, /*device_pages=*/64,
/*lcm_raw_tokens=*/256, /*sliding_window_tokens=*/128,
/*fh_rows_per_page=*/64, /*fh_stride=*/4,
/*swa_rows_per_page=*/64, /*swa_stride=*/1,
/*group_total_pages=*/32,
};
using PagedCacheLargeFixture = PagedCacheTestFixtureT<kLargeFixtureParams>;
// Wide-window variant: state window > history alignment so `segments_needed=2`.
inline constexpr PagedCacheFixtureParams kWideWindowFixtureParams{
/*page_size=*/64, /*device_pages=*/64,
/*lcm_raw_tokens=*/256, /*sliding_window_tokens=*/512,
/*fh_rows_per_page=*/64, /*fh_stride=*/4,
/*swa_rows_per_page=*/64, /*swa_stride=*/1,
/*group_total_pages=*/64,
};
using PagedCacheWideWindowFixture = PagedCacheTestFixtureT<kWideWindowFixtureParams>;
} // namespace tokenspeed::test