|
32 | 32 | #include <executorch/backends/vulkan/runtime/graph/ops/DispatchNode.h> |
33 | 33 |
|
34 | 34 | #include <executorch/backends/vulkan/runtime/graph/ops/impl/Int8x4Staging.h> |
| 35 | +#include <executorch/backends/vulkan/runtime/graph/ops/impl/QuantizeDequantize.h> |
35 | 36 |
|
36 | 37 | #include <executorch/backends/vulkan/runtime/utils/VecUtils.h> |
37 | 38 |
|
@@ -2379,6 +2380,216 @@ TEST(VulkanComputeGraphTest, execute_advances_value_update_generation) { |
2379 | 2380 | EXPECT_FALSE(graph.was_value_updated(values)); |
2380 | 2381 | } |
2381 | 2382 |
|
| 2383 | +TEST(VulkanComputeGraphTest, choose_qparams_handles_dynamic_row_counts) { |
| 2384 | + constexpr int64_t kMaxM = 8; |
| 2385 | + constexpr int64_t kK = 128; |
| 2386 | + |
| 2387 | + GraphConfig config; |
| 2388 | + config.enable_querypool = true; |
| 2389 | + config.expect_dynamic_shapes = true; |
| 2390 | + ComputeGraph graph(config); |
| 2391 | + |
| 2392 | + const IOValueRef input = |
| 2393 | + graph.add_input_tensor({kMaxM, kK}, vkapi::kFloat, utils::kBuffer); |
| 2394 | + const ValueRef quant_min = graph.add_scalar<int64_t>(-128); |
| 2395 | + const ValueRef quant_max = graph.add_scalar<int64_t>(127); |
| 2396 | + const ValueRef scales = graph.add_tensor( |
| 2397 | + {kMaxM}, vkapi::kFloat, utils::kTexture3D, utils::kWidthPacked); |
| 2398 | + const ValueRef zero_points = graph.add_tensor( |
| 2399 | + {kMaxM}, vkapi::kChar, utils::kTexture3D, utils::kWidthPacked); |
| 2400 | + |
| 2401 | + VK_GET_OP_FN("etvk.choose_qparams_per_row.default") |
| 2402 | + (graph, {input.value, quant_min, quant_max, scales, zero_points}); |
| 2403 | + |
| 2404 | + const ValueRef scales_staging = graph.set_output_tensor(scales); |
| 2405 | + const ValueRef zero_points_staging = graph.set_output_tensor(zero_points); |
| 2406 | + |
| 2407 | + graph.prepare(); |
| 2408 | + graph.prepack(); |
| 2409 | + |
| 2410 | + for (const int64_t M : std::vector<int64_t>{kMaxM, 4, 1, kMaxM}) { |
| 2411 | + graph.resize_input(0, {M, kK}); |
| 2412 | + graph.propagate_resize(); |
| 2413 | + |
| 2414 | + EXPECT_EQ(graph.sizes_of(scales), std::vector<int64_t>({M})); |
| 2415 | + EXPECT_EQ(graph.sizes_of(zero_points), std::vector<int64_t>({M})); |
| 2416 | + |
| 2417 | + std::vector<float> input_data(M * kK); |
| 2418 | + for (int64_t m = 0; m < M; ++m) { |
| 2419 | + std::fill_n(input_data.begin() + m * kK, kK, float(m + 1)); |
| 2420 | + } |
| 2421 | + graph.maybe_cast_and_copy_into_staging( |
| 2422 | + input.staging, input_data.data(), input_data.size(), vkapi::kFloat); |
| 2423 | + |
| 2424 | + graph.execute(); |
| 2425 | + |
| 2426 | + std::vector<float> scale_data(M); |
| 2427 | + std::vector<int8_t> zero_point_data(M); |
| 2428 | + graph.maybe_cast_and_copy_from_staging( |
| 2429 | + scales_staging, scale_data.data(), scale_data.size(), vkapi::kFloat); |
| 2430 | + graph.maybe_cast_and_copy_from_staging( |
| 2431 | + zero_points_staging, |
| 2432 | + zero_point_data.data(), |
| 2433 | + zero_point_data.size(), |
| 2434 | + vkapi::kChar); |
| 2435 | + |
| 2436 | + for (int64_t m = 0; m < M; ++m) { |
| 2437 | + EXPECT_NEAR(scale_data[m], float(m + 1) / 255.0f, 1e-6f); |
| 2438 | + EXPECT_EQ(zero_point_data[m], -128); |
| 2439 | + } |
| 2440 | + |
| 2441 | + graph.context()->querypool().extract_results(); |
| 2442 | + const auto shader_results = |
| 2443 | + graph.context()->querypool().get_shader_timestamp_data(); |
| 2444 | + const auto choose_result = std::find_if( |
| 2445 | + shader_results.begin(), shader_results.end(), [](const auto& result) { |
| 2446 | + return result.kernel_name.find("choose_qparams_per_row") != |
| 2447 | + std::string::npos; |
| 2448 | + }); |
| 2449 | + ASSERT_NE(choose_result, shader_results.end()); |
| 2450 | + EXPECT_EQ(choose_result->metadata.gwg[0], 1u); |
| 2451 | + EXPECT_EQ( |
| 2452 | + choose_result->metadata.gwg[1], |
| 2453 | + utils::div_up_4(utils::safe_downcast<uint32_t>(M))); |
| 2454 | + EXPECT_EQ(choose_result->metadata.gwg[2], 1u); |
| 2455 | + EXPECT_EQ(choose_result->metadata.lwg[0], 64u); |
| 2456 | + EXPECT_EQ(choose_result->metadata.lwg[1], 1u); |
| 2457 | + EXPECT_EQ(choose_result->metadata.lwg[2], 1u); |
| 2458 | + } |
| 2459 | +} |
| 2460 | + |
| 2461 | +void test_quantize_and_pack_handles_dynamic_row_counts( |
| 2462 | + const int64_t group_size_value, |
| 2463 | + const utils::uvec3& expected_local_wg_size) { |
| 2464 | + if (!api::context()->adapter_ptr()->supports_int8_dot_product()) { |
| 2465 | + GTEST_SKIP() << "Quantize and pack requires integer dot product support"; |
| 2466 | + } |
| 2467 | + |
| 2468 | + constexpr int64_t kMaxM = 8; |
| 2469 | + constexpr int64_t kK = 128; |
| 2470 | + const int64_t num_groups = kK / group_size_value; |
| 2471 | + const int64_t max_m4 = utils::div_up(kMaxM, int64_t(4)); |
| 2472 | + |
| 2473 | + GraphConfig config; |
| 2474 | + config.enable_querypool = true; |
| 2475 | + config.expect_dynamic_shapes = true; |
| 2476 | + ComputeGraph graph(config); |
| 2477 | + |
| 2478 | + const IOValueRef input = |
| 2479 | + graph.add_input_tensor({kMaxM, kK}, vkapi::kFloat, utils::kBuffer); |
| 2480 | + const ValueRef quant_min = graph.add_scalar<int64_t>(-128); |
| 2481 | + const ValueRef quant_max = graph.add_scalar<int64_t>(127); |
| 2482 | + const ValueRef scales = graph.add_tensor( |
| 2483 | + {kMaxM}, vkapi::kFloat, utils::kTexture3D, utils::kWidthPacked); |
| 2484 | + const ValueRef zero_points = graph.add_tensor( |
| 2485 | + {kMaxM}, vkapi::kChar, utils::kTexture3D, utils::kWidthPacked); |
| 2486 | + |
| 2487 | + VK_GET_OP_FN("etvk.choose_qparams_per_row.default") |
| 2488 | + (graph, {input.value, quant_min, quant_max, scales, zero_points}); |
| 2489 | + |
| 2490 | + const ValueRef packed_input = graph.add_tensor( |
| 2491 | + {kMaxM, kK}, vkapi::kInt8x4, utils::kBuffer, utils::kPackedInt8_4H4W); |
| 2492 | + const ValueRef input_sums = graph.add_tensor( |
| 2493 | + {num_groups * max_m4 * 4}, |
| 2494 | + vkapi::kInt, |
| 2495 | + utils::kBuffer, |
| 2496 | + utils::kWidthPacked); |
| 2497 | + const ValueRef group_size = graph.add_scalar<int64_t>(group_size_value); |
| 2498 | + const QuantizationConfig input_quant_config( |
| 2499 | + 8, kPerChannel, {1, kK}, false, true); |
| 2500 | + |
| 2501 | + add_quantize_and_pack_4h4w_with_group_sums_node( |
| 2502 | + graph, |
| 2503 | + input_quant_config, |
| 2504 | + input.value, |
| 2505 | + input_sums, |
| 2506 | + scales, |
| 2507 | + zero_points, |
| 2508 | + packed_input, |
| 2509 | + group_size); |
| 2510 | + |
| 2511 | + const ValueRef packed_input_staging = graph.set_output_tensor(packed_input); |
| 2512 | + const ValueRef input_sums_staging = graph.set_output_tensor(input_sums); |
| 2513 | + |
| 2514 | + graph.prepare(); |
| 2515 | + graph.prepack(); |
| 2516 | + |
| 2517 | + for (const int64_t M : std::vector<int64_t>{kMaxM, 4, 1, kMaxM}) { |
| 2518 | + graph.resize_input(0, {M, kK}); |
| 2519 | + graph.propagate_resize(); |
| 2520 | + |
| 2521 | + std::vector<float> input_data(M * kK); |
| 2522 | + for (int64_t m = 0; m < M; ++m) { |
| 2523 | + std::fill_n(input_data.begin() + m * kK, kK, float(m + 1)); |
| 2524 | + } |
| 2525 | + graph.maybe_cast_and_copy_into_staging( |
| 2526 | + input.staging, input_data.data(), input_data.size(), vkapi::kFloat); |
| 2527 | + |
| 2528 | + graph.execute(); |
| 2529 | + |
| 2530 | + graph.context()->querypool().extract_results(); |
| 2531 | + const auto shader_results = |
| 2532 | + graph.context()->querypool().get_shader_timestamp_data(); |
| 2533 | + const auto quantize_result = std::find_if( |
| 2534 | + shader_results.begin(), shader_results.end(), [](const auto& result) { |
| 2535 | + return result.kernel_name.find( |
| 2536 | + "quantize_and_pack_4h4w_with_group_sums") != |
| 2537 | + std::string::npos; |
| 2538 | + }); |
| 2539 | + |
| 2540 | + if (M == 1) { |
| 2541 | + EXPECT_EQ(quantize_result, shader_results.end()); |
| 2542 | + continue; |
| 2543 | + } |
| 2544 | + |
| 2545 | + ASSERT_NE(quantize_result, shader_results.end()); |
| 2546 | + EXPECT_EQ( |
| 2547 | + quantize_result->metadata.gwg[0], |
| 2548 | + utils::safe_downcast<uint32_t>(num_groups)); |
| 2549 | + EXPECT_EQ( |
| 2550 | + quantize_result->metadata.gwg[1], |
| 2551 | + utils::div_up_4(utils::safe_downcast<uint32_t>(M))); |
| 2552 | + EXPECT_EQ(quantize_result->metadata.gwg[2], 1u); |
| 2553 | + EXPECT_EQ(quantize_result->metadata.lwg[0], expected_local_wg_size[0]); |
| 2554 | + EXPECT_EQ(quantize_result->metadata.lwg[1], expected_local_wg_size[1]); |
| 2555 | + EXPECT_EQ(quantize_result->metadata.lwg[2], expected_local_wg_size[2]); |
| 2556 | + |
| 2557 | + const size_t packed_numel = graph.staging_buffer_numel_of(packed_input); |
| 2558 | + std::vector<int32_t> packed_data(packed_numel); |
| 2559 | + graph.maybe_cast_and_copy_from_staging( |
| 2560 | + packed_input_staging, |
| 2561 | + packed_data.data(), |
| 2562 | + packed_data.size(), |
| 2563 | + vkapi::kInt8x4); |
| 2564 | + for (int64_t i = 0; i < M * kK / 4; ++i) { |
| 2565 | + EXPECT_EQ(packed_data[i], 0x7f7f7f7f); |
| 2566 | + } |
| 2567 | + |
| 2568 | + std::vector<int32_t> sums_data(num_groups * max_m4 * 4); |
| 2569 | + graph.maybe_cast_and_copy_from_staging( |
| 2570 | + input_sums_staging, sums_data.data(), sums_data.size(), vkapi::kInt); |
| 2571 | + const int64_t current_m4 = utils::div_up(M, int64_t(4)); |
| 2572 | + for (int64_t group = 0; group < num_groups; ++group) { |
| 2573 | + for (int64_t m = 0; m < M; ++m) { |
| 2574 | + EXPECT_EQ( |
| 2575 | + sums_data[group * current_m4 * 4 + m], 127 * group_size_value); |
| 2576 | + } |
| 2577 | + } |
| 2578 | + } |
| 2579 | +} |
| 2580 | + |
| 2581 | +TEST( |
| 2582 | + VulkanComputeGraphTest, |
| 2583 | + quantize_and_pack_handles_dynamic_row_counts_with_small_groups) { |
| 2584 | + test_quantize_and_pack_handles_dynamic_row_counts(32, {4u, 1u, 16u}); |
| 2585 | +} |
| 2586 | + |
| 2587 | +TEST( |
| 2588 | + VulkanComputeGraphTest, |
| 2589 | + quantize_and_pack_handles_dynamic_row_counts_with_large_groups) { |
| 2590 | + test_quantize_and_pack_handles_dynamic_row_counts(128, {2u, 1u, 32u}); |
| 2591 | +} |
| 2592 | + |
2382 | 2593 | #define CREATE_WEIGHT_TENSOR(name, sizes, dtype, val) \ |
2383 | 2594 | std::vector<float> data_##name(utils::multiply_integers(sizes)); \ |
2384 | 2595 | std::fill(data_##name.begin(), data_##name.end(), val); \ |
|
0 commit comments