Skip to content

Commit d042bfd

Browse files
committed
[GPU] GQA f8: address review - add f8e4m3 kernel unit tests
Mirror of the i8/i4 review feedback for the f8e4m3 dynamic path: - slice_gpu_f8e4m3.bfyx - f8 Slice output (dynamic Slice+Concat path). - concat_gpu.dynamic_f8e4m3 - f8 Concat in/out on a dynamic shape (as GQA uses it). Both cover the f8 primitive paths enabled for a quantized KV cache. ScatterUpdate f8 is not tested here as the static (ScatterUpdate) f8 path is out of scope / a documented follow-up.
1 parent eddfe00 commit d042bfd

2 files changed

Lines changed: 78 additions & 1 deletion

File tree

src/plugins/intel_gpu/tests/unit/test_cases/concatenation_gpu_test.cpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2463,3 +2463,39 @@ INSTANTIATE_TEST_SUITE_P(smoke,
24632463
TestParamType_concat_fsv32(1, { 64, 64, 64, 64 }, 1, 1, data_types::i8)
24642464
),
24652465
concat_gpu_b_fs_yx_fsv32_force::PrintToStringParamName);
2466+
2467+
// f8e4m3 Concat: exercises the f8 in/out path enabled for a quantized KV cache (GroupQueryAttention
2468+
// f8e4m3 dynamic Slice+Concat). Uses a dynamic shape (as GQA does) and concatenates two f8 inputs along
2469+
// the feature axis; values are exactly representable so the result is exact.
2470+
TEST(concat_gpu, dynamic_f8e4m3) {
2471+
auto& engine = get_test_engine();
2472+
2473+
layout layout0_dyn = {{1, -1, 2, 2}, data_types::f8e4m3, format::bfyx};
2474+
layout layout1_dyn = {{1, -1, 2, 2}, data_types::f8e4m3, format::bfyx};
2475+
2476+
topology topology(input_layout("input0", layout0_dyn),
2477+
input_layout("input1", layout1_dyn),
2478+
concatenation("concat", {input_info("input0"), input_info("input1")}, 1, data_types::f8e4m3));
2479+
2480+
ExecutionConfig config = get_test_default_config(engine);
2481+
config.set_property(ov::intel_gpu::allow_new_shape_infer(true));
2482+
auto network = cldnn::network::build_network(engine, topology, config);
2483+
2484+
auto input0 = engine.allocate_memory({{1, 1, 2, 2}, data_types::f8e4m3, format::bfyx});
2485+
auto input1 = engine.allocate_memory({{1, 1, 2, 2}, data_types::f8e4m3, format::bfyx});
2486+
set_values<ov::float8_e4m3>(input0, {0.5f, 1.0f, 1.5f, 2.0f});
2487+
set_values<ov::float8_e4m3>(input1, {-0.5f, -1.0f, -1.5f, -2.0f});
2488+
2489+
network->set_input_data("input0", input0);
2490+
network->set_input_data("input1", input1);
2491+
auto outputs = network->execute();
2492+
ASSERT_EQ(outputs.begin()->first, "concat");
2493+
2494+
auto output_memory = outputs.at("concat").get_memory();
2495+
cldnn::mem_lock<ov::float8_e4m3, mem_lock_type::read> output_ptr(output_memory, get_test_stream());
2496+
2497+
std::vector<float> expected = {0.5f, 1.0f, 1.5f, 2.0f, -0.5f, -1.0f, -1.5f, -2.0f};
2498+
ASSERT_EQ(output_ptr.size(), expected.size());
2499+
for (size_t i = 0; i < expected.size(); ++i)
2500+
ASSERT_EQ(expected[i], static_cast<float>(output_ptr[i])) << "i=" << i;
2501+
}

src/plugins/intel_gpu/tests/unit/test_cases/slice_gpu_test.cpp

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -372,4 +372,45 @@ TEST(slice_gpu_i8, bfyx) {
372372
ASSERT_EQ(expected_results[i], output_ptr[i]) << "i=" << i;
373373
}
374374

375-
} // anonymous namespace
375+
// f8e4m3 Slice: values are exactly representable, so the sliced (byte-moved) result is exact. Covers
376+
// the f8 output path enabled for a quantized KV cache (GroupQueryAttention f8e4m3 dynamic Slice+Concat).
377+
TEST(slice_gpu_f8e4m3, bfyx) {
378+
auto& engine = get_test_engine();
379+
380+
// input [1,1,2,4] f8e4m3, slice axis 2 -> [1,1,1,4] (second row).
381+
auto input = engine.allocate_memory({ov::PartialShape{1, 1, 2, 4}, data_types::f8e4m3, format::bfyx});
382+
auto start = engine.allocate_memory({ov::PartialShape{1}, data_types::i64, format::bfyx});
383+
auto stop = engine.allocate_memory({ov::PartialShape{1}, data_types::i64, format::bfyx});
384+
auto step = engine.allocate_memory({ov::PartialShape{1}, data_types::i64, format::bfyx});
385+
auto axes = engine.allocate_memory({ov::PartialShape{1}, data_types::i64, format::bfyx});
386+
387+
set_values<ov::float8_e4m3>(input, {1.0f, 2.0f, 3.0f, 4.0f, -0.5f, -1.5f, -2.5f, -0.25f});
388+
set_values<int64_t>(start, {1});
389+
set_values<int64_t>(stop, {2});
390+
set_values<int64_t>(step, {1});
391+
set_values<int64_t>(axes, {2});
392+
393+
topology topology;
394+
topology.add(input_layout("input", input->get_layout()));
395+
topology.add(data("start", start));
396+
topology.add(data("stop", stop));
397+
topology.add(data("step", step));
398+
topology.add(data("axes", axes));
399+
topology.add(slice("slice", {input_info("input"), input_info("start"), input_info("stop"), input_info("step"), input_info("axes")}));
400+
401+
ExecutionConfig config = get_test_default_config(engine);
402+
config.set_property(ov::intel_gpu::allow_new_shape_infer(true));
403+
cldnn::network network(engine, topology, config);
404+
network.set_input_data("input", input);
405+
auto outputs = network.execute();
406+
407+
auto output = outputs.at("slice").get_memory();
408+
cldnn::mem_lock<ov::float8_e4m3, mem_lock_type::read> output_ptr(output, get_test_stream());
409+
410+
std::vector<ov::float8_e4m3> expected_results = {-0.5f, -1.5f, -2.5f, -0.25f};
411+
ASSERT_EQ(output_ptr.size(), expected_results.size());
412+
for (size_t i = 0; i < expected_results.size(); ++i)
413+
ASSERT_EQ(expected_results[i], output_ptr[i]) << "i=" << i;
414+
}
415+
416+
} // anonymous namespace

0 commit comments

Comments
 (0)