char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
6602dd338
char/aliceai-t5-moe
1// 2// MIT license 3// Copyright (C) 2024 Intel Corporation 4// SPDX-License-Identifier: MIT 5// 6 7// 8// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 9// See https://llvm.org/LICENSE.txt for license information. 10// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 11// 12 13#ifndef GGML_SYCL_DPCT_HELPER_HPP 14#define GGML_SYCL_DPCT_HELPER_HPP 15 16#include <cstdlib> 17#include <iostream> 18#include <map> 19 20#include <sycl/sycl.hpp> 21#include <sycl/half_type.hpp> 22#include <oneapi/mkl.hpp> 23 24#if defined(__linux__ ) 25#include <sys/mman.h> 26#elif defined(_WIN64 ) 27#ifndef NOMINMAX 28#define NOMINMAX 29#endif 30#include <windows.h> 31#else 32#error "Only support Windows and Linux." 33#endif 34 35#if defined(__linux__ ) 36#include <unistd.h> 37#include <sys/syscall.h> 38#endif 39#if defined(_WIN64 ) 40#ifndef NOMINMAX 41#define NOMINMAX 42#endif 43#include <windows.h> 44#endif 45 46 47#define DPCT_COMPATIBILITY_TEMP (900) 48 49#if defined(_MSC_VER ) 50#define __dpct_align__ (n ) __declspec(align(n)) 51#define __dpct_inline__ __forceinline 52#else 53#define __dpct_align__ (n ) __attribute__((aligned(n))) 54#define __dpct_inline__ __inline__ __attribute__((always_inline)) 55#endif 56 57#if defined(_MSC_VER ) 58#define __dpct_noinline__ __declspec(noinline) 59#else 60#define __dpct_noinline__ __attribute__((noinline)) 61#endif 62 63#define DPCT_UNUSED (x ) (void)(x) 64 65[[noreturn ]]inline void _abort (const char * str ) { 66 std::cerr <<str << std::endl ; 67 std::abort (); 68} 69 70inline std::string get_device_type_name (const sycl::device & Device ) { 71auto DeviceType = Device .get_info < sycl::info::device::device_type > (); 72switch (DeviceType ) { 73case sycl::info::device_type::cpu : 74return "cpu" ; 75case sycl::info::device_type::gpu : 76return "gpu" ; 77case sycl::info::device_type::host : 78return "host" ; 79case sycl::info::device_type::accelerator : 80return "acc" ; 81default : 82return "unknown" ; 83 } 84} 85 86inline std::string get_device_backend_and_type (const sycl::device & device ) { 87 std::stringstream device_type ; 88 sycl::backend backend = device .get_backend (); 89device_type <<backend <<":" <<get_device_type_name (device ); 90return device_type .str (); 91} 92 93template < typename Ts > struct matrix_info_t { 94 oneapi::mkl::transpose transpose_info [2 ]; 95Ts value_info [2 ]; 96 std::int64_t size_info [3 ]; 97 std::int64_t ld_info [3 ]; 98 std::int64_t groupsize_info ; 99}; 100 101namespace dpct 102{ 103typedef sycl::queue * queue_ptr ; 104typedef sycl::event * event_ptr ; 105typedef char * device_ptr ; 106typedef uint8_t byte_t ; 107typedef sycl::buffer < byte_t > buffer_t ; 108 109/// SYCL default exception handler 110inline auto exception_handler = [](sycl::exception_list exceptions ) 111 { 112for (std::exception_ptr const & e :exceptions ) 113 { 114try 115 { 116 std::rethrow_exception (e ); 117 } 118catch (sycl::exception const & e ) 119 { 120 std::cerr <<"Caught asynchronous SYCL exception:" << std::endl 121 <<e .what () << std::endl 122 <<"Exception caught at file:" <<__FILE__ 123 <<", line:" <<__LINE__ << std::endl ; 124 } 125 } 126 }; 127 128enum error_code 129 { 130success = 0 , 131default_error = 999 132 }; 133 134enum memcpy_direction 135 { 136host_to_host , 137host_to_device , 138device_to_host , 139device_to_device , 140automatic 141 }; 142 143enum memory_region 144 { 145global = 0 ,// device global memory 146constant ,// device constant memory 147local ,// device local memory 148shared ,// memory which can be accessed by host and device 149 }; 150 151enum class library_data_t :unsigned char 152 { 153real_float = 0 , 154complex_float , 155real_double , 156complex_double , 157real_half , 158complex_half , 159real_bfloat16 , 160complex_bfloat16 , 161real_int4 , 162complex_int4 , 163real_uint4 , 164complex_uint4 , 165real_int8 , 166complex_int8 , 167real_uint8 , 168complex_uint8 , 169real_int16 , 170complex_int16 , 171real_uint16 , 172complex_uint16 , 173real_int32 , 174complex_int32 , 175real_uint32 , 176complex_uint32 , 177real_int64 , 178complex_int64 , 179real_uint64 , 180complex_uint64 , 181real_int8_4 , 182real_int8_32 , 183real_uint8_4 , 184library_data_t_size 185 }; 186 187template < typename T > 188struct DataType 189 { 190using T2 = T ; 191 }; 192template < typename T > 193struct DataType < sycl::vec < T ,2 >> 194 { 195using T2 = std::complex < T > ; 196 }; 197 198static void destroy_event (event_ptr event ) 199 { 200delete event ; 201 } 202 203static inline unsigned int get_tid () 204 { 205#if defined(__linux__ ) 206return syscall (SYS_gettid ); 207#elif defined(_WIN64 ) 208return GetCurrentThreadId (); 209#else 210#error "Only support Windows and Linux." 211#endif 212 } 213 214namespace detail 215 { 216static void get_version (const sycl::device & dev ,int & major ,int & minor ) 217 { 218// Version string has the following format: 219// a. OpenCL<space><major.minor><space><vendor-specific-information> 220// b. <major.minor> 221// c. <AmdGcnArchName> e.g gfx1030 222 std::string ver ; 223ver = dev .get_info < sycl::info::device::version > (); 224 std::string::size_type i = 0 ; 225while (i < ver .size ()) { 226if (isdigit (ver [i ])) 227break ; 228i ++ ; 229 } 230major = std::stoi (& (ver [i ])); 231while (i < ver .size ()) { 232if (ver [i ]== '.' ) 233break ; 234i ++ ; 235 } 236if (i < ver .size ()) { 237// a. and b. 238i ++ ; 239minor = std::stoi (& (ver [i ])); 240 }else { 241// c. 242minor = 0 ; 243 } 244 } 245 246template < typename tag ,typename T > 247class generic_error_type 248 { 249public : 250generic_error_type ()= default ; 251generic_error_type (T value ) :value {value } {} 252 operatorT ()const {return value ; } 253 254private : 255T value ; 256 }; 257 258 }// namespace detail 259 260// COPY from DPCT head files 261/// dim3 is used to store 3 component dimensions. 262class dim3 { 263public : 264unsigned x ,y ,z ; 265 266constexpr dim3 (unsigned x = 1 ,unsigned y = 1 ,unsigned z = 1 ) 267 :x (x ),y (y ),z (z ) {} 268 269dim3 (const sycl::id < 3 > & r ) :dim3 (r [2 ],r [1 ],r [0 ]) {} 270 271 operator sycl::range < 3 > ()const {return sycl::range < 3 > (z ,y ,x ); } 272 };// namespace dim3 273 274inline dim3 operator* (const dim3 & a ,const dim3 & b ) { 275return dim3 {a .x * b .x ,a .y * b .y ,a .z * b .z }; 276 } 277// COPY from DPCT head files 278 279 280/// Pitched 2D/3D memory data. 281class pitched_data 282 { 283public : 284pitched_data () :pitched_data (nullptr ,0 ,0 ,0 ) {} 285pitched_data (void * data ,size_t pitch ,size_t x ,size_t y ) 286 :_data (data ),_pitch (pitch ),_x (x ),_y (y ) {} 287 288void * get_data_ptr () {return _data ; } 289void set_data_ptr (void * data ) {_data = data ; } 290 291size_t get_pitch () {return _pitch ; } 292void set_pitch (size_t pitch ) {_pitch = pitch ; } 293 294size_t get_x () {return _x ; } 295void set_x (size_t x ) {_x = x ; } 296 297size_t get_y () {return _y ; } 298void set_y (size_t y ) {_y = y ; } 299 300private : 301void * _data ; 302size_t _pitch ,_x ,_y ; 303 }; 304 305class device_info 306 { 307public : 308// get interface 309const char * get_name ()const {return _name ; } 310char * get_name () {return _name ; } 311template < typename WorkItemSizesTy = sycl::range < 3 > , 312 std::enable_if_t < std::is_same_v < WorkItemSizesTy , sycl::range < 3 >> || 313 std::is_same_v < WorkItemSizesTy ,int *> , 314int > = 0 > 315auto get_max_work_item_sizes ()const 316 { 317if constexpr (std::is_same_v < WorkItemSizesTy , sycl::range < 3 >> ) 318return sycl::range < 3 > (_max_work_item_sizes_i [0 ], 319_max_work_item_sizes_i [1 ], 320_max_work_item_sizes_i [2 ]); 321else 322 { 323return _max_work_item_sizes_i ; 324 } 325 } 326template < typename WorkItemSizesTy = sycl::range < 3 > , 327 std::enable_if_t < std::is_same_v < WorkItemSizesTy , sycl::range < 3 >> || 328 std::is_same_v < WorkItemSizesTy ,int *> , 329int > = 0 > 330auto get_max_work_item_sizes () 331 { 332if constexpr (std::is_same_v < WorkItemSizesTy , sycl::range < 3 >> ) 333return sycl::range < 3 > (_max_work_item_sizes_i [0 ], 334_max_work_item_sizes_i [1 ], 335_max_work_item_sizes_i [2 ]); 336else 337 { 338return _max_work_item_sizes_i ; 339 } 340 } 341bool get_host_unified_memory ()const {return _host_unified_memory ; } 342int get_major_version ()const {return _major ; } 343int get_minor_version ()const {return _minor ; } 344int get_integrated ()const {return _integrated ; } 345int get_max_clock_frequency ()const {return _frequency ; } 346int get_max_compute_units ()const {return _max_compute_units ; } 347int get_max_work_group_size ()const {return _max_work_group_size ; } 348int get_max_sub_group_size ()const {return _max_sub_group_size ; } 349int get_max_work_items_per_compute_unit ()const 350 { 351return _max_work_items_per_compute_unit ; 352 } 353int get_max_register_size_per_work_group ()const 354 { 355return _max_register_size_per_work_group ; 356 } 357template < typename NDRangeSizeTy = size_t * , 358 std::enable_if_t < std::is_same_v < NDRangeSizeTy ,size_t *> || 359 std::is_same_v < NDRangeSizeTy ,int *> , 360int > = 0 > 361auto get_max_nd_range_size ()const 362 { 363if constexpr (std::is_same_v < NDRangeSizeTy ,size_t *> ) 364return _max_nd_range_size ; 365else 366return _max_nd_range_size_i ; 367 } 368template < typename NDRangeSizeTy = size_t * , 369 std::enable_if_t < std::is_same_v < NDRangeSizeTy ,size_t *> || 370 std::is_same_v < NDRangeSizeTy ,int *> , 371int > = 0 > 372auto get_max_nd_range_size () 373 { 374if constexpr (std::is_same_v < NDRangeSizeTy ,size_t *> ) 375return _max_nd_range_size ; 376else 377return _max_nd_range_size_i ; 378 } 379size_t get_global_mem_size ()const {return _global_mem_size ; } 380size_t get_local_mem_size ()const {return _local_mem_size ; } 381size_t get_max_mem_alloc_size ()const {return _max_mem_alloc_size ; } 382/// Returns the maximum clock rate of device's global memory in kHz. If 383/// compiler does not support this API then returns default value 3200000 kHz. 384unsigned int get_memory_clock_rate ()const {return _memory_clock_rate ; } 385/// Returns the maximum bus width between device and memory in bits. If 386/// compiler does not support this API then returns default value 64 bits. 387unsigned int get_memory_bus_width ()const {return _memory_bus_width ; } 388uint32_t get_device_id ()const {return _device_id ; } 389 std::array < unsigned char ,16 > get_uuid ()const {return _uuid ; } 390/// Returns global memory cache size in bytes. 391unsigned int get_global_mem_cache_size ()const 392 { 393return _global_mem_cache_size ; 394 } 395 396// set interface 397void set_name (const char * name ) 398 { 399size_t length = strlen (name ); 400if (length < 256 ) 401 { 402 std::memcpy (_name ,name ,length + 1 ); 403 } 404else 405 { 406 std::memcpy (_name ,name ,255 ); 407_name [255 ]= '\0' ; 408 } 409 } 410void set_max_work_item_sizes (const sycl::range < 3 > max_work_item_sizes ) 411 { 412for (int i = 0 ;i < 3 ;++ i ) 413_max_work_item_sizes_i [i ]= max_work_item_sizes [i ]; 414 } 415 [[deprecated ]]void 416set_max_work_item_sizes (const sycl::id < 3 > max_work_item_sizes ) 417 { 418for (int i = 0 ;i < 3 ;++ i ) 419 { 420_max_work_item_sizes_i [i ]= max_work_item_sizes [i ]; 421 } 422 } 423void set_host_unified_memory (bool host_unified_memory ) 424 { 425_host_unified_memory = host_unified_memory ; 426 } 427void set_major_version (int major ) {_major = major ; } 428void set_minor_version (int minor ) {_minor = minor ; } 429void set_integrated (int integrated ) {_integrated = integrated ; } 430void set_max_clock_frequency (int frequency ) {_frequency = frequency ; } 431void set_max_compute_units (int max_compute_units ) 432 { 433_max_compute_units = max_compute_units ; 434 } 435void set_global_mem_size (size_t global_mem_size ) 436 { 437_global_mem_size = global_mem_size ; 438 } 439void set_local_mem_size (size_t local_mem_size ) 440 { 441_local_mem_size = local_mem_size ; 442 } 443void set_max_mem_alloc_size (size_t max_mem_alloc_size ) 444 { 445_max_mem_alloc_size = max_mem_alloc_size ; 446 } 447void set_max_work_group_size (int max_work_group_size ) 448 { 449_max_work_group_size = max_work_group_size ; 450 } 451void set_max_sub_group_size (int max_sub_group_size ) 452 { 453_max_sub_group_size = max_sub_group_size ; 454 } 455void 456set_max_work_items_per_compute_unit (int max_work_items_per_compute_unit ) 457 { 458_max_work_items_per_compute_unit = max_work_items_per_compute_unit ; 459 } 460void set_max_nd_range_size (int max_nd_range_size []) 461 { 462for (int i = 0 ;i < 3 ;i ++ ) 463 { 464_max_nd_range_size [i ]= max_nd_range_size [i ]; 465_max_nd_range_size_i [i ]= max_nd_range_size [i ]; 466 } 467 } 468void set_memory_clock_rate (unsigned int memory_clock_rate ) 469 { 470_memory_clock_rate = memory_clock_rate ; 471 } 472void set_memory_bus_width (unsigned int memory_bus_width ) 473 { 474_memory_bus_width = memory_bus_width ; 475 } 476void 477set_max_register_size_per_work_group (int max_register_size_per_work_group ) 478 { 479_max_register_size_per_work_group = max_register_size_per_work_group ; 480 } 481void set_device_id (uint32_t device_id ) 482 { 483_device_id = device_id ; 484 } 485void set_uuid (std::array < unsigned char ,16 > uuid ) 486 { 487_uuid = std::move (uuid ); 488 } 489void set_global_mem_cache_size (unsigned int global_mem_cache_size ) 490 { 491_global_mem_cache_size = global_mem_cache_size ; 492 } 493 494private : 495char _name [256 ]; 496int _max_work_item_sizes_i [3 ]; 497bool _host_unified_memory = false; 498int _major ; 499int _minor ; 500int _integrated = 0 ; 501int _frequency ; 502// Set estimated value 3200000 kHz as default value. 503unsigned int _memory_clock_rate = 3200000 ; 504// Set estimated value 64 bits as default value. 505unsigned int _memory_bus_width = 64 ; 506unsigned int _global_mem_cache_size ; 507int _max_compute_units ; 508int _max_work_group_size ; 509int _max_sub_group_size ; 510int _max_work_items_per_compute_unit ; 511int _max_register_size_per_work_group ; 512size_t _global_mem_size ; 513size_t _local_mem_size ; 514size_t _max_mem_alloc_size ; 515size_t _max_nd_range_size [3 ]; 516int _max_nd_range_size_i [3 ]; 517uint32_t _device_id ; 518 std::array < unsigned char ,16 > _uuid ; 519 }; 520 521static int get_major_version (const sycl::device & dev ) 522 { 523int major ,minor ; 524 detail::get_version (dev ,major ,minor ); 525return major ; 526 } 527 528static int get_minor_version (const sycl::device & dev ) 529 { 530int major ,minor ; 531 detail::get_version (dev ,major ,minor ); 532return minor ; 533 } 534 535static void get_device_info (device_info & out ,const sycl::device & dev ) 536 { 537device_info prop ; 538prop .set_name (dev .get_info < sycl::info::device::name > ().c_str ()); 539 540int major ,minor ; 541 detail::get_version (dev ,major ,minor ); 542prop .set_major_version (major ); 543prop .set_minor_version (minor ); 544 545prop .set_max_work_item_sizes ( 546#if (__SYCL_COMPILER_VERSION && __SYCL_COMPILER_VERSION < 20220902 ) 547// oneAPI DPC++ compiler older than 2022/09/02, where max_work_item_sizes 548// is an enum class element 549dev .get_info < sycl::info::device::max_work_item_sizes > ()); 550#else 551// SYCL 2020-conformant code, max_work_item_sizes is a struct templated by 552// an int 553dev .get_info < sycl::info::device::max_work_item_sizes < 3 >> ()); 554#endif 555prop .set_host_unified_memory (dev .has (sycl::aspect::usm_host_allocations )); 556 557prop .set_max_clock_frequency ( 558dev .get_info < sycl::info::device::max_clock_frequency > ()* 1000 ); 559 560prop .set_max_compute_units ( 561dev .get_info < sycl::info::device::max_compute_units > ()); 562prop .set_max_work_group_size ( 563dev .get_info < sycl::info::device::max_work_group_size > ()); 564prop .set_global_mem_size (dev .get_info < sycl::info::device::global_mem_size > ()); 565prop .set_local_mem_size (dev .get_info < sycl::info::device::local_mem_size > ()); 566prop .set_max_mem_alloc_size (dev .get_info < sycl::info::device::max_mem_alloc_size > ()); 567 568#if (defined(SYCL_EXT_INTEL_DEVICE_INFO )&& SYCL_EXT_INTEL_DEVICE_INFO >=6 ) 569if (dev .has (sycl::aspect::ext_intel_memory_clock_rate )) 570 { 571unsigned int tmp = 572dev .get_info < sycl::ext::intel::info::device::memory_clock_rate > (); 573if (tmp != 0 ) 574prop .set_memory_clock_rate (1000 * tmp ); 575 } 576if (dev .has (sycl::aspect::ext_intel_memory_bus_width )) 577 { 578prop .set_memory_bus_width ( 579dev .get_info < sycl::ext::intel::info::device::memory_bus_width > ()); 580 } 581if (dev .has (sycl::aspect::ext_intel_device_id )) 582 { 583prop .set_device_id ( 584dev .get_info < sycl::ext::intel::info::device::device_id > ()); 585 } 586if (dev .has (sycl::aspect::ext_intel_device_info_uuid )) 587 { 588prop .set_uuid (dev .get_info < sycl::ext::intel::info::device::uuid > ()); 589 } 590#elif defined(_MSC_VER )&& !defined(__clang__ ) 591#pragma message("get_device_info: querying memory_clock_rate and \ 592 memory_bus_width are not supported by the compiler used. \ 593 Use 3200000 kHz as memory_clock_rate default value. \ 594 Use 64 bits as memory_bus_width default value.") 595#else 596#warning "get_device_info: querying memory_clock_rate and \ 597 memory_bus_width are not supported by the compiler used. \ 598 Use 3200000 kHz as memory_clock_rate default value. \ 599 Use 64 bits as memory_bus_width default value." 600#endif 601 602size_t max_sub_group_size = 1 ; 603 std::vector < size_t > sub_group_sizes = 604dev .get_info < sycl::info::device::sub_group_sizes > (); 605 606for (const auto & sub_group_size :sub_group_sizes ) 607 { 608if (max_sub_group_size < sub_group_size ) 609max_sub_group_size = sub_group_size ; 610 } 611 612prop .set_max_sub_group_size (max_sub_group_size ); 613 614prop .set_max_work_items_per_compute_unit ( 615dev .get_info < sycl::info::device::max_work_group_size > ()); 616int max_nd_range_size []= {0x7FFFFFFF ,0x7FFFFFFF ,0x7FFFFFFF }; 617prop .set_max_nd_range_size (max_nd_range_size ); 618 619// Estimates max register size per work group, feel free to update the value 620// according to device properties. 621prop .set_max_register_size_per_work_group (65536 ); 622 623prop .set_global_mem_cache_size ( 624dev .get_info < sycl::info::device::global_mem_cache_size > ()); 625out = prop ; 626 } 627 628/// dpct device extension 629class device_ext :public sycl::device { 630typedef std::mutex mutex_type ; 631 632public : 633device_ext () : sycl::device () {} 634 ~device_ext () { 635 std::lock_guard < mutex_type > lock (m_mutex ); 636clear_queues (); 637 } 638device_ext (const sycl::device & base ) : sycl::device (base ) { 639 std::lock_guard < mutex_type > lock (m_mutex ); 640init_queues (); 641 } 642 643int is_native_atomic_supported () {return 0 ; } 644int get_major_version ()const {return dpct::get_major_version (* this ); } 645 646int get_minor_version ()const {return dpct::get_minor_version (* this ); } 647 648int get_max_compute_units ()const { 649return get_device_info ().get_max_compute_units (); 650 } 651 652/// Return the maximum clock frequency of this device in KHz. 653int get_max_clock_frequency ()const { 654return get_device_info ().get_max_clock_frequency (); 655 } 656 657int get_integrated ()const {return get_device_info ().get_integrated (); } 658 659int get_max_sub_group_size ()const { 660return get_device_info ().get_max_sub_group_size (); 661 } 662 663int get_max_register_size_per_work_group ()const { 664return get_device_info ().get_max_register_size_per_work_group (); 665 } 666 667int get_max_work_group_size ()const { 668return get_device_info ().get_max_work_group_size (); 669 } 670 671int get_mem_base_addr_align ()const { 672return get_info < sycl::info::device::mem_base_addr_align > (); 673 } 674 675size_t get_global_mem_size ()const { 676return get_device_info ().get_global_mem_size (); 677 } 678 679size_t get_max_mem_alloc_size ()const { 680return get_device_info ().get_max_mem_alloc_size (); 681 } 682 683/// Get the number of bytes of free and total memory on the SYCL device. 684/// \param [out] free_memory The number of bytes of free memory on the 685/// SYCL device. \param [out] total_memory The number of bytes of total 686/// memory on the SYCL device. 687void get_memory_info (size_t & free_memory ,size_t & total_memory ) { 688total_memory = get_device_info ().get_global_mem_size (); 689const char * warning_info = 690"get_memory_info: [warning] ext_intel_free_memory is not " 691"supported (export/set ZES_ENABLE_SYSMAN=1 to support), " 692"use total memory as free memory" ; 693#if (defined(__SYCL_COMPILER_VERSION )&& __SYCL_COMPILER_VERSION >=20221105 ) 694if (!has (sycl::aspect::ext_intel_free_memory )) { 695 std::cerr <<warning_info << std::endl ; 696free_memory = total_memory ; 697 }else { 698free_memory = get_info < sycl::ext::intel::info::device::free_memory > (); 699 } 700#else 701 std::cerr <<warning_info << std::endl ; 702free_memory = total_memory ; 703#if defined(_MSC_VER )&& !defined(__clang__ ) 704#pragma message("Querying the number of bytes of free memory is not supported") 705#else 706#warning "Querying the number of bytes of free memory is not supported" 707#endif 708#endif 709 } 710 711void get_device_info (device_info & out )const { 712 dpct::get_device_info (out ,* this ); 713 } 714 715device_info get_device_info ()const { 716device_info prop ; 717 dpct::get_device_info (prop ,* this ); 718return prop ; 719 } 720 721void reset () { 722 std::lock_guard < mutex_type > lock (m_mutex ); 723clear_queues (); 724init_queues (); 725 } 726 727 sycl::queue & in_order_queue () {return _q_in_order ; } 728 729 sycl::queue & out_of_order_queue () {return _q_out_of_order ; } 730 731 sycl::queue & default_queue () {return in_order_queue (); } 732 733void queues_wait_and_throw () { 734 std::unique_lock < mutex_type > lock (m_mutex ); 735lock .unlock (); 736for (auto & q :_queues ) { 737q .wait_and_throw (); 738 } 739// Guard the destruct of current_queues to make sure the ref count is 740// safe. 741lock .lock (); 742 } 743 744 sycl::queue create_queue (bool enable_exception_handler = false) { 745return create_in_order_queue (enable_exception_handler ); 746 } 747 748 sycl::queue create_queue (sycl::device device , 749bool enable_exception_handler = false) { 750return create_in_order_queue (device ,enable_exception_handler ); 751 } 752 753 sycl::queue create_in_order_queue (bool enable_exception_handler = false) { 754 std::lock_guard < mutex_type > lock (m_mutex ); 755return create_queue_impl (enable_exception_handler , 756 sycl::property::queue::in_order ()); 757 } 758 759 sycl::queue create_in_order_queue (sycl::device device , 760bool enable_exception_handler = false) { 761 std::lock_guard < mutex_type > lock (m_mutex ); 762return create_queue_impl (device ,enable_exception_handler , 763 sycl::property::queue::in_order ()); 764 } 765 766 sycl::queue create_out_of_order_queue ( 767bool enable_exception_handler = false) { 768 std::lock_guard < mutex_type > lock (m_mutex ); 769return create_queue_impl (enable_exception_handler ); 770 } 771 772void destroy_queue (sycl::queue queue ) { 773 std::lock_guard < mutex_type > lock (m_mutex ); 774_queues .erase (std::remove_if (_queues .begin (),_queues .end (), 775 [= ](const sycl::queue & q )-> bool 776 { 777return q == queue ; 778 }), 779_queues .end ()); 780 } 781void set_saved_queue (sycl::queue q ) { 782 std::lock_guard < mutex_type > lock (m_mutex ); 783_saved_queue = q ; 784 } 785 sycl::queue get_saved_queue ()const { 786 std::lock_guard < mutex_type > lock (m_mutex ); 787return _saved_queue ; 788 } 789 790private : 791void clear_queues () {_queues .clear (); } 792 793void init_queues () { 794_q_in_order = 795create_queue_impl (true, sycl::property::queue::in_order ()); 796_q_out_of_order = create_queue_impl (true); 797_saved_queue = default_queue (); 798 } 799 800/// Caller should acquire resource \p m_mutex before calling this 801/// function. 802template < class ...Properties > 803 sycl::queue create_queue_impl (bool enable_exception_handler , 804Properties ...properties ) { 805 sycl::async_handler eh = {}; 806if (enable_exception_handler ) { 807eh = exception_handler ; 808 } 809_queues .push_back (sycl::queue ( 810* this ,eh , 811 sycl::property_list ( 812#ifdef DPCT_PROFILING_ENABLED 813 sycl::property::queue::enable_profiling (), 814#endif 815properties ...))); 816 817return _queues .back (); 818 } 819 820template < class ...Properties > 821 sycl::queue create_queue_impl (sycl::device device , 822bool enable_exception_handler , 823Properties ...properties ) { 824 sycl::async_handler eh = {}; 825if (enable_exception_handler ) { 826eh = exception_handler ; 827 } 828_queues .push_back (sycl::queue ( 829device ,eh , 830 sycl::property_list ( 831#ifdef DPCT_PROFILING_ENABLED 832 sycl::property::queue::enable_profiling (), 833#endif 834properties ...))); 835 836return _queues .back (); 837 } 838 839void get_version (int & major ,int & minor )const { 840 detail::get_version (* this ,major ,minor ); 841 } 842 sycl::queue _q_in_order ,_q_out_of_order ; 843 sycl::queue _saved_queue ; 844 std::vector < sycl::queue > _queues ; 845mutable mutex_type m_mutex ; 846 }; 847 848 849/// device manager 850class dev_mgr 851 { 852public : 853device_ext & current_device () 854 { 855unsigned int dev_id = current_device_id (); 856check_id (dev_id ); 857return * _devs [dev_id ]; 858 } 859device_ext & cpu_device ()const 860 { 861 std::lock_guard < std::recursive_mutex > lock (m_mutex ); 862if (_cpu_device == -1 ) 863 { 864throw std::runtime_error ("no valid cpu device" ); 865 } 866else 867 { 868return * _devs [_cpu_device ]; 869 } 870 } 871device_ext & get_device (unsigned int id )const 872 { 873 std::lock_guard < std::recursive_mutex > lock (m_mutex ); 874check_id (id ); 875return * _devs [id ]; 876 } 877unsigned int current_device_id ()const 878 { 879 std::lock_guard < std::recursive_mutex > lock (m_mutex ); 880auto it = _thread2dev_map .find (get_tid ()); 881if (it != _thread2dev_map .end ()) 882return it -> second ; 883return DEFAULT_DEVICE_ID ; 884 } 885 886/// Select device with a device ID. 887/// \param [in] id The id of the device which can 888/// be obtained through get_device_id(const sycl::device). 889void select_device (unsigned int id ) 890 { 891 std::lock_guard < std::recursive_mutex > lock (m_mutex ); 892check_id (id ); 893_thread2dev_map [get_tid ()]= id ; 894 } 895unsigned int device_count () {return _devs .size (); } 896 897unsigned int get_device_id (const sycl::device & dev ) 898 { 899unsigned int id = 0 ; 900for (auto & dev_item :_devs ) 901 { 902if (* dev_item == dev ) 903 { 904return id ; 905 } 906id ++ ; 907 } 908return -1 ; 909 } 910 911inline std::string get_preferred_gpu_platform_name () { 912 std::string result ; 913 914 std::string filter = "" ; 915char * env = getenv ("ONEAPI_DEVICE_SELECTOR" ); 916if (env ) { 917if (std::strstr (env ,"level_zero" )) { 918filter = "level-zero" ; 919 } 920else if (std::strstr (env ,"opencl" )) { 921filter = "opencl" ; 922 } 923else if (std::strstr (env ,"cuda" )) { 924filter = "cuda" ; 925 } 926else if (std::strstr (env ,"hip" )) { 927filter = "hip" ; 928 } 929else { 930throw std::runtime_error ("invalid device filter: " + std::string (env )); 931 } 932 }else { 933auto default_device = sycl::device (sycl::default_selector_v ); 934auto default_platform_name = default_device .get_platform ().get_info < sycl::info::platform::name > (); 935 936if (std::strstr (default_platform_name .c_str (),"Level-Zero" )|| default_device .is_cpu ()) { 937filter = "level-zero" ; 938 } 939else if (std::strstr (default_platform_name .c_str (),"CUDA" )) { 940filter = "cuda" ; 941 } 942else if (std::strstr (default_platform_name .c_str (),"HIP" )) { 943filter = "hip" ; 944 } 945 } 946 947auto platform_list = sycl::platform::get_platforms (); 948 949for (const auto & platform :platform_list ) { 950auto devices = platform .get_devices (); 951auto gpu_dev = std::find_if (devices .begin (),devices .end (), [](const sycl::device & d ) { 952return d .is_gpu (); 953 }); 954 955if (gpu_dev == devices .end ()) { 956// cout << "platform [" << platform_name 957// << "] does not contain GPU devices, skipping\n"; 958continue ; 959 } 960 961auto platform_name = platform .get_info < sycl::info::platform::name > (); 962 std::string platform_name_low_case ; 963platform_name_low_case .resize (platform_name .size ()); 964 965 std::transform ( 966platform_name .begin (),platform_name .end (),platform_name_low_case .begin (), ::tolower ); 967 968if (platform_name_low_case .find (filter )== std::string::npos ) { 969// cout << "platform [" << platform_name 970// << "] does not match with requested " 971// << filter << ", skipping\n"; 972continue ; 973 } 974 975result = platform_name ; 976 } 977 978if (result .empty ()) 979throw std::runtime_error ("can not find preferred GPU platform" ); 980 981return result ; 982 } 983 984template < class DeviceSelector > 985 std::enable_if_t < 986 std::is_invocable_r_v < int ,DeviceSelector ,const sycl::device &>> 987select_device (const DeviceSelector & selector = sycl::gpu_selector_v ) 988 { 989 sycl::device selected_device = sycl::device (selector ); 990unsigned int selected_device_id = get_device_id (selected_device ); 991select_device (selected_device_id ); 992 } 993 994/// Returns the instance of device manager singleton. 995static dev_mgr & instance () 996 { 997static dev_mgr d_m ; 998return d_m ; 999 } 1000dev_mgr (const dev_mgr & )= delete ; 1001dev_mgr & operator= (const dev_mgr & )= delete ; 1002dev_mgr (dev_mgr && )= delete ; 1003dev_mgr & operator= (dev_mgr && )= delete ; 1004 1005private : 1006mutable std::recursive_mutex m_mutex ; 1007static bool compare_dev (sycl::device & device1 , sycl::device & device2 ) 1008 { 1009 sycl::backend backend1 = device1 .get_backend (); 1010 sycl::backend backend2 = device2 .get_backend (); 1011// levelzero backends always come first 1012if (backend1 == sycl::backend::ext_oneapi_level_zero && backend2 != sycl::backend::ext_oneapi_level_zero )return true; 1013if (backend1 != sycl::backend::ext_oneapi_level_zero && backend2 == sycl::backend::ext_oneapi_level_zero )return false; 1014 dpct::device_info prop1 ; 1015 dpct::get_device_info (prop1 ,device1 ); 1016 dpct::device_info prop2 ; 1017 dpct::get_device_info (prop2 ,device2 ); 1018return prop1 .get_max_compute_units ()> prop2 .get_max_compute_units (); 1019 } 1020static int convert_backend_index (std::string & backend ) { 1021if (backend == "ext_oneapi_level_zero:gpu" )return 0 ; 1022if (backend == "opencl:gpu" )return 1 ; 1023if (backend == "ext_oneapi_cuda:gpu" )return 2 ; 1024if (backend == "ext_oneapi_hip:gpu" )return 3 ; 1025if (backend == "opencl:cpu" )return 4 ; 1026if (backend == "opencl:acc" )return 5 ; 1027printf ("convert_backend_index: can't handle backend=%s\n" ,backend .c_str ()); 1028_abort ("fatal error" ); 1029 } 1030static bool compare_backend (std::string & backend1 , std::string & backend2 ) { 1031return convert_backend_index (backend1 )< convert_backend_index (backend2 ); 1032 } 1033dev_mgr () 1034 { 1035 sycl::device default_device = 1036 sycl::device (sycl::default_selector_v ); 1037_devs .push_back (std::make_shared < device_ext > (default_device )); 1038 1039 std::vector < sycl::device > sycl_all_devs ; 1040// Collect other devices except for the default device. 1041if (default_device .is_cpu ()) 1042_cpu_device = 0 ; 1043 1044auto Platforms = sycl::platform::get_platforms (); 1045// Keep track of the number of devices per backend 1046 std::map < sycl::backend ,size_t > DeviceNums ; 1047 std::map < std::string , std::vector < sycl::device >> backend_devices ; 1048auto preferred_platform_name = get_preferred_gpu_platform_name (); 1049 1050while (!Platforms .empty ()) { 1051auto Platform = Platforms .back (); 1052Platforms .pop_back (); 1053auto platform_name = Platform .get_info < sycl::info::platform::name > (); 1054if (platform_name .compare (preferred_platform_name )!= 0 ) { 1055continue ; 1056 } 1057auto devices = Platform .get_devices (); 1058 std::string backend_type = get_device_backend_and_type (devices [0 ]); 1059for (const auto & device :devices ) { 1060backend_devices [backend_type ].push_back (device ); 1061 } 1062 } 1063 1064 std::vector < std::string > keys ; 1065for (auto it = backend_devices .begin ();it != backend_devices .end ();++ it ) { 1066keys .push_back (it -> first ); 1067 } 1068 std::sort (keys .begin (),keys .end (),compare_backend ); 1069 1070for (auto & key :keys ) { 1071 std::vector < sycl::device > devs = backend_devices [key ]; 1072 std::sort (devs .begin (),devs .end (),compare_dev ); 1073for (const auto & dev :devs ) { 1074sycl_all_devs .push_back (dev ); 1075 } 1076 } 1077 1078for (auto & dev :sycl_all_devs ) 1079 { 1080if (dev == default_device ) 1081 { 1082continue ; 1083 } 1084_devs .push_back (std::make_shared < device_ext > (dev )); 1085if (_cpu_device == -1 && dev .is_cpu ()) 1086 { 1087_cpu_device = _devs .size ()- 1 ; 1088 } 1089 } 1090 } 1091void check_id (unsigned int id )const 1092 { 1093if (id >=_devs .size ()) 1094 { 1095throw std::runtime_error ("invalid device id" ); 1096 } 1097 } 1098 std::vector < std::shared_ptr < device_ext >> _devs ; 1099/// DEFAULT_DEVICE_ID is used, if current_device_id() can not find current 1100/// thread id in _thread2dev_map, which means default device should be used 1101/// for the current thread. 1102const unsigned int DEFAULT_DEVICE_ID = 0 ; 1103/// thread-id to device-id map. 1104 std::map < unsigned int ,unsigned int > _thread2dev_map ; 1105int _cpu_device = -1 ; 1106 }; 1107 1108static inline sycl::queue & get_default_queue () 1109 { 1110return dev_mgr::instance ().current_device ().default_queue (); 1111 } 1112 1113namespace detail 1114 { 1115enum class pointer_access_attribute 1116 { 1117host_only = 0 , 1118device_only , 1119host_device , 1120end 1121 }; 1122 1123static pointer_access_attribute get_pointer_attribute (sycl::queue & q , 1124const void * ptr ) 1125 { 1126switch (sycl::get_pointer_type (ptr ,q .get_context ())) 1127 { 1128case sycl::usm::alloc::unknown : 1129return pointer_access_attribute::host_only ; 1130case sycl::usm::alloc::device : 1131return pointer_access_attribute::device_only ; 1132case sycl::usm::alloc::shared : 1133case sycl::usm::alloc::host : 1134return pointer_access_attribute::host_device ; 1135 } 1136 } 1137 1138template < typename ArgT > 1139inline constexpr std::uint64_t get_type_combination_id (ArgT Val ) 1140 { 1141 static_assert((unsigned char )library_data_t::library_data_t_size <= 1142 std::numeric_limits < unsigned char > ::max ()&& 1143"library_data_t size exceeds limit." ); 1144 static_assert(std::is_same_v < ArgT ,library_data_t > ,"Unsupported ArgT" ); 1145return (std::uint64_t )Val ; 1146 } 1147 1148template < typename FirstT ,typename ...RestT > 1149inline constexpr std::uint64_t get_type_combination_id (FirstT FirstVal , 1150RestT ...RestVal ) 1151 { 1152 static_assert((std::uint8_t )library_data_t::library_data_t_size <= 1153 std::numeric_limits < unsigned char > ::max ()&& 1154"library_data_t size exceeds limit." ); 1155 static_assert(sizeof ...(RestT ) <=8 && "Too many parameters" ); 1156 static_assert(std::is_same_v < FirstT ,library_data_t > ,"Unsupported FirstT" ); 1157return get_type_combination_id (RestVal ...) <<8 | ((std::uint64_t )FirstVal ); 1158 } 1159 1160class mem_mgr 1161 { 1162mem_mgr () 1163 { 1164// Reserved address space, no real memory allocation happens here. 1165#if defined(__linux__ ) 1166mapped_address_space = 1167 (byte_t * )mmap (nullptr ,mapped_region_size ,PROT_NONE , 1168MAP_PRIVATE |MAP_ANONYMOUS ,-1 ,0 ); 1169#elif defined(_WIN64 ) 1170mapped_address_space = (byte_t * )VirtualAlloc ( 1171NULL ,// NULL specified as the base address parameter 1172mapped_region_size ,// Size of allocation 1173MEM_RESERVE ,// Allocate reserved pages 1174PAGE_NOACCESS );// Protection = no access 1175#else 1176#error "Only support Windows and Linux." 1177#endif 1178next_free = mapped_address_space ; 1179 } 1180 1181public : 1182using buffer_id_t = int ; 1183 1184struct allocation 1185 { 1186buffer_t buffer ; 1187byte_t * alloc_ptr ; 1188size_t size ; 1189 }; 1190 1191 ~mem_mgr () 1192 { 1193#if defined(__linux__ ) 1194munmap (mapped_address_space ,mapped_region_size ); 1195#elif defined(_WIN64 ) 1196VirtualFree (mapped_address_space ,0 ,MEM_RELEASE ); 1197#else 1198#error "Only support Windows and Linux." 1199#endif 1200 } 1201 1202mem_mgr (const mem_mgr & )= delete ; 1203mem_mgr & operator= (const mem_mgr & )= delete ; 1204mem_mgr (mem_mgr && )= delete ; 1205mem_mgr & operator= (mem_mgr && )= delete ; 1206 1207/// Allocate 1208void * mem_alloc (size_t size ) 1209 { 1210if (!size ) 1211return nullptr ; 1212 std::lock_guard < std::mutex > lock (m_mutex ); 1213if (next_free + size > mapped_address_space + mapped_region_size ) 1214 { 1215throw std::runtime_error ("dpct_malloc: out of memory for virtual memory pool" ); 1216 } 1217// Allocation 1218 sycl::range < 1 > r (size ); 1219buffer_t buf (r ); 1220allocation A {buf ,next_free ,size }; 1221// Map allocation to device pointer 1222void * result = next_free ; 1223m_map .emplace (next_free + size ,A ); 1224// Update pointer to the next free space. 1225next_free += (size + extra_padding + alignment - 1 )& ~(alignment - 1 ); 1226 1227return result ; 1228 } 1229 1230/// Deallocate 1231void mem_free (const void * ptr ) 1232 { 1233if (!ptr ) 1234return ; 1235 std::lock_guard < std::mutex > lock (m_mutex ); 1236auto it = get_map_iterator (ptr ); 1237m_map .erase (it ); 1238 } 1239 1240/// map: device pointer -> allocation(buffer, alloc_ptr, size) 1241allocation translate_ptr (const void * ptr ) 1242 { 1243 std::lock_guard < std::mutex > lock (m_mutex ); 1244auto it = get_map_iterator (ptr ); 1245return it -> second ; 1246 } 1247 1248/// Check if the pointer represents device pointer or not. 1249bool is_device_ptr (const void * ptr )const 1250 { 1251 std::lock_guard < std::mutex > lock (m_mutex ); 1252return (mapped_address_space <=ptr )&& 1253 (ptr < mapped_address_space + mapped_region_size ); 1254 } 1255 1256/// Returns the instance of memory manager singleton. 1257static mem_mgr & instance () 1258 { 1259static mem_mgr m ; 1260return m ; 1261 } 1262 1263private : 1264 std::map < byte_t * ,allocation > m_map ; 1265mutable std::mutex m_mutex ; 1266byte_t * mapped_address_space ; 1267byte_t * next_free ; 1268const size_t mapped_region_size = 128ull * 1024 * 1024 * 1024 ; 1269const size_t alignment = 256 ; 1270/// This padding may be defined to some positive value to debug 1271/// out of bound accesses. 1272const size_t extra_padding = 0 ; 1273 1274 std::map < byte_t * ,allocation > ::iterator get_map_iterator (const void * ptr ) 1275 { 1276auto it = m_map .upper_bound (const_cast < byte_t *> (reinterpret_cast < const byte_t *> (ptr ))); 1277if (it == m_map .end ()) 1278 { 1279// Not a virtual pointer. 1280throw std::runtime_error ("can not get buffer from non-virtual pointer" ); 1281 } 1282const allocation & alloc = it -> second ; 1283if (ptr < alloc .alloc_ptr ) 1284 { 1285// Out of bound. 1286// This may happen if there's a gap between allocations due to alignment 1287// or extra padding and pointer points to this gap. 1288throw std::runtime_error ("invalid virtual pointer" ); 1289 } 1290return it ; 1291 } 1292 }; 1293 1294template < class T ,memory_region Memory ,size_t Dimension > 1295class accessor ; 1296template < memory_region Memory ,class T = byte_t > 1297class memory_traits 1298 { 1299public : 1300static constexpr sycl::access::target target = 1301 sycl::access::target::device ; 1302static constexpr sycl::access_mode mode = 1303 (Memory == constant ) ? sycl::access_mode::read 1304 : sycl::access_mode::read_write ; 1305static constexpr size_t type_size = sizeof (T ); 1306using element_t = 1307typename std::conditional < Memory == constant ,const T ,T > ::type ; 1308using value_t = typename std::remove_cv < T > ::type ; 1309template < size_t Dimension = 1 > 1310using accessor_t = typename std::conditional < 1311Memory == local , sycl::local_accessor < value_t ,Dimension > , 1312 sycl::accessor < T ,Dimension ,mode ,target >> ::type ; 1313using pointer_t = T * ; 1314 }; 1315 1316static inline void * dpct_malloc (size_t size , sycl::queue & q ) 1317 { 1318return sycl::malloc_device (size ,q .get_device (),q .get_context ()); 1319 } 1320 1321#define PITCH_DEFAULT_ALIGN (x ) (((x) + 31) & ~(0x1F)) 1322static inline void * dpct_malloc (size_t & pitch ,size_t x ,size_t y ,size_t z , 1323 sycl::queue & q ) 1324 { 1325pitch = PITCH_DEFAULT_ALIGN (x ); 1326return dpct_malloc (pitch * y * z ,q ); 1327 } 1328 1329/** 1330* @brief Sets \p value to the first \p size elements starting from \p dev_ptr in \p q. 1331* @tparam valueT The type of the element to be set. 1332* @param [in] q The queue in which the operation is done. 1333* @param [in] dev_ptr Pointer to the virtual device memory address. 1334* @param [in] value The value to be set. 1335* @param [in] size Number of elements to be set to the value. 1336* @return An event representing the memset operation. 1337*/ 1338template < typename valueT > 1339static inline sycl::event dpct_memset (sycl::queue & q ,void * dev_ptr , 1340valueT value ,size_t size ) 1341 { 1342return q .fill (dev_ptr ,value ,size ); 1343 } 1344 1345/** 1346* @brief Sets \p value to the 3D memory region pointed by \p data in \p q. 1347* @tparam valueT The type of the element to be set. 1348* @param [in] q The queue in which the operation is done. 1349* @param [in] data Pointer to the pitched device memory region. 1350* @param [in] value The value to be set. 1351* @param [in] size 3D memory region by number of elements. 1352* @return An event list representing the memset operations. 1353*/ 1354template < typename valueT > 1355static inline std::vector < sycl::event > 1356dpct_memset (sycl::queue & q ,pitched_data data ,valueT value , 1357 sycl::range < 3 > size ) 1358 { 1359 std::vector < sycl::event > event_list ; 1360size_t slice = data .get_pitch ()* data .get_y (); 1361unsigned char * data_surface = (unsigned char * )data .get_data_ptr (); 1362for (size_t z = 0 ;z < size .get (2 );++ z ) 1363 { 1364unsigned char * data_ptr = data_surface ; 1365for (size_t y = 0 ;y < size .get (1 );++ y ) 1366 { 1367event_list .push_back (dpct_memset (q ,data_ptr ,value ,size .get (0 ))); 1368data_ptr += data .get_pitch (); 1369 } 1370data_surface += slice ; 1371 } 1372return event_list ; 1373 } 1374 1375/** 1376* @brief Sets \p val to the pitched 2D memory region pointed by \p ptr in \p q. 1377* @tparam valueT The type of the element to be set. 1378* @param [in] q The queue in which the operation is done. 1379* @param [in] ptr Pointer to the virtual device memory. 1380* @param [in] pitch The pitch size by number of elements, including padding. 1381* @param [in] val The value to be set. 1382* @param [in] x The width of memory region by number of elements. 1383* @param [in] y The height of memory region by number of elements. 1384* @return An event list representing the memset operations. 1385*/ 1386template < typename valueT > 1387static inline std::vector < sycl::event > 1388dpct_memset (sycl::queue & q ,void * ptr ,size_t pitch ,valueT val ,size_t x , 1389size_t y ) 1390 { 1391return dpct_memset (q ,pitched_data (ptr ,pitch ,x ,1 ),val , 1392 sycl::range < 3 > (x ,y ,1 )); 1393 } 1394 1395static memcpy_direction deduce_memcpy_direction (sycl::queue & q ,void * to_ptr , 1396const void * from_ptr , 1397memcpy_direction dir ) 1398 { 1399switch (dir ) 1400 { 1401case memcpy_direction::host_to_host : 1402case memcpy_direction::host_to_device : 1403case memcpy_direction::device_to_host : 1404case memcpy_direction::device_to_device : 1405return dir ; 1406case memcpy_direction::automatic : 1407 { 1408// table[to_attribute][from_attribute] 1409static const memcpy_direction 1410direction_table [static_cast < unsigned > (pointer_access_attribute::end )] 1411 [static_cast < unsigned > (pointer_access_attribute::end )]= 1412 {{memcpy_direction::host_to_host , 1413 memcpy_direction::device_to_host , 1414 memcpy_direction::host_to_host }, 1415 {memcpy_direction::host_to_device , 1416 memcpy_direction::device_to_device , 1417 memcpy_direction::device_to_device }, 1418 {memcpy_direction::host_to_host , 1419 memcpy_direction::device_to_device , 1420 memcpy_direction::device_to_device }}; 1421return direction_table [static_cast < unsigned > (get_pointer_attribute ( 1422q ,to_ptr ))][static_cast < unsigned > (get_pointer_attribute (q ,from_ptr ))]; 1423 } 1424default : 1425throw std::runtime_error ("dpct_memcpy: invalid direction value" ); 1426 } 1427 } 1428 1429static sycl::event 1430dpct_memcpy (sycl::queue & q ,void * to_ptr ,const void * from_ptr ,size_t size , 1431memcpy_direction direction , 1432const std::vector < sycl::event > & dep_events = {}) 1433{ 1434if (! size ) 1435return sycl::event{}; 1436return q . memcpy ( to_ptr , from_ptr , size , dep_events ); 1437DPCT_UNUSED ( direction ); 1438} 1439 1440// Get actual copy range and make sure it will not exceed range. 1441static inline size_t get_copy_range (sycl::range < 3 > size , size_t slice , 1442size_t pitch ) 1443{ 1444return slice * ( size . get ( 2 ) - 1 ) + pitch * ( size . get ( 1 ) - 1 ) + size . get ( 0 ); 1445} 1446 1447static inline size_t get_offset (sycl::id < 3 > id , size_t slice , 1448size_t pitch ) 1449{ 1450return slice * id . get ( 2 ) + pitch * id . get ( 1 ) + id . get ( 0 ); 1451} 1452 1453/// copy 3D matrix specified by \p size from 3D matrix specified by \p from_ptr 1454/// and \p from_range to another specified by \p to_ptr and \p to_range. 1455static inline std::vector < sycl::event > 1456dpct_memcpy (sycl::queue & q , void * to_ptr , const void * from_ptr , 1457sycl::range < 3 > to_range , sycl::range < 3 > from_range , 1458sycl::id < 3 > to_id , sycl::id < 3 > from_id , 1459sycl::range < 3 > size , memcpy_direction direction , 1460const std::vector < sycl::event > & dep_events = {}) 1461{ 1462// RAII for host pointer 1463class host_buffer 1464{ 1465void * _buf ; 1466size_t _size ; 1467sycl::queue & _q ; 1468const std::vector < sycl::event > & _deps ; // free operation depends 1469 1470public : 1471host_buffer (size_t size , sycl::queue & q , 1472const std::vector < sycl::event > & deps ) 1473: _buf (std:: malloc ( size )), _size ( size ), _q ( q ), _deps ( deps ) {} 1474void * get_ptr () const { return _buf ; } 1475size_t get_size () const { return _size ; } 1476~ host_buffer () 1477{ 1478if ( _buf ) 1479{ 1480_q . submit ([ & ](sycl::handler & cgh ) 1481{ 1482cgh . depends_on ( _deps ); 1483cgh . host_task ([ buf = _buf ] { std:: free ( buf ); }); }); 1484} 1485} 1486}; 1487std::vector < sycl::event > event_list ; 1488 1489size_t to_slice = to_range . get ( 1 ) * to_range . get ( 0 ), 1490from_slice = from_range . get ( 1 ) * from_range . get ( 0 ); 1491unsigned char * to_surface = 1492(unsigned char * ) to_ptr + get_offset ( to_id , to_slice , to_range . get ( 0 )); 1493const unsigned char * from_surface = 1494( const unsigned char * ) from_ptr + 1495get_offset ( from_id , from_slice , from_range . get ( 0 )); 1496 1497if ( to_slice == from_slice && to_slice == size . get ( 1 ) * size . get ( 0 )) 1498{ 1499return { dpct_memcpy ( q , to_surface , from_surface , to_slice * size . get ( 2 ), 1500direction , dep_events )}; 1501} 1502direction = deduce_memcpy_direction ( q , to_ptr , from_ptr , direction ); 1503size_t size_slice = size . get ( 1 ) * size . get ( 0 ); 1504switch ( direction ) 1505{ 1506case host_to_host : 1507for (size_t z = 0 ; z < size . get ( 2 ); ++ z ) 1508{ 1509unsigned char * to_ptr = to_surface ; 1510const unsigned char * from_ptr = from_surface ; 1511if ( to_range . get ( 0 ) == from_range . get ( 0 ) && 1512to_range . get ( 0 ) == size . get ( 0 )) 1513{ 1514event_list . push_back ( dpct_memcpy ( q , to_ptr , from_ptr , size_slice , 1515direction , dep_events )); 1516} 1517else 1518{ 1519for (size_t y = 0 ; y < size . get ( 1 ); ++ y ) 1520{ 1521event_list . push_back ( dpct_memcpy ( q , to_ptr , from_ptr , size . get ( 0 ), 1522direction , dep_events )); 1523to_ptr += to_range . get ( 0 ); 1524from_ptr += from_range . get ( 0 ); 1525} 1526} 1527to_surface += to_slice ; 1528from_surface += from_slice ; 1529} 1530break ; 1531case host_to_device : 1532{ 1533host_buffer buf ( get_copy_range ( size , to_slice , to_range . get ( 0 )), q , 1534event_list ); 1535std::vector < sycl::event > host_events ; 1536if ( to_slice == size_slice ) 1537{ 1538// Copy host data to a temp host buffer with the shape of target. 1539host_events = 1540dpct_memcpy ( q , buf . get_ptr (), from_surface , to_range , from_range , 1541sycl:: id < 3 > ( 0 , 0 , 0 ), sycl:: id < 3 > ( 0 , 0 , 0 ), size , 1542host_to_host , dep_events ); 1543} 1544else 1545{ 1546// Copy host data to a temp host buffer with the shape of target. 1547host_events = dpct_memcpy ( 1548q , buf . get_ptr (), from_surface , to_range , from_range , 1549sycl:: id < 3 > ( 0 , 0 , 0 ), sycl:: id < 3 > ( 0 , 0 , 0 ), size , host_to_host , 1550// If has padding data, not sure whether it is useless. So fill temp 1551// buffer with it. 1552std::vector < sycl::event > { 1553dpct_memcpy ( q , buf . get_ptr (), to_surface , buf . get_size (), 1554device_to_host , dep_events )}); 1555} 1556// Copy from temp host buffer to device with only one submit. 1557event_list . push_back ( dpct_memcpy ( q , to_surface , buf . get_ptr (), 1558buf . get_size (), host_to_device , 1559host_events )); 1560break ; 1561} 1562case device_to_host : 1563{ 1564host_buffer buf ( get_copy_range ( size , from_slice , from_range . get ( 0 )), q , 1565event_list ); 1566// Copy from host temp buffer to host target with reshaping. 1567event_list = dpct_memcpy ( 1568q , to_surface , buf . get_ptr (), to_range , from_range , sycl:: id < 3 > ( 0 , 0 , 0 ), 1569sycl:: id < 3 > ( 0 , 0 , 0 ), size , host_to_host , 1570// Copy from device to temp host buffer with only one submit. 1571std::vector < sycl::event > { dpct_memcpy ( q , buf . get_ptr (), from_surface , 1572buf . get_size (), 1573device_to_host , dep_events )}); 1574break ; 1575} 1576case device_to_device : 1577event_list . push_back ( q . submit ([ & ](sycl::handler & cgh ){ 1578cgh . depends_on ( dep_events ); 1579cgh . parallel_for < class dpct_memcpy_3d_detail > ( 1580size , 1581[ = ](sycl::id < 3 > id ) { 1582to_surface [ get_offset ( id , to_slice , to_range . get ( 0 ))] = 1583from_surface [ get_offset ( id , from_slice , from_range . get ( 0 ))]; 1584}); })); 1585break ; 1586default : 1587throw std:: runtime_error ( "dpct_memcpy: invalid direction value" ); 1588} 1589return event_list ; 1590} 1591 1592/// memcpy 2D/3D matrix specified by pitched_data. 1593static inline std::vector < sycl::event > 1594dpct_memcpy (sycl::queue & q , pitched_data to , sycl::id < 3 > to_id , 1595pitched_data from , sycl::id < 3 > from_id , sycl::range < 3 > size , 1596memcpy_direction direction = automatic ) 1597{ 1598return dpct_memcpy ( q , to . get_data_ptr (), from . get_data_ptr (), 1599sycl:: range < 3 > ( to . get_pitch (), to . get_y (), 1 ), 1600sycl:: range < 3 > ( from . get_pitch (), from . get_y (), 1 ), to_id , from_id , 1601size , direction ); 1602} 1603 1604/// memcpy 2D matrix with pitch. 1605static inline std::vector < sycl::event > 1606dpct_memcpy (sycl::queue & q , void * to_ptr , const void * from_ptr , 1607size_t to_pitch , size_t from_pitch , size_t x , size_t y , 1608memcpy_direction direction = automatic ) 1609{ 1610return dpct_memcpy ( q , to_ptr , from_ptr , sycl:: range < 3 > ( to_pitch , y , 1 ), 1611sycl:: range < 3 > ( from_pitch , y , 1 ), 1612sycl:: id < 3 > ( 0 , 0 , 0 ), sycl:: id < 3 > ( 0 , 0 , 0 ), 1613sycl:: range < 3 > ( x , y , 1 ), direction ); 1614} 1615 1616namespace deprecated 1617{ 1618 1619template < typename T, sycl::usm::alloc AllocKind > 1620class usm_allocator 1621{ 1622private : 1623using Alloc = sycl::usm_allocator < T, AllocKind > ; 1624Alloc _impl ; 1625 1626public : 1627using value_type = typename std::allocator_traits < Alloc > ::value_type; 1628using pointer = typename std::allocator_traits < Alloc > ::pointer; 1629using const_pointer = typename std::allocator_traits < Alloc > ::const_pointer; 1630using void_pointer = typename std::allocator_traits < Alloc > ::void_pointer; 1631using const_void_pointer = 1632typename std::allocator_traits < Alloc > ::const_void_pointer; 1633using reference = typename std::allocator_traits < Alloc > ::value_type & ; 1634using const_reference = 1635const typename std::allocator_traits < Alloc > ::value_type & ; 1636using difference_type = 1637typename std::allocator_traits < Alloc > ::difference_type; 1638using size_type = typename std::allocator_traits < Alloc > ::size_type; 1639using propagate_on_container_copy_assignment = typename std::allocator_traits < 1640Alloc > ::propagate_on_container_copy_assignment; 1641using propagate_on_container_move_assignment = typename std::allocator_traits < 1642Alloc > ::propagate_on_container_move_assignment; 1643using propagate_on_container_swap = 1644typename std::allocator_traits < Alloc > ::propagate_on_container_swap; 1645using is_always_equal = 1646typename std::allocator_traits < Alloc > ::is_always_equal; 1647 1648template < typename U > 1649struct rebind 1650{ 1651typedef usm_allocator < U, AllocKind > other; 1652}; 1653 1654usm_allocator () : _impl (dpct:: get_default_queue ()) {} 1655~ usm_allocator () {} 1656usm_allocator ( const usm_allocator & other ) : _impl ( other . _impl ) {} 1657usm_allocator (usm_allocator && other ) : _impl (std:: move ( other . _impl )) {} 1658pointer address (reference r ) { return & r ; } 1659const_pointer address (const_reference r ) { return & r ; } 1660pointer allocate (size_type cnt , const_void_pointer hint = nullptr ) 1661{ 1662return std::allocator_traits < Alloc > :: allocate ( _impl , cnt , hint ); 1663} 1664void deallocate (pointer p , size_type cnt ) 1665{ 1666std::allocator_traits < Alloc > :: deallocate ( _impl , p , cnt ); 1667} 1668size_type max_size () const 1669{ 1670return std::allocator_traits < Alloc > :: max_size ( _impl ); 1671} 1672bool operator == ( const usm_allocator & other ) const { return _impl == other . _impl ; } 1673bool operator != ( const usm_allocator & other ) const { return _impl != other . _impl ; } 1674}; 1675 1676} // namespace deprecated 1677 1678inline void dpct_free (void * ptr , 1679const sycl::queue & q ) 1680{ 1681if ( ptr ) 1682{ 1683sycl:: free ( ptr , q . get_context ()); 1684} 1685} 1686 1687template < typename T > 1688inline auto get_memory ( const void * x ) 1689{ 1690T * new_x = reinterpret_cast < T *> ( const_cast < void *> ( x )); 1691return new_x ; 1692} 1693 1694template < typename T > 1695inline typename DataType < T > ::T2 get_value ( const T * s , sycl::queue & q ) 1696{ 1697using Ty = typename DataType < T > ::T2; 1698Ty s_h ; 1699if ( get_pointer_attribute ( q , s ) == pointer_access_attribute:: device_only ) 1700detail:: dpct_memcpy ( q , (void * ) & s_h , ( const void * ) s , sizeof ( T ), device_to_host ) 1701. wait (); 1702else 1703s_h = * reinterpret_cast < const Ty *> ( s ); 1704return s_h ; 1705} 1706 1707} // namespace detail 1708 1709template < typename T > 1710inline auto get_value ( const T * s , sycl::queue & q ) 1711{ 1712return detail:: get_value ( s , q ); 1713} 1714 1715namespace detail 1716{ 1717template < class Ta, class Tb, class Tc, class Ts > 1718inline void gemm_impl (sycl::queue & q , oneapi::mkl::transpose a_trans , oneapi::mkl::transpose b_trans , int m , 1719int n , int k , const void * alpha , const void * a , int lda , const void * b , int ldb , 1720const void * beta , void * c , int ldc ) { 1721Ts alpha_value = dpct:: get_value ( reinterpret_cast < const Ts *> ( alpha ), q ); 1722Ts beta_value = dpct:: get_value ( reinterpret_cast < const Ts *> ( beta ), q ); 1723auto data_a = get_memory < const Ta > ( a ); 1724auto data_b = get_memory < const Tb > ( b ); 1725auto data_c = get_memory < Tc > ( c ); 1726oneapi::mkl::blas::column_major:: gemm ( q , a_trans , b_trans , m , n , k , alpha_value , data_a , 1727lda , data_b , ldb , beta_value , data_c , ldc ); 1728} 1729 1730template < typename VecT, class BinaryOperation, class = void > 1731class vectorized_binary 1732{ 1733public : 1734inline VecT operator()(VecT a , VecT b , const BinaryOperation binary_op ) 1735{ 1736VecT v4 ; 1737for (size_t i = 0 ; i < v4 . size (); ++ i ) 1738{ 1739v4 [ i ] = binary_op ( a [ i ], b [ i ]); 1740} 1741return v4 ; 1742} 1743}; 1744 1745template < typename VecT, class BinaryOperation > 1746class vectorized_binary < 1747VecT, BinaryOperation, 1748std::void_t < std::invoke_result_t < BinaryOperation, VecT, VecT >>> 1749{ 1750public : 1751inline VecT operator()(VecT a , VecT b , const BinaryOperation binary_op ) 1752{ 1753return binary_op ( a , b ). template as < VecT > (); 1754} 1755}; 1756 1757template < class Ta, class Tb, class Tc, class Ts > 1758inline void gemm_batch_impl (sycl::queue & q , oneapi::mkl::transpose a_trans , oneapi::mkl::transpose b_trans , 1759int m , int n , int k , const void * alpha , const void ** a , int lda , const void ** b , 1760int ldb , const void * beta , void ** c , int ldc , int batch_size , 1761matrix_info_t < float > * matrix_info ) { 1762Ts alpha_value = dpct:: get_value ( reinterpret_cast < const Ts *> ( alpha ), q ); 1763Ts beta_value = dpct:: get_value ( reinterpret_cast < const Ts *> ( beta ), q ); 1764 1765matrix_info -> transpose_info [ 0 ] = a_trans ; 1766matrix_info -> transpose_info [ 1 ] = b_trans ; 1767matrix_info -> value_info [ 0 ] = alpha_value ; 1768matrix_info -> value_info [ 1 ] = beta_value ; 1769matrix_info -> size_info [ 0 ] = m ; 1770matrix_info -> size_info [ 1 ] = n ; 1771matrix_info -> size_info [ 2 ] = k ; 1772matrix_info -> ld_info [ 0 ] = lda ; 1773matrix_info -> ld_info [ 1 ] = ldb ; 1774matrix_info -> ld_info [ 2 ] = ldc ; 1775matrix_info -> groupsize_info = batch_size ; 1776 1777sycl::event e = oneapi::mkl::blas::column_major:: gemm_batch ( 1778q , matrix_info -> transpose_info , matrix_info -> transpose_info + 1 , 1779matrix_info -> size_info , matrix_info -> size_info + 1 , matrix_info -> size_info + 2 , 1780reinterpret_cast < Ts *> ( matrix_info -> value_info ), reinterpret_cast < const Ta **> ( a ), matrix_info -> ld_info , 1781reinterpret_cast < const Tb **> ( b ), matrix_info -> ld_info + 1 , 1782reinterpret_cast < Ts *> ( matrix_info -> value_info + 1 ), reinterpret_cast < Tc **> ( c ), 1783matrix_info -> ld_info + 2 , 1 , & ( matrix_info -> groupsize_info )); 1784} 1785 1786template < class Ta, class Tb, class Tc, class Ts > 1787inline void gemm_batch_impl (sycl::queue & q , oneapi::mkl::transpose a_trans , oneapi::mkl::transpose b_trans , 1788int m , int n , int k , const void * alpha , const void * a , int lda , 1789long long int stride_a , const void * b , int ldb , long long int stride_b , 1790const void * beta , void * c , int ldc , long long int stride_c , int batch_size ) { 1791Ts alpha_value = dpct:: get_value ( reinterpret_cast < const Ts *> ( alpha ), q ); 1792Ts beta_value = dpct:: get_value ( reinterpret_cast < const Ts *> ( beta ), q ); 1793auto data_a = get_memory < const Ta > ( a ); 1794auto data_b = get_memory < const Tb > ( b ); 1795auto data_c = get_memory < Tc > ( c ); 1796oneapi::mkl::blas::column_major:: gemm_batch ( q , a_trans , b_trans , m , n , k , alpha_value , 1797data_a , lda , stride_a , data_b , ldb , stride_b , beta_value , 1798data_c , ldc , stride_c , batch_size ); 1799} 1800 1801} // namespace detail 1802 1803template < typename VecT, class BinaryOperation > 1804inline unsigned vectorized_binary (unsigned a , unsigned b , 1805const BinaryOperation binary_op ) 1806{ 1807sycl::vec < unsigned, 1 > v0 { a }, v1 { b }; 1808auto v2 = v0 . as < VecT > (); 1809auto v3 = v1 . as < VecT > (); 1810auto v4 = 1811detail:: vectorized_binary < VecT, BinaryOperation > ()( v2 , v3 , binary_op ); 1812v0 = v4 . template as < sycl::vec < unsigned, 1 >> (); 1813return v0 ; 1814} 1815 1816static void async_dpct_memcpy (void * to_ptr , const void * from_ptr , size_t size , 1817memcpy_direction direction = automatic , 1818sycl::queue & q = dpct:: get_default_queue ()) 1819{ 1820detail:: dpct_memcpy ( q , to_ptr , from_ptr , size , direction ); 1821} 1822 1823static inline unsigned int select_device (unsigned int id ) 1824{ 1825dev_mgr:: instance (). select_device ( id ); 1826return id ; 1827} 1828 1829template < typename T > 1830T permute_sub_group_by_xor (sycl::sub_group g , T x , unsigned int mask , 1831unsigned int logical_sub_group_size = 32 ) 1832{ 1833unsigned int id = g . get_local_linear_id (); 1834unsigned int start_index = 1835id / logical_sub_group_size * logical_sub_group_size ; 1836unsigned int target_offset = ( id % logical_sub_group_size ) ^ mask ; 1837return sycl:: select_from_group ( g , x , 1838target_offset < logical_sub_group_size 1839? start_index + target_offset 1840: id ); 1841} 1842 1843template < typename T1, typename T2 > 1844using dot_product_acc_t = std::conditional_t < 1845std:: is_unsigned_v < T1 > && std:: is_unsigned_v < T2 > , 1846uint32_t, 1847int32_t > ; 1848 1849template < typename T > 1850sycl::vec < T, 4 > extract_and_sign_or_zero_extend4 (T val ) { 1851return sycl:: vec < T, 1 > ( val ) 1852. template as < sycl::vec < 1853std::conditional_t < std::is_signed_v < T > , int8_t, uint8_t > , 18544 >> () 1855. template convert < T > (); 1856} 1857 1858template < typename T1, typename T2, typename T3 > 1859inline auto dp4a (T1 a , T2 b , T3 c ) { 1860dot_product_acc_t < T1, T2 > res = c ; 1861auto va = extract_and_sign_or_zero_extend4 ( a ); 1862auto vb = extract_and_sign_or_zero_extend4 ( b ); 1863res += va [ 0 ] * vb [ 0 ]; 1864res += va [ 1 ] * vb [ 1 ]; 1865res += va [ 2 ] * vb [ 2 ]; 1866res += va [ 3 ] * vb [ 3 ]; 1867return res ; 1868} 1869 1870struct sub_sat 1871{ 1872template < typename T > 1873auto operator()( const T x , const T y ) const 1874{ 1875return sycl:: sub_sat ( x , y ); 1876} 1877}; 1878 1879template < typename S, typename T > 1880inline T vectorized_min (T a , T b ) 1881{ 1882sycl::vec < T, 1 > v0 { a }, v1 { b }; 1883auto v2 = v0 . template as < S > (); 1884auto v3 = v1 . template as < S > (); 1885auto v4 = sycl:: min ( v2 , v3 ); 1886v0 = v4 . template as < sycl::vec < T, 1 >> (); 1887return v0 ; 1888} 1889 1890inline float pow ( const float a , const int b ) { return sycl:: pown ( a , b ); } 1891inline double pow ( const double a , const int b ) { return sycl:: pown ( a , b ); } 1892inline float pow ( const float a , const float b ) { return sycl:: pow ( a , b ); } 1893inline double pow ( const double a , const double b ) { return sycl:: pow ( a , b ); } 1894template < typename T, typename U > 1895inline typename std::enable_if_t < std::is_floating_point_v < T > , T > 1896pow ( const T a , const U b ) 1897{ 1898return sycl:: pow ( a , static_cast < T > ( b )); 1899} 1900template < typename T, typename U > 1901inline typename std::enable_if_t < !std:: is_floating_point_v < T > , double > 1902pow ( const T a , const U b ) 1903{ 1904return sycl:: pow ( static_cast < double > ( a ), static_cast < double > ( b )); 1905} 1906 1907inline double min ( const double a , const float b ) 1908{ 1909return sycl:: fmin ( a , static_cast < double > ( b )); 1910} 1911inline double min ( const float a , const double b ) 1912{ 1913return sycl:: fmin ( static_cast < double > ( a ), b ); 1914} 1915inline float min ( const float a , const float b ) { return sycl:: fmin ( a , b ); } 1916inline double min ( const double a , const double b ) { return sycl:: fmin ( a , b ); } 1917inline std::uint32_t min ( const std::uint32_t a , const std::int32_t b ) 1918{ 1919return sycl:: min ( a , static_cast < std::uint32_t > ( b )); 1920} 1921inline std::uint32_t min ( const std::int32_t a , const std::uint32_t b ) 1922{ 1923return sycl:: min ( static_cast < std::uint32_t > ( a ), b ); 1924} 1925inline std::int32_t min ( const std::int32_t a , const std::int32_t b ) 1926{ 1927return sycl:: min ( a , b ); 1928} 1929inline std::uint32_t min ( const std::uint32_t a , const std::uint32_t b ) 1930{ 1931return sycl:: min ( a , b ); 1932} 1933inline std::uint64_t min ( const std::uint64_t a , const std::int64_t b ) 1934{ 1935return sycl:: min ( a , static_cast < std::uint64_t > ( b )); 1936} 1937inline std::uint64_t min ( const std::int64_t a , const std::uint64_t b ) 1938{ 1939return sycl:: min ( static_cast < std::uint64_t > ( a ), b ); 1940} 1941inline std::int64_t min ( const std::int64_t a , const std::int64_t b ) 1942{ 1943return sycl:: min ( a , b ); 1944} 1945inline std::uint64_t min ( const std::uint64_t a , const std::uint64_t b ) 1946{ 1947return sycl:: min ( a , b ); 1948} 1949inline std::uint64_t min ( const std::uint64_t a , const std::int32_t b ) 1950{ 1951return sycl:: min ( a , static_cast < std::uint64_t > ( b )); 1952} 1953inline std::uint64_t min ( const std::int32_t a , const std::uint64_t b ) 1954{ 1955return sycl:: min ( static_cast < std::uint64_t > ( a ), b ); 1956} 1957inline std::uint64_t min ( const std::uint64_t a , const std::uint32_t b ) 1958{ 1959return sycl:: min ( a , static_cast < std::uint64_t > ( b )); 1960} 1961inline std::uint64_t min ( const std::uint32_t a , const std::uint64_t b ) 1962{ 1963return sycl:: min ( static_cast < std::uint64_t > ( a ), b ); 1964} 1965// max function overloads. 1966// For floating-point types, `float` or `double` arguments are acceptable. 1967// For integer types, `std::uint32_t`, `std::int32_t`, `std::uint64_t` or 1968// `std::int64_t` type arguments are acceptable. 1969inline double max ( const double a , const float b ) 1970{ 1971return sycl:: fmax ( a , static_cast < double > ( b )); 1972} 1973inline double max ( const float a , const double b ) 1974{ 1975return sycl:: fmax ( static_cast < double > ( a ), b ); 1976} 1977inline float max ( const float a , const float b ) { return sycl:: fmax ( a , b ); } 1978inline double max ( const double a , const double b ) { return sycl:: fmax ( a , b ); } 1979inline std::uint32_t max ( const std::uint32_t a , const std::int32_t b ) 1980{ 1981return sycl:: max ( a , static_cast < std::uint32_t > ( b )); 1982} 1983inline std::uint32_t max ( const std::int32_t a , const std::uint32_t b ) 1984{ 1985return sycl:: max ( static_cast < std::uint32_t > ( a ), b ); 1986} 1987inline std::int32_t max ( const std::int32_t a , const std::int32_t b ) 1988{ 1989return sycl:: max ( a , b ); 1990} 1991inline std::uint32_t max ( const std::uint32_t a , const std::uint32_t b ) 1992{ 1993return sycl:: max ( a , b ); 1994} 1995inline std::uint64_t max ( const std::uint64_t a , const std::int64_t b ) 1996{ 1997return sycl:: max ( a , static_cast < std::uint64_t > ( b )); 1998} 1999inline std::uint64_t max ( const std::int64_t a , const std::uint64_t b ) 2000{ 2001return sycl:: max ( static_cast < std::uint64_t > ( a ), b ); 2002} 2003inline std::int64_t max ( const std::int64_t a , const std::int64_t b ) 2004{ 2005return sycl:: max ( a , b ); 2006} 2007inline std::uint64_t max ( const std::uint64_t a , const std::uint64_t b ) 2008{ 2009return sycl:: max ( a , b ); 2010} 2011inline std::uint64_t max ( const std::uint64_t a , const std::int32_t b ) 2012{ 2013return sycl:: max ( a , static_cast < std::uint64_t > ( b )); 2014} 2015inline std::uint64_t max ( const std::int32_t a , const std::uint64_t b ) 2016{ 2017return sycl:: max ( static_cast < std::uint64_t > ( a ), b ); 2018} 2019inline std::uint64_t max ( const std::uint64_t a , const std::uint32_t b ) 2020{ 2021return sycl:: max ( a , static_cast < std::uint64_t > ( b )); 2022} 2023inline std::uint64_t max ( const std::uint32_t a , const std::uint64_t b ) 2024{ 2025return sycl:: max ( static_cast < std::uint64_t > ( a ), b ); 2026} 2027 2028inline void 2029has_capability_or_fail ( const sycl::device & dev , 2030const std::initializer_list < sycl::aspect > & props ) 2031{ 2032for ( const auto & it : props ) 2033{ 2034if ( dev . has ( it )) 2035continue ; 2036switch ( it ) 2037{ 2038case sycl::aspect:: fp64 : 2039throw std:: runtime_error ( "'double' is not supported in '" + 2040dev . get_info < sycl::info::device::name > () + 2041"' device" ); 2042break ; 2043case sycl::aspect:: fp16 : 2044throw std:: runtime_error ( "'half' is not supported in '" + 2045dev . get_info < sycl::info::device::name > () + 2046"' device" ); 2047break ; 2048default : 2049#define __SYCL_ASPECT ( ASPECT , ID ) \ 2050case sycl::aspect::ASPECT: \ 2051return #ASPECT; 2052#define __SYCL_ASPECT_DEPRECATED ( ASPECT , ID , MESSAGE ) __SYCL_ASPECT(ASPECT, ID) 2053#define __SYCL_ASPECT_DEPRECATED_ALIAS ( ASPECT , ID , MESSAGE ) 2054auto getAspectNameStr = [](sycl::aspect AspectNum ) -> std::string 2055{ 2056switch ( AspectNum ) 2057{ 2058#include <sycl/info/aspects.def> 2059#include <sycl/info/aspects_deprecated.def> 2060default : 2061return "unknown aspect" ; 2062} 2063}; 2064#undef __SYCL_ASPECT_DEPRECATED_ALIAS 2065#undef __SYCL_ASPECT_DEPRECATED 2066#undef __SYCL_ASPECT 2067throw std:: runtime_error ( 2068"'" + getAspectNameStr ( it ) + "' is not supported in '" + 2069dev . get_info < sycl::info::device::name > () + "' device" ); 2070} 2071break ; 2072} 2073} 2074 2075static inline unsigned int get_current_device_id () 2076{ 2077return dev_mgr:: instance (). current_device_id (); 2078} 2079 2080static inline device_ext & get_current_device () 2081{ 2082return dev_mgr:: instance (). current_device (); 2083} 2084 2085static inline device_ext & get_device (unsigned int id ) 2086{ 2087return dev_mgr:: instance (). get_device ( id ); 2088} 2089 2090static inline sycl::queue & get_in_order_queue () 2091{ 2092return dev_mgr:: instance (). current_device (). in_order_queue (); 2093} 2094 2095static sycl::event 2096dpct_memcpy (sycl::queue & q , void * to_ptr , const void * from_ptr , size_t size , 2097memcpy_direction direction , 2098const std::vector < sycl::event > & dep_events = {}) 2099{ 2100if (! size ) 2101return sycl::event{}; 2102return q . memcpy ( to_ptr , from_ptr , size , dep_events ); 2103DPCT_UNUSED ( direction ); 2104} 2105 2106// Get actual copy range and make sure it will not exceed range. 2107static inline size_t get_copy_range (sycl::range < 3 > size , size_t slice , 2108size_t pitch ) 2109{ 2110return slice * ( size . get ( 2 ) - 1 ) + pitch * ( size . get ( 1 ) - 1 ) + size . get ( 0 ); 2111} 2112 2113static inline size_t get_offset (sycl::id < 3 > id , size_t slice , 2114size_t pitch ) 2115{ 2116return slice * id . get ( 2 ) + pitch * id . get ( 1 ) + id . get ( 0 ); 2117} 2118 2119/// copy 3D matrix specified by \p size from 3D matrix specified by \p from_ptr 2120/// and \p from_range to another specified by \p to_ptr and \p to_range. 2121static inline std::vector < sycl::event > 2122dpct_memcpy (sycl::queue & q , void * to_ptr , const void * from_ptr , 2123sycl::range < 3 > to_range , sycl::range < 3 > from_range , 2124sycl::id < 3 > to_id , sycl::id < 3 > from_id , 2125sycl::range < 3 > size , memcpy_direction direction , 2126const std::vector < sycl::event > & dep_events = {}) 2127{ 2128// RAII for host pointer 2129class host_buffer 2130{ 2131void * _buf ; 2132size_t _size ; 2133sycl::queue & _q ; 2134const std::vector < sycl::event > & _deps ; // free operation depends 2135 2136public : 2137host_buffer (size_t size , sycl::queue & q , 2138const std::vector < sycl::event > & deps ) 2139: _buf (std:: malloc ( size )), _size ( size ), _q ( q ), _deps ( deps ) {} 2140void * get_ptr () const { return _buf ; } 2141size_t get_size () const { return _size ; } 2142~ host_buffer () 2143{ 2144if ( _buf ) 2145{ 2146_q . submit ([ & ](sycl::handler & cgh ) 2147{ 2148cgh . depends_on ( _deps ); 2149cgh . host_task ([ buf = _buf ] { std:: free ( buf ); }); }); 2150} 2151} 2152}; 2153std::vector < sycl::event > event_list ; 2154 2155size_t to_slice = to_range . get ( 1 ) * to_range . get ( 0 ), 2156from_slice = from_range . get ( 1 ) * from_range . get ( 0 ); 2157unsigned char * to_surface = 2158(unsigned char * ) to_ptr + get_offset ( to_id , to_slice , to_range . get ( 0 )); 2159const unsigned char * from_surface = 2160( const unsigned char * ) from_ptr + 2161get_offset ( from_id , from_slice , from_range . get ( 0 )); 2162 2163if ( to_slice == from_slice && to_slice == size . get ( 1 ) * size . get ( 0 )) 2164{ 2165return { dpct_memcpy ( q , to_surface , from_surface , to_slice * size . get ( 2 ), 2166direction , dep_events )}; 2167} 2168direction = detail:: deduce_memcpy_direction ( q , to_ptr , from_ptr , direction ); 2169size_t size_slice = size . get ( 1 ) * size . get ( 0 ); 2170switch ( direction ) 2171{ 2172case host_to_host : 2173for (size_t z = 0 ; z < size . get ( 2 ); ++ z ) 2174{ 2175unsigned char * to_ptr = to_surface ; 2176const unsigned char * from_ptr = from_surface ; 2177if ( to_range . get ( 0 ) == from_range . get ( 0 ) && 2178to_range . get ( 0 ) == size . get ( 0 )) 2179{ 2180event_list . push_back ( dpct_memcpy ( q , to_ptr , from_ptr , size_slice , 2181direction , dep_events )); 2182} 2183else 2184{ 2185for (size_t y = 0 ; y < size . get ( 1 ); ++ y ) 2186{ 2187event_list . push_back ( dpct_memcpy ( q , to_ptr , from_ptr , size . get ( 0 ), 2188direction , dep_events )); 2189to_ptr += to_range . get ( 0 ); 2190from_ptr += from_range . get ( 0 ); 2191} 2192} 2193to_surface += to_slice ; 2194from_surface += from_slice ; 2195} 2196break ; 2197case host_to_device : 2198{ 2199host_buffer buf ( get_copy_range ( size , to_slice , to_range . get ( 0 )), q , 2200event_list ); 2201std::vector < sycl::event > host_events ; 2202if ( to_slice == size_slice ) 2203{ 2204// Copy host data to a temp host buffer with the shape of target. 2205host_events = 2206dpct_memcpy ( q , buf . get_ptr (), from_surface , to_range , from_range , 2207sycl:: id < 3 > ( 0 , 0 , 0 ), sycl:: id < 3 > ( 0 , 0 , 0 ), size , 2208host_to_host , dep_events ); 2209} 2210else 2211{ 2212// Copy host data to a temp host buffer with the shape of target. 2213host_events = dpct_memcpy ( 2214q , buf . get_ptr (), from_surface , to_range , from_range , 2215sycl:: id < 3 > ( 0 , 0 , 0 ), sycl:: id < 3 > ( 0 , 0 , 0 ), size , host_to_host , 2216// If has padding data, not sure whether it is useless. So fill temp 2217// buffer with it. 2218std::vector < sycl::event > { 2219dpct_memcpy ( q , buf . get_ptr (), to_surface , buf . get_size (), 2220device_to_host , dep_events )}); 2221} 2222// Copy from temp host buffer to device with only one submit. 2223event_list . push_back ( dpct_memcpy ( q , to_surface , buf . get_ptr (), 2224buf . get_size (), host_to_device , 2225host_events )); 2226break ; 2227} 2228case device_to_host : 2229{ 2230host_buffer buf ( get_copy_range ( size , from_slice , from_range . get ( 0 )), q , 2231event_list ); 2232// Copy from host temp buffer to host target with reshaping. 2233event_list = dpct_memcpy ( 2234q , to_surface , buf . get_ptr (), to_range , from_range , sycl:: id < 3 > ( 0 , 0 , 0 ), 2235sycl:: id < 3 > ( 0 , 0 , 0 ), size , host_to_host , 2236// Copy from device to temp host buffer with only one submit. 2237std::vector < sycl::event > { dpct_memcpy ( q , buf . get_ptr (), from_surface , 2238buf . get_size (), 2239device_to_host , dep_events )}); 2240break ; 2241} 2242case device_to_device : 2243event_list . push_back ( q . submit ([ & ](sycl::handler & cgh ) 2244{ 2245cgh . depends_on ( dep_events ); 2246cgh . parallel_for < class dpct_memcpy_3d_detail > ( 2247size , 2248[ = ](sycl::id < 3 > id ) { 2249to_surface [ get_offset ( id , to_slice , to_range . get ( 0 ))] = 2250from_surface [ get_offset ( id , from_slice , from_range . get ( 0 ))]; 2251}); })); 2252break ; 2253default : 2254throw std:: runtime_error ( "dpct_memcpy: invalid direction value" ); 2255} 2256return event_list ; 2257} 2258 2259/// memcpy 2D/3D matrix specified by pitched_data. 2260static inline std::vector < sycl::event > 2261dpct_memcpy (sycl::queue & q , pitched_data to , sycl::id < 3 > to_id , 2262pitched_data from , sycl::id < 3 > from_id , sycl::range < 3 > size , 2263memcpy_direction direction = automatic ) 2264{ 2265return dpct_memcpy ( q , to . get_data_ptr (), from . get_data_ptr (), 2266sycl:: range < 3 > ( to . get_pitch (), to . get_y (), 1 ), 2267sycl:: range < 3 > ( from . get_pitch (), from . get_y (), 1 ), to_id , from_id , 2268size , direction ); 2269} 2270 2271/// memcpy 2D matrix with pitch. 2272static inline std::vector < sycl::event > 2273dpct_memcpy (sycl::queue & q , void * to_ptr , const void * from_ptr , 2274size_t to_pitch , size_t from_pitch , size_t x , size_t y , 2275memcpy_direction direction = automatic ) 2276{ 2277return dpct_memcpy ( q , to_ptr , from_ptr , sycl:: range < 3 > ( to_pitch , y , 1 ), 2278sycl:: range < 3 > ( from_pitch , y , 1 ), 2279sycl:: id < 3 > ( 0 , 0 , 0 ), sycl:: id < 3 > ( 0 , 0 , 0 ), 2280sycl:: range < 3 > ( x , y , 1 ), direction ); 2281} 2282 2283inline void gemm (sycl::queue & q , oneapi::mkl::transpose a_trans , oneapi::mkl::transpose b_trans , int m , int n , 2284int k , const void * alpha , const void * a , library_data_t a_type , int lda , const void * b , 2285library_data_t b_type , int ldb , const void * beta , void * c , library_data_t c_type , int ldc , 2286library_data_t scaling_type ) { 2287if ( scaling_type == library_data_t:: real_float && 2288c_type == library_data_t:: complex_float ) 2289{ 2290scaling_type = library_data_t:: complex_float ; 2291} 2292else if ( scaling_type == library_data_t:: real_double && 2293c_type == library_data_t:: complex_double ) 2294{ 2295scaling_type = library_data_t:: complex_double ; 2296} 2297 2298std::uint64_t key = 2299detail:: get_type_combination_id ( a_type , b_type , c_type , scaling_type ); 2300switch ( key ) 2301{ 2302case detail:: get_type_combination_id ( 2303library_data_t:: real_float , library_data_t:: real_float , 2304library_data_t:: real_float , library_data_t:: real_float ): 2305{ 2306detail:: gemm_impl < float, float, float, float > ( 2307q , a_trans , b_trans , m , n , k , alpha , a , lda , b , ldb , beta , c , ldc ); 2308break ; 2309} 2310case detail:: get_type_combination_id ( 2311library_data_t:: real_double , library_data_t:: real_double , 2312library_data_t:: real_double , library_data_t:: real_double ): 2313{ 2314detail:: gemm_impl < double, double, double, double > ( 2315q , a_trans , b_trans , m , n , k , alpha , a , lda , b , ldb , beta , c , ldc ); 2316break ; 2317} 2318case detail:: get_type_combination_id ( 2319library_data_t:: complex_float , library_data_t:: complex_float , 2320library_data_t:: complex_float , library_data_t:: complex_float ): 2321{ 2322detail:: gemm_impl < std::complex < float > , std::complex < float > , 2323std::complex < float > , std::complex < float >> ( 2324q , a_trans , b_trans , m , n , k , alpha , a , lda , b , ldb , beta , c , ldc ); 2325break ; 2326} 2327case detail:: get_type_combination_id ( 2328library_data_t:: complex_double , library_data_t:: complex_double , 2329library_data_t:: complex_double , library_data_t:: complex_double ): 2330{ 2331detail:: gemm_impl < std::complex < double > , std::complex < double > , 2332std::complex < double > , std::complex < double >> ( 2333q , a_trans , b_trans , m , n , k , alpha , a , lda , b , ldb , beta , c , ldc ); 2334break ; 2335} 2336case detail:: get_type_combination_id ( 2337library_data_t:: real_half , library_data_t:: real_half , 2338library_data_t:: real_half , library_data_t:: real_half ): 2339{ 2340detail:: gemm_impl < sycl::half, sycl::half, sycl::half, 2341sycl::half > ( q , a_trans , b_trans , m , n , k , alpha , a , 2342lda , b , ldb , beta , c , ldc ); 2343break ; 2344} 2345#ifdef __INTEL_MKL__ 2346case detail:: get_type_combination_id ( 2347library_data_t:: real_bfloat16 , library_data_t:: real_bfloat16 , 2348library_data_t:: real_float , library_data_t:: real_float ): 2349{ 2350detail:: gemm_impl < oneapi::mkl::bfloat16, oneapi::mkl::bfloat16, float, float > ( 2351q , a_trans , b_trans , m , n , k , alpha , a , lda , b , ldb , beta , c , ldc ); 2352break ; 2353} 2354case detail:: get_type_combination_id ( 2355library_data_t:: real_half , library_data_t:: real_half , 2356library_data_t:: real_float , library_data_t:: real_float ): 2357{ 2358detail:: gemm_impl < sycl::half, sycl::half, float, float > ( 2359q , a_trans , b_trans , m , n , k , alpha , a , lda , b , ldb , beta , c , ldc ); 2360break ; 2361} 2362case detail:: get_type_combination_id ( 2363library_data_t:: real_half , library_data_t:: real_half , 2364library_data_t:: real_half , library_data_t:: real_float ): 2365{ 2366float alpha_value = 2367dpct:: get_value ( reinterpret_cast < const float *> ( alpha ), q ); 2368float beta_value = 2369dpct:: get_value ( reinterpret_cast < const float *> ( beta ), q ); 2370sycl::half alpha_half (alpha_value); 2371sycl::half beta_half (beta_value); 2372detail:: gemm_impl < sycl::half, sycl::half, sycl::half, 2373sycl::half > ( q , a_trans , b_trans , m , n , k , & alpha_half , 2374a , lda , b , ldb , & beta_half , c , ldc ); 2375break ; 2376} 2377case detail:: get_type_combination_id ( 2378library_data_t:: real_int8 , library_data_t:: real_int8 , 2379library_data_t:: real_float , library_data_t:: real_float ): 2380{ 2381detail:: gemm_impl < std::int8_t, std::int8_t, float, float > ( 2382q , a_trans , b_trans , m , n , k , alpha , a , lda , b , ldb , beta , c , ldc ); 2383break ; 2384} 2385case detail:: get_type_combination_id ( 2386library_data_t:: real_bfloat16 , library_data_t:: real_bfloat16 , 2387library_data_t:: real_bfloat16 , library_data_t:: real_float ): 2388{ 2389detail:: gemm_impl < oneapi::mkl::bfloat16, oneapi::mkl::bfloat16, oneapi::mkl::bfloat16, float > ( 2390q , a_trans , b_trans , m , n , k , alpha , a , lda , b , ldb , beta , c , ldc ); 2391break ; 2392} 2393case detail:: get_type_combination_id ( 2394library_data_t:: real_int8 , library_data_t:: real_int8 , 2395library_data_t:: real_int32 , library_data_t:: real_int32 ): 2396{ 2397float alpha_float = 2398dpct:: get_value ( reinterpret_cast < const std::int32_t *> ( alpha ), q ); 2399float beta_float = 2400dpct:: get_value ( reinterpret_cast < const std::int32_t *> ( beta ), q ); 2401detail:: gemm_impl < std::int8_t, std::int8_t, std::int32_t, float > ( 2402q , a_trans , b_trans , m , n , k , & alpha_float , a , lda , b , ldb , & beta_float , c , ldc ); 2403break ; 2404} 2405#endif // __INTEL_MKL__ 2406default : 2407throw std:: runtime_error ( "the combination of data type is unsupported" ); 2408} 2409} // gemm() 2410 2411/// Computes a batch of matrix-matrix product with general matrices. 2412/// \param [in] q The queue where the routine should be executed. 2413/// \param [in] a_trans Specifies the operation applied to A. 2414/// \param [in] b_trans Specifies the operation applied to B. 2415/// \param [in] m Specifies the number of rows of the matrix op(A) and of the matrix C. 2416/// \param [in] n Specifies the number of columns of the matrix op(B) and of the matrix C. 2417/// \param [in] k Specifies the number of columns of the matrix op(A) and the number of rows of the matrix op(B). 2418/// \param [in] alpha Scaling factor for the matrix-matrix product. 2419/// \param [in] a Input matrix A. 2420/// \param [in] a_type Data type of the matrix A. 2421/// \param [in] lda Leading dimension of A. 2422/// \param [in] b Input matrix B. 2423/// \param [in] b_type Data type of the matrix B. 2424/// \param [in] ldb Leading dimension of B. 2425/// \param [in] beta Scaling factor for matrix C. 2426/// \param [in, out] c Input/Output matrix C. 2427/// \param [in] c_type Data type of the matrix C. 2428/// \param [in] ldc Leading dimension of C. 2429/// \param [in] batch_size Specifies the number of matrix multiply operations to perform. 2430/// \param [in] scaling_type Data type of the scaling factors. 2431inline void gemm_batch (sycl::queue & q , oneapi::mkl::transpose a_trans , oneapi::mkl::transpose b_trans , int m , 2432int n , int k , const void * alpha , const void * a [], library_data_t a_type , int lda , 2433const void * b [], library_data_t b_type , int ldb , const void * beta , void * c [], 2434library_data_t c_type , int ldc , int batch_size , library_data_t scaling_type , 2435matrix_info_t < float > * matrix_info ) { 2436std::uint64_t key = 2437detail:: get_type_combination_id ( a_type , b_type , c_type , scaling_type ); 2438switch ( key ) 2439{ 2440case detail:: get_type_combination_id ( 2441library_data_t:: real_float , library_data_t:: real_float , 2442library_data_t:: real_float , library_data_t:: real_float ): 2443{ 2444detail:: gemm_batch_impl < float, float, float, float > ( q , a_trans , b_trans , m , n , k , alpha , a , lda , b , ldb , 2445beta , c , ldc , batch_size , matrix_info ); 2446break ; 2447} 2448case detail:: get_type_combination_id ( 2449library_data_t:: real_double , library_data_t:: real_double , 2450library_data_t:: real_double , library_data_t:: real_double ): 2451{ 2452detail:: gemm_batch_impl < double, double, double, double > ( q , a_trans , b_trans , m , n , k , alpha , a , lda , b , ldb , 2453beta , c , ldc , batch_size , matrix_info ); 2454break ; 2455} 2456case detail:: get_type_combination_id ( 2457library_data_t:: real_half , library_data_t:: real_half , 2458library_data_t:: real_half , library_data_t:: real_half ): 2459{ 2460detail:: gemm_batch_impl < sycl::half, sycl::half, sycl::half, sycl::half > ( 2461q , a_trans , b_trans , m , n , k , alpha , a , lda , b , ldb , beta , c , ldc , batch_size , matrix_info ); 2462break ; 2463} 2464#ifdef __INTEL_MKL__ 2465case detail:: get_type_combination_id ( 2466library_data_t:: real_bfloat16 , library_data_t:: real_bfloat16 , 2467library_data_t:: real_bfloat16 , library_data_t:: real_float ): 2468{ 2469detail:: gemm_batch_impl < oneapi::mkl::bfloat16, oneapi::mkl::bfloat16, oneapi::mkl::bfloat16, float > ( 2470q , a_trans , b_trans , m , n , k , alpha , a , lda , b , ldb , beta , c , ldc , batch_size , matrix_info ); 2471break ; 2472} 2473case detail:: get_type_combination_id ( 2474library_data_t:: real_bfloat16 , library_data_t:: real_bfloat16 , 2475library_data_t:: real_float , library_data_t:: real_float ): 2476{ 2477detail:: gemm_batch_impl < oneapi::mkl::bfloat16, oneapi::mkl::bfloat16, float, float > ( 2478q , a_trans , b_trans , m , n , k , alpha , a , lda , b , ldb , beta , c , ldc , batch_size , matrix_info ); 2479break ; 2480} 2481#endif 2482case detail:: get_type_combination_id ( 2483library_data_t:: real_int8 , library_data_t:: real_int8 , 2484library_data_t:: real_int32 , library_data_t:: real_int32 ): 2485{ 2486float alpha_float = 2487dpct:: get_value ( reinterpret_cast < const std::int32_t *> ( alpha ), q ); 2488float beta_float = 2489dpct:: get_value ( reinterpret_cast < const std::int32_t *> ( beta ), q ); 2490detail:: gemm_batch_impl < std::int8_t, std::int8_t, std::int32_t, float > ( 2491q , a_trans , b_trans , m , n , k , & alpha_float , a , lda , b , ldb , & beta_float , c , ldc , batch_size , 2492matrix_info ); 2493break ; 2494} 2495case detail:: get_type_combination_id ( 2496library_data_t:: real_int8 , library_data_t:: real_int8 , 2497library_data_t:: real_float , library_data_t:: real_float ): 2498{ 2499detail:: gemm_batch_impl < std::int8_t, std::int8_t, float, float > ( 2500q , a_trans , b_trans , m , n , k , alpha , a , lda , b , ldb , beta , c , ldc , batch_size , matrix_info ); 2501break ; 2502} 2503case detail:: get_type_combination_id ( 2504library_data_t:: real_half , library_data_t:: real_half , 2505library_data_t:: real_float , library_data_t:: real_float ): 2506{ 2507detail:: gemm_batch_impl < sycl::half, sycl::half, float, float > ( 2508q , a_trans , b_trans , m , n , k , alpha , a , lda , b , ldb , beta , c , ldc , batch_size , matrix_info ); 2509break ; 2510} 2511case detail:: get_type_combination_id ( 2512library_data_t:: real_half , library_data_t:: real_half , 2513library_data_t:: real_half , library_data_t:: real_float ): 2514{ 2515float alpha_value = 2516dpct:: get_value ( reinterpret_cast < const float *> ( alpha ), q ); 2517float beta_value = 2518dpct:: get_value ( reinterpret_cast < const float *> ( beta ), q ); 2519sycl::half alpha_half (alpha_value); 2520sycl::half beta_half (beta_value); 2521detail:: gemm_batch_impl < sycl::half, sycl::half, sycl::half, sycl::half > ( 2522q , a_trans , b_trans , m , n , k , & alpha_half , a , lda , b , ldb , & beta_half , c , ldc , batch_size , matrix_info ); 2523break ; 2524} 2525default : 2526throw std:: runtime_error ( "the combination of data type is unsupported" ); 2527} 2528} 2529 2530/// Computes a batch of matrix-matrix product with general matrices. 2531/// \param [in] q The queue where the routine should be executed. 2532/// \param [in] a_trans Specifies the operation applied to A. 2533/// \param [in] b_trans Specifies the operation applied to B. 2534/// \param [in] m Specifies the number of rows of the matrix op(A) and of the matrix C. 2535/// \param [in] n Specifies the number of columns of the matrix op(B) and of the matrix C. 2536/// \param [in] k Specifies the number of columns of the matrix op(A) and the number of rows of the matrix op(B). 2537/// \param [in] alpha Scaling factor for the matrix-matrix product. 2538/// \param [in] a Input matrix A. 2539/// \param [in] a_type Data type of the matrix A. 2540/// \param [in] lda Leading dimension of A. 2541/// \param [in] stride_a Stride between the different A matrices. 2542/// \param [in] b Input matrix B. 2543/// \param [in] b_type Data type of the matrix B. 2544/// \param [in] ldb Leading dimension of B. 2545/// \param [in] stride_b Stride between the different B matrices. 2546/// \param [in] beta Scaling factor for matrix C. 2547/// \param [in, out] c Input/Output matrix C. 2548/// \param [in] c_type Data type of the matrix C. 2549/// \param [in] ldc Leading dimension of C. 2550/// \param [in] stride_c Stride between the different C matrices. 2551/// \param [in] batch_size Specifies the number of matrix multiply operations to perform. 2552/// \param [in] scaling_type Data type of the scaling factors. 2553inline void gemm_batch (sycl::queue & q , oneapi::mkl::transpose a_trans , oneapi::mkl::transpose b_trans , int m , 2554int n , int k , const void * alpha , const void * a , library_data_t a_type , int lda , 2555long long int stride_a , const void * b , library_data_t b_type , int ldb , 2556long long int stride_b , const void * beta , void * c , library_data_t c_type , int ldc , 2557long long int stride_c , int batch_size , library_data_t scaling_type ) { 2558if ( scaling_type == library_data_t:: real_float && 2559c_type == library_data_t:: complex_float ) 2560{ 2561scaling_type = library_data_t:: complex_float ; 2562} 2563else if ( scaling_type == library_data_t:: real_double && 2564c_type == library_data_t:: complex_double ) 2565{ 2566scaling_type = library_data_t:: complex_double ; 2567} 2568 2569std::uint64_t key = 2570detail:: get_type_combination_id ( a_type , b_type , c_type , scaling_type ); 2571switch ( key ) 2572{ 2573case detail:: get_type_combination_id ( 2574library_data_t:: real_float , library_data_t:: real_float , 2575library_data_t:: real_float , library_data_t:: real_float ): 2576{ 2577detail:: gemm_batch_impl < float, float, float, float > ( 2578q , a_trans , b_trans , m , n , k , alpha , a , lda , stride_a , b , ldb , stride_b , 2579beta , c , ldc , stride_c , batch_size ); 2580break ; 2581} 2582case detail:: get_type_combination_id ( 2583library_data_t:: real_double , library_data_t:: real_double , 2584library_data_t:: real_double , library_data_t:: real_double ): 2585{ 2586detail:: gemm_batch_impl < double, double, double, double > ( 2587q , a_trans , b_trans , m , n , k , alpha , a , lda , stride_a , b , ldb , stride_b , 2588beta , c , ldc , stride_c , batch_size ); 2589break ; 2590} 2591case detail:: get_type_combination_id ( 2592library_data_t:: complex_float , library_data_t:: complex_float , 2593library_data_t:: complex_float , library_data_t:: complex_float ): 2594{ 2595detail:: gemm_batch_impl < std::complex < float > , std::complex < float > , 2596std::complex < float > , std::complex < float >> ( 2597q , a_trans , b_trans , m , n , k , alpha , a , lda , stride_a , b , ldb , stride_b , 2598beta , c , ldc , stride_c , batch_size ); 2599break ; 2600} 2601case detail:: get_type_combination_id ( 2602library_data_t:: complex_double , library_data_t:: complex_double , 2603library_data_t:: complex_double , library_data_t:: complex_double ): 2604{ 2605detail:: gemm_batch_impl < std::complex < double > , std::complex < double > , 2606std::complex < double > , std::complex < double >> ( 2607q , a_trans , b_trans , m , n , k , alpha , a , lda , stride_a , b , ldb , stride_b , 2608beta , c , ldc , stride_c , batch_size ); 2609break ; 2610} 2611case detail:: get_type_combination_id ( 2612library_data_t:: real_half , library_data_t:: real_half , 2613library_data_t:: real_half , library_data_t:: real_half ): 2614{ 2615detail:: gemm_batch_impl < sycl::half, sycl::half, sycl::half, 2616sycl::half > ( q , a_trans , b_trans , m , n , k , alpha , 2617a , lda , stride_a , b , ldb , stride_b , 2618beta , c , ldc , stride_c , batch_size ); 2619break ; 2620} 2621#ifdef __INTEL_MKL__ 2622case detail:: get_type_combination_id ( 2623library_data_t:: real_bfloat16 , library_data_t:: real_bfloat16 , 2624library_data_t:: real_bfloat16 , library_data_t:: real_float ): 2625{ 2626detail:: gemm_batch_impl < oneapi::mkl::bfloat16, oneapi::mkl::bfloat16, oneapi::mkl::bfloat16, float > ( 2627q , a_trans , b_trans , m , n , k , alpha , a , lda , stride_a , b , ldb , stride_b , beta , c , ldc , stride_c , 2628batch_size ); 2629break ; 2630} 2631case detail:: get_type_combination_id ( 2632library_data_t:: real_bfloat16 , library_data_t:: real_bfloat16 , 2633library_data_t:: real_float , library_data_t:: real_float ): 2634{ 2635detail:: gemm_batch_impl < oneapi::mkl::bfloat16, oneapi::mkl::bfloat16, float, float > ( 2636q , a_trans , b_trans , m , n , k , alpha , a , lda , stride_a , b , ldb , stride_b , beta , c , ldc , stride_c , 2637batch_size ); 2638break ; 2639} 2640#endif 2641case detail:: get_type_combination_id ( 2642library_data_t:: real_int8 , library_data_t:: real_int8 , 2643library_data_t:: real_int32 , library_data_t:: real_int32 ): 2644{ 2645detail:: gemm_batch_impl < std::int8_t, std::int8_t, std::int32_t, 2646std::int32_t > ( q , a_trans , b_trans , m , n , k , alpha , 2647a , lda , stride_a , b , ldb , stride_b , 2648beta , c , ldc , stride_c , batch_size ); 2649break ; 2650} 2651case detail:: get_type_combination_id ( 2652library_data_t:: real_int8 , library_data_t:: real_int8 , 2653library_data_t:: real_float , library_data_t:: real_float ): 2654{ 2655detail:: gemm_batch_impl < std::int8_t, std::int8_t, float, float > ( 2656q , a_trans , b_trans , m , n , k , alpha , a , lda , stride_a , b , ldb , stride_b , 2657beta , c , ldc , stride_c , batch_size ); 2658break ; 2659} 2660case detail:: get_type_combination_id ( 2661library_data_t:: real_half , library_data_t:: real_half , 2662library_data_t:: real_float , library_data_t:: real_float ): 2663{ 2664detail:: gemm_batch_impl < sycl::half, sycl::half, float, float > ( 2665q , a_trans , b_trans , m , n , k , alpha , a , lda , stride_a , b , ldb , stride_b , 2666beta , c , ldc , stride_c , batch_size ); 2667break ; 2668} 2669case detail:: get_type_combination_id ( 2670library_data_t:: real_half , library_data_t:: real_half , 2671library_data_t:: real_half , library_data_t:: real_float ): 2672{ 2673float alpha_value = 2674dpct:: get_value ( reinterpret_cast < const float *> ( alpha ), q ); 2675float beta_value = 2676dpct:: get_value ( reinterpret_cast < const float *> ( beta ), q ); 2677sycl::half alpha_half (alpha_value); 2678sycl::half beta_half (beta_value); 2679detail:: gemm_batch_impl < sycl::half, sycl::half, sycl::half, sycl::half > ( 2680q , a_trans , b_trans , m , n , k , & alpha_half , a , lda , stride_a , b , ldb , stride_b , 2681& beta_half , c , ldc , stride_c , batch_size ); 2682break ; 2683} 2684default : 2685throw std:: runtime_error ( "the combination of data type is unsupported" ); 2686} 2687} 2688 2689static inline void 2690async_dpct_memcpy (void * to_ptr , size_t to_pitch , const void * from_ptr , 2691size_t from_pitch , size_t x , size_t y , 2692memcpy_direction direction = automatic , 2693sycl::queue & q = get_default_queue ()) 2694{ 2695detail:: dpct_memcpy ( q , to_ptr , from_ptr , to_pitch , from_pitch , x , y , 2696direction ); 2697} 2698 2699using err0 = detail::generic_error_type < struct err0_tag, int > ; 2700using err1 = detail::generic_error_type < struct err1_tag, int > ; 2701 2702static inline void dpct_free (void * ptr , sycl::queue & q = get_default_queue ()) { 2703detail:: dpct_free ( ptr , q ); 2704} 2705 2706/// dpct accessor used as device function parameter. 2707template < class T, memory_region Memory , size_t Dimension > class accessor; 2708template < class T, memory_region Memory > class accessor < T, Memory, 3 > { 2709public : 2710using memory_t = detail::memory_traits < Memory, T > ; 2711using element_t = typename memory_t::element_t; 2712using pointer_t = typename memory_t::pointer_t; 2713using accessor_t = typename memory_t:: template accessor_t < 3 > ; 2714accessor (pointer_t data , const sycl::range < 3 > & in_range ) 2715: _data ( data ), _range ( in_range ) {} 2716template < memory_region M = Memory > 2717accessor ( typename std::enable_if < M != local , const accessor_t > ::type & acc ) 2718: accessor ( acc , acc . get_range ()) {} 2719accessor ( const accessor_t & acc , const sycl::range < 3 > & in_range ) 2720: accessor ( acc . get_pointer (), in_range ) {} 2721accessor < T, Memory, 2 > operator[](size_t index ) const { 2722sycl:: range < 2 > sub ( _range . get ( 1 ), _range . get ( 2 )); 2723return accessor < T, Memory, 2 > ( _data + index * sub . size (), sub ); 2724} 2725 2726pointer_t get_ptr () const { return _data ; } 2727 2728private : 2729pointer_t _data ; 2730sycl::range < 3 > _range ; 2731}; 2732template < class T, memory_region Memory > class accessor < T, Memory, 2 > { 2733public : 2734using memory_t = detail::memory_traits < Memory, T > ; 2735using element_t = typename memory_t::element_t; 2736using pointer_t = typename memory_t::pointer_t; 2737using accessor_t = typename memory_t:: template accessor_t < 2 > ; 2738accessor (pointer_t data , const sycl::range < 2 > & in_range ) 2739: _data ( data ), _range ( in_range ) {} 2740template < memory_region M = Memory > 2741accessor ( typename std::enable_if < M != local , const accessor_t > ::type & acc ) 2742: accessor ( acc , acc . get_range ()) {} 2743accessor ( const accessor_t & acc , const sycl::range < 2 > & in_range ) 2744: accessor ( acc . get_pointer (), in_range ) {} 2745 2746pointer_t operator[](size_t index ) const { 2747return _data + _range . get ( 1 ) * index ; 2748} 2749 2750pointer_t get_ptr () const { return _data ; } 2751 2752private : 2753pointer_t _data ; 2754sycl::range < 2 > _range ; 2755}; 2756 2757namespace detail { 2758/// Device variable with address space of shared, global or constant. 2759template < class T, memory_region Memory , size_t Dimension > class device_memory { 2760public : 2761using accessor_t = 2762typename detail::memory_traits < Memory, 2763T > :: template accessor_t < Dimension > ; 2764using value_t = typename detail::memory_traits < Memory, T > ::value_t; 2765using dpct_accessor_t = dpct::accessor < T, Memory, Dimension > ; 2766 2767device_memory () : device_memory (sycl:: range < Dimension > ( 1 )) {} 2768 2769/// Constructor of 1-D array with initializer list 2770device_memory ( const sycl::range < Dimension > & in_range , 2771std::initializer_list < value_t > && init_list ) 2772: device_memory ( in_range ) { 2773assert ( init_list . size () <= in_range . size ()); 2774_host_ptr = (value_t * )std:: malloc ( _size ); 2775std:: memset ( _host_ptr , 0 , _size ); 2776std:: memcpy ( _host_ptr , init_list . begin (), init_list . size () * sizeof ( T )); 2777} 2778 2779/// Constructor of 2-D array with initializer list 2780template < size_t D = Dimension > 2781device_memory ( 2782const typename std::enable_if < D == 2 , sycl::range < 2 >> ::type & in_range , 2783std::initializer_list < std::initializer_list < value_t >> && init_list ) 2784: device_memory ( in_range ) { 2785assert ( init_list . size () <= in_range [ 0 ]); 2786_host_ptr = (value_t * )std:: malloc ( _size ); 2787std:: memset ( _host_ptr , 0 , _size ); 2788auto tmp_data = _host_ptr ; 2789for (auto sub_list : init_list ) { 2790assert ( sub_list . size () <= in_range [ 1 ]); 2791std:: memcpy ( tmp_data , sub_list . begin (), 2792sub_list . size () * sizeof ( T )); 2793tmp_data += in_range [ 1 ]; 2794} 2795} 2796 2797/// Constructor with range 2798device_memory ( const sycl::range < Dimension > & range_in ) 2799: _size ( range_in . size () * sizeof ( T )), _range ( range_in ), 2800_reference (false), _host_ptr ( nullptr ), _device_ptr ( nullptr ) { 2801static_assert( 2802( Memory == global ) || ( Memory == constant ) || ( Memory == shared ), 2803"device memory region should be global, constant or shared" ); 2804// Make sure that singleton class mem_mgr and dev_mgr will destruct 2805// later than this. 2806detail::mem_mgr:: instance (); 2807dev_mgr:: instance (); 2808} 2809 2810/// Constructor with range 2811template < class ... Args > 2812device_memory (Args... Arguments ) 2813: device_memory (sycl:: range < Dimension > ( Arguments ...)) {} 2814 2815~ device_memory () { 2816if ( _device_ptr && ! _reference ) 2817dpct:: dpct_free ( _device_ptr ); 2818if ( _host_ptr ) 2819std:: free ( _host_ptr ); 2820} 2821 2822/// Allocate memory with default queue, and init memory if has initial 2823/// value. 2824void init () { init (dpct:: get_default_queue ()); } 2825/// Allocate memory with specified queue, and init memory if has initial 2826/// value. 2827void init (sycl::queue & q ) { 2828if ( _device_ptr ) 2829return ; 2830if (! _size ) 2831return ; 2832allocate_device ( q ); 2833if ( _host_ptr ) 2834detail:: dpct_memcpy ( q , _device_ptr , _host_ptr , _size , 2835host_to_device ); 2836} 2837 2838/// The variable is assigned to a device pointer. 2839void assign (value_t * src , size_t size ) { 2840this -> ~ device_memory (); 2841new ( this ) device_memory( src , size ); 2842} 2843 2844/// Get memory pointer of the memory object, which is virtual pointer when 2845/// usm is not used, and device pointer when usm is used. 2846value_t * get_ptr () { return get_ptr ( get_default_queue ()); } 2847/// Get memory pointer of the memory object, which is virtual pointer when 2848/// usm is not used, and device pointer when usm is used. 2849value_t * get_ptr (sycl::queue & q ) { 2850init ( q ); 2851return _device_ptr ; 2852} 2853 2854/// Get the device memory object size in bytes. 2855size_t get_size () { return _size ; } 2856 2857template < size_t D = Dimension > 2858typename std::enable_if < D == 1 , T > ::type & operator[](size_t index ) { 2859init (); 2860return _device_ptr [ index ]; 2861} 2862 2863/// Get dpct::accessor with dimension info for the device memory object 2864/// when usm is used and dimension is greater than 1. 2865template < size_t D = Dimension > 2866typename std::enable_if < D != 1 , dpct_accessor_t > ::type 2867get_access ([[ maybe_unused ]] sycl::handler & cgh ) { 2868return dpct_accessor_t ((T * ) _device_ptr , _range ); 2869} 2870 2871private : 2872device_memory (value_t * memory_ptr , size_t size ) 2873: _size ( size ), _range ( size / sizeof ( T )), _reference (true), 2874_device_ptr ( memory_ptr ) {} 2875 2876void allocate_device (sycl::queue & q ) { 2877#ifndef DPCT_USM_LEVEL_NONE 2878if ( Memory == shared ) { 2879_device_ptr = (value_t * )sycl:: malloc_shared ( _size , q . get_device (), 2880q . get_context ()); 2881return ; 2882} 2883#ifdef SYCL_EXT_ONEAPI_USM_DEVICE_READ_ONLY 2884if ( Memory == constant ) { 2885_device_ptr = (value_t * )sycl:: malloc_device ( 2886_size , q . get_device (), q . get_context (), 2887sycl::ext::oneapi::property::usm:: device_read_only ()); 2888return ; 2889} 2890#endif 2891#endif 2892_device_ptr = (value_t * )detail:: dpct_malloc ( _size , q ); 2893} 2894 2895size_t _size ; 2896sycl::range < Dimension > _range ; 2897bool _reference ; 2898value_t * _host_ptr ; 2899value_t * _device_ptr ; 2900}; 2901template < class T, memory_region Memory > 2902class device_memory < T, Memory, 0 > : public device_memory < T, Memory, 1 > { 2903public : 2904using base = device_memory < T, Memory, 1 > ; 2905using value_t = typename base::value_t; 2906using accessor_t = 2907typename detail::memory_traits < Memory, T > :: template accessor_t < 0 > ; 2908 2909/// Constructor with initial value. 2910device_memory ( const value_t & val ) : base (sycl:: range < 1 > ( 1 ), { val }) {} 2911 2912/// Default constructor 2913device_memory () : base ( 1 ) {} 2914}; 2915} // namespace detail 2916 2917template < class T, size_t Dimension > 2918using global_memory = detail::device_memory < T, global, Dimension > ; 2919template < class T, size_t Dimension > 2920using constant_memory = detail::device_memory < T, constant, Dimension > ; 2921template < class T, size_t Dimension > 2922using shared_memory = detail::device_memory < T, shared, Dimension > ; 2923 2924 2925template < typename T, 2926sycl::access::address_space addressSpace = 2927sycl::access::address_space:: global_space , 2928sycl::memory_order memoryOrder = sycl::memory_order:: relaxed , 2929sycl::memory_scope memoryScope = sycl::memory_scope:: device > 2930inline T atomic_fetch_add (T * addr , T operand ) { 2931auto atm = 2932sycl:: atomic_ref < T, memoryOrder, memoryScope, addressSpace > ( addr [ 0 ]); 2933return atm . fetch_add ( operand ); 2934} 2935 2936template < sycl::access::address_space addressSpace = 2937sycl::access::address_space:: global_space , 2938sycl::memory_order memoryOrder = sycl::memory_order:: relaxed , 2939sycl::memory_scope memoryScope = sycl::memory_scope:: device , 2940typename T1, typename T2 > 2941inline T1 atomic_fetch_add (T1 * addr , T2 operand ) { 2942auto atm = 2943sycl:: atomic_ref < T1, memoryOrder, memoryScope, addressSpace > ( addr [ 0 ]); 2944return atm . fetch_add ( operand ); 2945} 2946 2947template < typename T, sycl::access::address_space addressSpace = 2948sycl::access::address_space:: global_space > 2949inline T atomic_fetch_add (T * addr , T operand , 2950sycl::memory_order memoryOrder ) { 2951switch ( memoryOrder ) { 2952case sycl::memory_order:: relaxed : 2953return atomic_fetch_add < T, addressSpace, sycl::memory_order::relaxed, 2954sycl::memory_scope::device > ( addr , operand ); 2955case sycl::memory_order:: acq_rel : 2956return atomic_fetch_add < T, addressSpace, sycl::memory_order::acq_rel, 2957sycl::memory_scope::device > ( addr , operand ); 2958case sycl::memory_order:: seq_cst : 2959return atomic_fetch_add < T, addressSpace, sycl::memory_order::seq_cst, 2960sycl::memory_scope::device > ( addr , operand ); 2961default : 2962assert (false && "Invalid memory_order for atomics. Valid memory_order for " 2963"atomics are: sycl::memory_order::relaxed, " 2964"sycl::memory_order::acq_rel, sycl::memory_order::seq_cst!" ); 2965} 2966} 2967 2968template < sycl::access::address_space addressSpace = 2969sycl::access::address_space:: global_space , 2970typename T1, typename T2 > 2971inline T1 atomic_fetch_add (T1 * addr , T2 operand , 2972sycl::memory_order memoryOrder ) { 2973atomic_fetch_add < T1, addressSpace > ( addr , operand , memoryOrder ); 2974} 2975 2976inline unsigned int byte_level_permute ( 2977unsigned int a , unsigned int b , unsigned int s ) { 2978unsigned int ret ; 2979ret = ((((std::uint64_t) b << 32 | a ) >> ( s & 0x7 ) * 8 ) & 0xff ) | 2980(((((std::uint64_t) b << 32 | a ) >> (( s >> 4 ) & 0x7 ) * 8 ) & 0xff ) 2981<< 8 ) | 2982(((((std::uint64_t) b << 32 | a ) >> (( s >> 8 ) & 0x7 ) * 8 ) & 0xff ) 2983<< 16 ) | 2984(((((std::uint64_t) b << 32 | a ) >> (( s >> 12 ) & 0x7 ) * 8 ) & 0xff ) 2985<< 24 ); 2986return ret ; 2987} 2988 2989inline uint32_t byte_level_permute_custom ( 2990uint32_t low32 , uint32_t high32 , uint32_t sel , int mode = 0 ) { 2991constexpr uint16_t lookup [ 6 ][ 4 ] = { 2992{ 0x3210 , 0x4321 , 0x5432 , 0x6543 }, // Forward 4-byte extract 2993{ 0x5670 , 0x6701 , 0x7012 , 0x0123 }, // Backward 4-byte extract 2994{ 0x0000 , 0x1111 , 0x2222 , 0x3333 }, // Replicate 8-bit values 2995{ 0x3210 , 0x3211 , 0x3222 , 0x3333 }, // Edge clamp left 2996{ 0x0000 , 0x1110 , 0x2210 , 0x3210 }, // Edge clamp right 2997{ 0x1010 , 0x3232 , 0x1010 , 0x3232 } // Replicate 16-bit values 2998}; 2999 3000if ( mode >= 1 && mode <= 6 ) { 3001return byte_level_permute ( low32 , high32 , lookup [ mode - 1 ][ sel & 0x3 ]); 3002} else if (! mode ) { 3003return byte_level_permute ( low32 , high32 , sel ); 3004} 3005return 0 ; 3006} 3007 3008template < int n_nondefault_params , int n_default_params , typename T > 3009class args_selector; 3010 3011/// args_selector is a helper class for extracting arguments from an 3012/// array of pointers to arguments or buffer of arguments to pass to a 3013/// kernel function. 3014/// 3015/// \param R(Ts...) The type of the kernel 3016/// \param n_nondefault_params The number of nondefault parameters of the 3017/// kernel (excluding parameters that like sycl::nd_item, etc.) \param 3018/// n_default_params The number of default parameters of the kernel 3019/// 3020/// Example usage: 3021/// With the following kernel: 3022/// void foo(sycl::float2 *x, int n, sycl::nd_item<3> item_ct1, float 3023/// f=.1) {} 3024/// and with the declaration: 3025/// args_selector<2, 1, decltype(foo)> selector(kernelParams, extra); 3026/// we have: 3027/// selector.get<0>() returns a reference to sycl::float*, 3028/// selector.get<1>() returns a reference to int, 3029/// selector.get<2>() returns a reference to float 3030template < int n_nondefault_params , int n_default_params , typename R, 3031typename ... Ts > 3032class args_selector < n_nondefault_params, n_default_params, R(Ts...) > { 3033private : 3034void ** kernel_params ; 3035char * args_buffer ; 3036 3037template < int i > static constexpr int account_for_default_params () { 3038constexpr int n_total_params = sizeof ...( Ts ); 3039if constexpr ( i >= n_nondefault_params ) { 3040return n_total_params - n_default_params + 3041( i - n_nondefault_params ); 3042} else { 3043return i ; 3044} 3045} 3046 3047public : 3048/// Get the type of the ith argument of R(Ts...) 3049/// \param [in] i Index of parameter to get 3050/// \returns Type of ith parameter 3051template < int i > 3052using arg_type = std::tuple_element_t < account_for_default_params < i > (), 3053std::tuple < Ts... >> ; 3054static constexpr int params_num = sizeof ...( Ts ); 3055 3056private : 3057template < int i > static constexpr int get_offset () { 3058if constexpr ( i == 0 ) { 3059// we can assume args_buffer is properly aligned to the 3060// first argument 3061return 0 ; 3062} else { 3063constexpr int prev_off = get_offset < i - 1 > (); 3064constexpr int prev_past_end = 3065prev_off + sizeof ( arg_type < i - 1 > ); 3066using T = arg_type < i > ; 3067// is the past-the-end of the i-1st element properly aligned 3068// with the ith element's alignment? 3069if constexpr ( prev_past_end % alignof(T) == 0 ) { 3070return prev_past_end ; 3071} 3072// otherwise bump prev_past_end to match alignment 3073else { 3074return prev_past_end + 3075(alignof(T) - ( prev_past_end % alignof(T))); 3076} 3077} 3078} 3079 3080static char * get_args_buffer (void ** extra ) { 3081if (! extra ) 3082return nullptr ; 3083for (; (std::size_t) * extra != 0 ; ++ extra ) { 3084if ((std::size_t) * extra == 1 ) { 3085return static_cast < char *> ( * ( extra + 1 )); 3086} 3087} 3088return nullptr ; 3089} 3090 3091public : 3092/// If kernel_params is nonnull, then args_selector will 3093/// extract arguments from kernel_params. Otherwise, it 3094/// will extract them from extra. 3095/// \param [in] kernel_params Array of pointers to arguments 3096/// a or null pointer. 3097/// \param [in] extra Array containing pointer to argument buffer. 3098args_selector (void ** kernel_params , void ** extra ) 3099: kernel_params ( kernel_params ), 3100args_buffer ( get_args_buffer ( extra )) {} 3101 3102/// Get a reference to the ith argument extracted from kernel_params 3103/// or extra. 3104/// \param [in] i Index of argument to get 3105/// \returns Reference to the ith argument 3106template < int i > arg_type < i > & get () { 3107if ( kernel_params ) { 3108return * static_cast < arg_type < i > *> ( kernel_params [ i ]); 3109} else { 3110return * reinterpret_cast < arg_type < i > *> ( args_buffer + 3111get_offset < i > ()); 3112} 3113} 3114}; // COPY from DPCT head file 3115// /opt/intel/oneapi/dpcpp-ct/latest/include/dpct/util.hpp 3116 3117/// Utility class for launching SYCL kernels through kernel 3118/// function wrapper. 3119/// For example: 3120/// A SYCL kernel function: 3121/// void kernel_func(int *ptr, sycl::nd_item<3> item); 3122/// Kernel function wrapper: 3123/// void kernel_func_wrapper(int *ptr) { 3124/// sycl::queue queue = *dpct::kernel_launcher::_que; 3125/// unsigned int localMemSize = dpct::kernel_launcher::_local_mem_size; 3126/// sycl::nd_range<3> nr = dpct::kernel_launcher::_nr; 3127/// queue.parallel_for( 3128/// nr, 3129/// [=](sycl::nd_item<3> item_ct1) { 3130/// kernel_func(ptr, item_ct1); 3131/// }); 3132/// } 3133/// Then launch the kernel through wrapper like: 3134/// typedef void(*fpt)(int *); 3135/// fpt fp = kernel_func_wrapper; 3136/// dpct::kernel_launcher::launch(fp, dpct::dim3(1), dpct::dim3(1), 0, 0, 3137/// device_ptr); 3138/// If the origin function type is erased, then need to register it first: 3139/// void *fp = (void *)wrapper_register(&kernel_func_wrapper).get(); 3140/// dpct::kernel_launcher::launch(fp, dpct::dim3(1), dpct::dim3(1), args, 3141/// 0, 0); 3142class kernel_launcher { 3143template < typename FuncT, typename ArgSelector, std::size_t... Index > 3144static void launch_helper (FuncT && func , ArgSelector & selector , 3145std::index_sequence < Index... > ) { 3146func ( selector . template get < Index > ()...); 3147} 3148static void set_execution_config (dim3 group_range , dim3 local_range , 3149unsigned int local_mem_size , 3150queue_ptr que ) { 3151if ( que ) { 3152_que = que ; 3153} else { 3154_que = & get_default_queue (); 3155} 3156_nr = sycl:: nd_range < 3 > ( 3157static_cast < sycl:: range < 3 >> ( group_range * local_range ), 3158static_cast < sycl::range < 3 >> ( local_range )); 3159_local_mem_size = local_mem_size ; 3160 3161 3162}; 3163static inline std::mutex kernel_function_ptr_map_mutex ; 3164 3165public : 3166/// Variables for storing execution configuration. 3167static inline thread_local sycl::queue * _que = nullptr ; 3168static inline thread_local sycl::nd_range < 3 > _nr = sycl:: nd_range < 3 > (); 3169static inline thread_local unsigned int _local_mem_size = 0 ; 3170/// Map for retrieving launchable functor from a raw pointer. 3171static inline std::map < 3172const void * , 3173std::function < void(dim3, dim3, void ** , unsigned int, queue_ptr) >> 3174kernel_function_ptr_map = {}; 3175 3176/// Registers a kernel function pointer with a corresponding launchable 3177/// functor. 3178/// \param [in] func Pointer to the kernel function. 3179/// \param [in] launcher Functor to handle kernel invocation. 3180static void register_kernel_ptr ( 3181const void * func , 3182std::function < void(dim3, dim3, void ** , unsigned int, queue_ptr) > 3183launcher ) { 3184std::lock_guard < std::mutex > lock (kernel_function_ptr_map_mutex); 3185kernel_function_ptr_map [ func ] = std:: move ( launcher ); 3186} 3187/// Launches a kernel function with arguments provided directly through 3188/// kernel function wrapper. 3189/// \tparam FuncT Type of the kernel function wrapper. 3190/// \tparam ArgsT Types of kernel arguments. 3191/// \param [in] func Pointer to the kernel function wrapper. 3192/// \param [in] group_range SYCL group range. 3193/// \param [in] local_range SYCL local range. 3194/// \param [in] local_mem_size The size of local memory required by the 3195/// kernel function. \param [in] que SYCL queue used to execute kernel. 3196/// \param [in] args Kernel arguments. 3197template < typename FuncT, typename ... ArgsT > 3198static std::enable_if_t < std::is_invocable_v < FuncT * , ArgsT... > , void > 3199launch (FuncT * func , dim3 group_range , dim3 local_range , 3200unsigned int local_mem_size , queue_ptr que , ArgsT... args ) { 3201set_execution_config ( group_range , local_range , local_mem_size , que ); 3202func ( args ...); 3203} 3204/// Launches a kernel function through registered kernel function 3205/// wrapper. \param [in] func Pointer to the registered kernel function 3206/// wrapper. \param [in] group_range SYCL group range. \param [in] 3207/// local_range SYCL local range. \param [in] args Array of pointers to 3208/// kernel arguments. \param [in] local_mem_size The size of local 3209/// memory required by the kernel function. \param [in] que SYCL queue 3210/// used to execute kernel. 3211static void launch ( const void * func , dim3 group_range , dim3 local_range , 3212void ** args , unsigned int local_mem_size , 3213queue_ptr que ) { 3214std::lock_guard < std::mutex > lock (kernel_function_ptr_map_mutex); 3215auto Iter = kernel_function_ptr_map . find ( func ); 3216if ( Iter == kernel_function_ptr_map . end ()) { 3217throw std:: runtime_error ( "dpct::launch() : no registered " 3218"kernel function wrapper found." ); 3219} 3220( Iter -> second )( group_range , local_range , args , local_mem_size , que ); 3221} 3222/// Launches a kernel function with packed arguments through kernel 3223/// function wrapper. 3224/// \tparam FuncT Type of the kernel function wrapper. 3225/// \param [in] func Pointer to the kernel function wrapper. 3226/// \param [in] group_range SYCL group range. 3227/// \param [in] local_range SYCL local range. 3228/// \param [in] args Array of pointers to kernel arguments. 3229/// \param [in] local_mem_size The size of local memory required by the 3230/// kernel function. \param [in] que SYCL queue used to execute kernel. 3231template < typename FuncT > 3232static std::enable_if_t < std::is_function_v < FuncT > , void > 3233launch (FuncT * func , dim3 group_range , dim3 local_range , void ** args , 3234unsigned int local_mem_size , queue_ptr que ) { 3235constexpr size_t p_num = args_selector < 0 , 0 , FuncT > :: params_num ; 3236set_execution_config ( group_range , local_range , local_mem_size , que ); 3237args_selector < p_num, p_num, FuncT > selector (args, nullptr); 3238launch_helper ( func , selector , std::make_index_sequence < p_num > {}); 3239} 3240}; // COPY from DPCT head file 3241// /opt/intel/oneapi/dpcpp-ct/latest/include/dpct/kernel.hpp 3242 3243// /opt/intel/oneapi/dpcpp-ct/latest/include/dpct/util.hpp 3244template < typename T > 3245T select_from_sub_group ( 3246sycl::sub_group g , 3247T x , 3248int remote_local_id , 3249int logical_sub_group_size = 32 ) { 3250unsigned int start_index = g . get_local_linear_id () / 3251logical_sub_group_size * 3252logical_sub_group_size ; 3253return sycl:: select_from_group ( 3254g , x , start_index + remote_local_id % logical_sub_group_size ); 3255} 3256 3257// /opt/intel/oneapi/dpcpp-ct/latest/include/dpct/math.hpp 3258template < typename T > 3259void ldmatrix (uintptr_t addr , T * m , bool trans = false, unsigned mat = 0 ) { 3260auto sg = sycl::ext::oneapi::this_work_item:: get_sub_group (); 3261int lane = sg . get_local_linear_id (); 3262 3263int lane_group8_row = lane / 8 ; 3264int lane_group8_col = lane % 8 ; 3265 3266if (! trans ) { 3267// calculate the source lane 3268int src_lane = 2 * lane_group8_row ; 3269if ( lane_group8_col >= 4 ) 3270src_lane += 1 ; 3271 3272// Broadcast the address from the source lane 3273auto recv_addr_uintp = 3274dpct:: select_from_sub_group ( sg , addr , mat * 8 + src_lane ); 3275 3276// Cast the received address from uintptr_t to the type of 'm' 3277auto recv_addr = reinterpret_cast < T *> ( recv_addr_uintp ); 3278 3279// Non-transposed load 3280* m = recv_addr [ lane_group8_col % 4 ]; 3281} else { 3282// calculate the source lane 3283int src_lane = ( lane % 4 ) * 2 ; 3284 3285// Broadcast the address from the source lane 3286auto recv_addr_uintp_1 = 3287dpct:: select_from_sub_group ( sg , addr , mat * 8 + src_lane ); 3288auto recv_addr_uintp_2 = 3289dpct:: select_from_sub_group ( sg , addr , mat * 8 + src_lane + 1 ); 3290 3291// Cast the received address from uintptr_t to 'half *' 3292auto recv_addr_1 = reinterpret_cast < sycl::half *> ( recv_addr_uintp_1 ); 3293auto recv_addr_2 = reinterpret_cast < sycl::half *> ( recv_addr_uintp_2 ); 3294 3295// Transposed load 3296int index = lane / 4 ; 3297sycl::half val0 = recv_addr_1 [ index ]; 3298sycl::half val1 = recv_addr_2 [ index ]; 3299 3300// Combine the two 16-bits into one 32-bit value 3301sycl::half2 val = sycl:: half2 ( val0 , val1 ); 3302* m = * reinterpret_cast < T *> ( & val ); 3303} 3304} 3305 3306template < typename T > 3307void ldmatrix (uintptr_t addr , T * m1 , T * m2 , bool trans = false) { 3308// Load 1st matrix 3309ldmatrix ( addr , m1 , trans , 0 ); 3310// Load 2nd matrix 3311ldmatrix ( addr , m2 , trans , 1 ); 3312} 3313 3314template < typename T > 3315void ldmatrix ( 3316uintptr_t addr , T * m1 , T * m2 , T * m3 , T * m4 , bool trans = false) { 3317// Load 1st matrix 3318ldmatrix ( addr , m1 , trans , 0 ); 3319// Load 2nd matrix 3320ldmatrix ( addr , m2 , trans , 1 ); 3321// Load 3rd matrix 3322ldmatrix ( addr , m3 , trans , 2 ); 3323// Load 4th matrix 3324ldmatrix ( addr , m4 , trans , 3 ); 3325} 3326 3327// /opt/intel/oneapi/dpcpp-ct/latest/include/dpct/math.hpp 3328 3329/// A helper struct that defines the pack type for the input matrix 3330/// fragments 3331/// of mma() function based on the type of input matrix fragments. 3332/// The MMAType struct is specialized for different types of input matrices. 3333/// Currently, the specialization for f16, bf16 and s8 types is defined 3334/// below. \tparam [in] T The type of the input matrix fragments 3335template < typename T > 3336struct MMAType { 3337using PackType = uint32_t; 3338}; 3339 3340/// Each work item of a sub-group (limited to size 32) calling this function 3341/// calculates a subset fragment for the output matrix D using MAD operation 3342/// on A, B & C matrix fragments (D = A * B + C). Current supported shapes & 3343/// types: 3344/// - m8n8k4 (f32.f16.f16.f32) 3345/// - m8n8k16 (s32.s8.s8.s32) 3346/// - m16n8k8 (f32.f16.f16.f32 & f32.bf16.bf16.f32) 3347/// - m16n8k16 (f32.f16.f16.f32 & s32.s8.s8.s32) 3348/// - m16n8k32 (s32.s8.s8.s32) 3349/// Here, m, n & k define the shapes of A, B & C matrices respectively 3350/// (A = [m x k], B = [k x n], C = [m x n]). 3351/// \tparam [in] M The rows of A, C & D matrices 3352/// \tparam [in] N The columns of B, C, D matrices 3353/// \tparam [in] K The columns & rows of A & B matrices respectively 3354/// \tparam [in] ABType The type of the input matrix (A & B) fragment 3355/// \tparam [in] CDType The type of the output matrix (C & D) fragment 3356/// \param [out] d_mat_frag The fragment of the output matrix D to store the 3357/// result of A * B + C 3358/// \param [in] a_mat_frag The fragment of the input matrix A to be 3359/// multiplied with B matrix fragment \param [in] b_mat_frag The fragment of 3360/// the input matrix B to be multiplied with A matrix fragment \param [in] 3361/// c_mat_frag The fragment of the input matrix C to be added with the 3362/// result of A * B fragments 3363template < int M , int N , int K , typename ABType, typename CDType > 3364void mma ( 3365volatile void ** d_mat_frag , 3366void * a_mat_frag , 3367void * b_mat_frag , 3368void * c_mat_frag ) { 3369auto d = reinterpret_cast < volatile CDType **> ( d_mat_frag ); 3370auto a = 3371reinterpret_cast < typename MMAType < ABType > ::PackType *> ( a_mat_frag ); 3372auto b = 3373reinterpret_cast < typename MMAType < ABType > ::PackType *> ( b_mat_frag ); 3374auto c = reinterpret_cast < CDType *> ( c_mat_frag ); 3375 3376auto sg = sycl::ext::oneapi::this_work_item:: get_sub_group (); 3377int lane = sg . get_local_linear_id (); 3378 3379static_assert( 3380( M == 8 && N == 8 && K == 4 ) || ( M == 8 && N == 8 && K == 16 ) || 3381( M == 16 && N == 8 && K == 8 ) || ( M == 16 && N == 8 && K == 16 ) || 3382( M == 16 && N == 8 && K == 32 ), 3383"Unsupported MMA shape!" ); 3384 3385short row_load_offset = 4 * ( lane >> 2 ); 3386short col_load_offset = 8 * ( lane % 4 ); 3387 3388if constexpr ( M == 8 && N == 8 && K == 4 ) { 3389if constexpr (std:: is_floating_point_v < CDType > ) { 3390col_load_offset = row_load_offset % 16 ; 3391 3392// Init D matrix with fragments of C matrix 3393* d [ 0 ] = c [ 0 ]; 3394* d [ 1 ] = c [ 1 ]; 3395* d [ 2 ] = c [ 2 ]; 3396* d [ 3 ] = c [ 3 ]; 3397* d [ 4 ] = c [ 4 ]; 3398* d [ 5 ] = c [ 5 ]; 3399* d [ 6 ] = c [ 6 ]; 3400* d [ 7 ] = c [ 7 ]; 3401 3402// Calculate the row and col offset indices to iterate through the row 3403// & col fragments of A & B matrices 3404int r_ind = ( lane % 2 ) ? 1 : 0 ; 3405int c_ind = (( lane % 4 ) / 2 ) ? 2 : 0 ; 3406 3407// Each sub-group is responsible for computing a fragment size of 8*8 3408// elements of matrix D for each of 4 MMA computations. 3409// Each work item computes 8 elements of matrix D by gathering 3410// their corresponding col & row matrix fragments of length k (4) 3411// from A & B matrices respectively using below mapping logic: 3412// row0 = (i % 4) if (lane < 16) else (i % 4) + 4 3413// col0 = (lane % 4) 3414// As each row & col fragment of A & B matrices is distributed across 3415// 4 work items, each iteration of below loop loads a partial fragment 3416// of matrix A (row) and matrix B (col) using the row & col offsets. 3417typename MMAType < ABType > ::PackType recv_a [ 2 ], recv_b [ 2 ]; 3418 3419for (int i = 0 ; i < 4 ; i ++ ) { 3420// Load partial fragment from col0 of matrix A ({a0, a1}) 3421recv_a [ 0 ] = 3422dpct:: select_from_sub_group ( sg , a [ 0 ], row_load_offset + i ); 3423// Load partial fragment from col0 of matrix A ({a2, a3}) 3424recv_a [ 1 ] = 3425dpct:: select_from_sub_group ( sg , a [ 1 ], row_load_offset + i ); 3426 3427// Load partial fragment from row0 of matrix B ({b0, b1}) 3428recv_b [ 0 ] = 3429dpct:: select_from_sub_group ( sg , b [ 0 ], col_load_offset + i ); 3430// Load partial fragment from row0 of matrix B ({b2, b3}) 3431recv_b [ 1 ] = 3432dpct:: select_from_sub_group ( sg , b [ 1 ], col_load_offset + i ); 3433 3434auto ra = reinterpret_cast < ABType *> ( recv_a ); 3435auto rb = reinterpret_cast < ABType *> ( recv_b ); 3436 3437// Each work item calculates a partial product of A & B matrix 3438// fragments and adds it to the corresponding D matrix fragment (for 3439// even work item indices) d0 += col0{ a0 } * row0{ b0 } d1 += col0{ 3440// a0 } * row0{ b1 } d2 += col1{ a2 } * row0{ b0 } d3 += col1{ a2 } 3441// * row0{ b1 } (for odd work item indices) d0 += col0{ a1 } * row0{ 3442// b2 } d1 += col0{ a1 } * row0{ b3 } d2 += col1{ a3 } * row0{ b2 } 3443// d3 += col1{ a3 } * row0{ b3 } 3444* d [ 0 ] += 3445static_cast < float > ( ra [ r_ind ]) * static_cast < float > ( rb [ c_ind ]); 3446* d [ 1 ] += static_cast < float > ( ra [ r_ind ]) * 3447static_cast < float > ( rb [ c_ind + 1 ]); 3448* d [ 2 ] += static_cast < float > ( ra [ r_ind + 2 ]) * 3449static_cast < float > ( rb [ c_ind ]); 3450* d [ 3 ] += static_cast < float > ( ra [ r_ind + 2 ]) * 3451static_cast < float > ( rb [ c_ind + 1 ]); 3452 3453// Load partial fragment from row1 of matrix B ({b0, b1}) 3454recv_b [ 0 ] = 3455dpct:: select_from_sub_group ( sg , b [ 0 ], col_load_offset + i + 16 ); 3456// Load partial fragment from row1 of matrix B ({b2, b3}) 3457recv_b [ 1 ] = 3458dpct:: select_from_sub_group ( sg , b [ 1 ], col_load_offset + i + 16 ); 3459 3460// (for even work item indices) 3461// d0 += col0{ a0 } * row1{ b0 } 3462// d1 += col0{ a0 } * row1{ b1 } 3463// d2 += col1{ a2 } * row1{ b0 } 3464// d3 += col1{ a2 } * row1{ b1 } 3465// (for odd work item indices) 3466// d0 += col0{ a1 } * row1{ b2 } 3467// d1 += col0{ a1 } * row1{ b3 } 3468// d2 += col1{ a3 } * row1{ b2 } 3469// d3 += col1{ a3 } * row1{ b3 } 3470* d [ 4 ] += 3471static_cast < float > ( ra [ r_ind ]) * static_cast < float > ( rb [ c_ind ]); 3472* d [ 5 ] += static_cast < float > ( ra [ r_ind ]) * 3473static_cast < float > ( rb [ c_ind + 1 ]); 3474* d [ 6 ] += static_cast < float > ( ra [ r_ind + 2 ]) * 3475static_cast < float > ( rb [ c_ind ]); 3476* d [ 7 ] += static_cast < float > ( ra [ r_ind + 2 ]) * 3477static_cast < float > ( rb [ c_ind + 1 ]); 3478} 3479} 3480} else if constexpr ( M == 8 && N == 8 && K == 16 ) { 3481if constexpr (std:: is_integral_v < ABType > ) { 3482// Init D matrix with fragments of C matrix 3483* d [ 0 ] = c [ 0 ]; 3484* d [ 1 ] = c [ 1 ]; 3485 3486// Each sub-group is responsible for computing a fragment size of 16*8 3487// elements of matrix D. 3488// Each work item computes 2 elements of matrix D by gathering 3489// their corresponding row & col matrix fragments of length k (16) 3490// from A & B matrices respectively using below mapping logic: 3491// row0 = ((lane % 4) * 4) + i 3492// col0 = (lane >> 2) 3493// As each row & col fragment of A & B matrices is distributed across 3494// 4 work items, each iteration of below loop loads a partial fragment 3495// of matrix A (row) and matrix B (col) using the row & col offsets. 3496for (int i = 0 ; i < 4 ; i ++ ) { 3497typename MMAType < ABType > ::PackType recv_a , recv_b [ 2 ]; 3498 3499// Load partial fragment from row0 of matrix A ({a0, a1, a2, a3}) 3500recv_a = dpct:: select_from_sub_group ( sg , a [ 0 ], row_load_offset + i ); 3501// Load partial fragment from col0 of matrix B ({b0, b1, b2, b3}) 3502recv_b [ 0 ] = 3503dpct:: select_from_sub_group ( sg , b [ 0 ], col_load_offset + i ); 3504// Load partial fragment from col1 of matrix B ({b0, b1, b2, b3}) 3505recv_b [ 1 ] = 3506dpct:: select_from_sub_group ( sg , b [ 0 ], col_load_offset + i + 4 ); 3507 3508auto a = reinterpret_cast < ABType *> ( & recv_a ); 3509auto b = reinterpret_cast < ABType *> ( recv_b ); 3510 3511// Each work item calculates a partial product of A & B matrix 3512// fragments and adds it to the corresponding D matrix fragment d0 3513// += row0{ a0, a1, a2, a3 } * col0{ b0, b1, b2, b3 } d1 += row0{ 3514// a0, a1, a2, a3 } * col1{ b0, b1, b2, b3 } d2 += row0{ a0, a1, a2, 3515// a3 } * col0{ b0, b1, b2, b3 } d3 += row0{ a0, a1, a2, a3 } * 3516// col1{ b0, b1, b2, b3 } 3517for (int j = 0 ; j < 4 ; j ++ ) { 3518* d [ 0 ] += a [ j ] * b [ j ]; 3519* d [ 1 ] += a [ j ] * b [ j + 4 ]; 3520} 3521} 3522} 3523} else if constexpr ( M == 16 && N == 8 && K == 8 ) { 3524if constexpr (std:: is_floating_point_v < CDType > ) { 3525// Init D matrix fragment with C matrix fragment 3526* d [ 0 ] = c [ 0 ]; 3527* d [ 1 ] = c [ 1 ]; 3528* d [ 2 ] = c [ 2 ]; 3529* d [ 3 ] = c [ 3 ]; 3530 3531// Each sub-group is responsible for computing a fragment size of 16*8 3532// elements of matrix D. 3533// Each work item computes 4 elements of matrix D by gathering 3534// their corresponding row & col matrix fragments of length k (8) 3535// from A & B matrices respectively using below mapping logic: 3536// row0 = (lane >> 2) & row1 = (lane >> 2) + 8 3537// col0 = (lane % 4) * 2 + (i & 0x1) 3538// As each row & col fragment of A & B matrices is distributed across 3539// 4 work items, each iteration of below loop loads a partial fragment 3540// of matrix A (row) and matrix B (col) using the row & col offsets. 3541for (int i = 0 ; i < 4 ; i ++ ) { 3542typename MMAType < ABType > ::PackType recv_a [ 2 ], recv_b [ 2 ]; 3543 3544// Load partial fragment from row0 of matrix A ({a0, a1}) 3545recv_a [ 0 ] = 3546dpct:: select_from_sub_group ( sg , a [ 0 ], row_load_offset + i ); 3547// Load partial fragment from row1 of matrix A ({a2, a3}) 3548recv_a [ 1 ] = 3549dpct:: select_from_sub_group ( sg , a [ 1 ], row_load_offset + i ); 3550// Load partial fragment from col0 of matrix B ({b0, b1}) 3551recv_b [ 0 ] = 3552dpct:: select_from_sub_group ( sg , b [ 0 ], col_load_offset + i ); 3553// Load partial fragment from col1 of matrix B ({b0, b1}) 3554recv_b [ 1 ] = 3555dpct:: select_from_sub_group ( sg , b [ 0 ], col_load_offset + i + 4 ); 3556 3557auto ra = reinterpret_cast < ABType *> ( recv_a ); 3558auto rb = reinterpret_cast < ABType *> ( recv_b ); 3559 3560// Each work item calculates a partial product of A & B matrix 3561// fragments and adds it to the corresponding D matrix fragment d0 3562// += row0{ a0, a1 } * col0{ b0, b1 } d1 += row0{ a0, a1 } * col1{ 3563// b0, b1 } d2 += row1{ a2, a3 } * col0{ b0, b1 } d3 += row1{ a2, a3 3564// } * col1{ b0, b1 } 3565for (int j = 0 ; j < 2 ; j ++ ) { 3566* d [ 0 ] += static_cast < float > ( ra [ j ]) * static_cast < float > ( rb [ j ]); 3567* d [ 1 ] += 3568static_cast < float > ( ra [ j ]) * static_cast < float > ( rb [ j + 2 ]); 3569* d [ 2 ] += 3570static_cast < float > ( ra [ j + 2 ]) * static_cast < float > ( rb [ j ]); 3571* d [ 3 ] += 3572static_cast < float > ( ra [ j + 2 ]) * static_cast < float > ( rb [ j + 2 ]); 3573} 3574} 3575} 3576} else if constexpr ( M == 16 && N == 8 && K == 16 ) { 3577if constexpr (std:: is_floating_point_v < CDType > ) { 3578// Init D matrix fragment with C matrix fragment 3579* d [ 0 ] = c [ 0 ]; 3580* d [ 1 ] = c [ 1 ]; 3581* d [ 2 ] = c [ 2 ]; 3582* d [ 3 ] = c [ 3 ]; 3583 3584// Each sub-group is responsible for computing a fragment size of 16*8 3585// elements of matrix D. 3586// Each work item computes 4 elements of matrix D by gathering 3587// their corresponding row & col matrix fragments of length k (8) 3588// from A & B matrices respectively using below mapping logic: 3589// row0 = (lane >> 2) & row1 = (lane >> 2) + 8 3590// col0 = (lane % 4) * 2 & col1 = (lane % 4) * 2 + 1 3591// As each row & col fragment of A & B matrices is distributed across 3592// 4 work items, each iteration of below loop loads a partial fragment 3593// of matrix A (row) and matrix B (col) using the row & col offsets. 3594for (int i = 0 ; i < 4 ; i ++ ) { 3595typename MMAType < ABType > ::PackType recv_a [ 4 ], recv_b [ 4 ]; 3596 3597// Load partial fragment from row0 of matrix A ({a0, a1}) 3598recv_a [ 0 ] = 3599dpct:: select_from_sub_group ( sg , a [ 0 ], row_load_offset + i ); 3600// Load partial fragment from row0 of matrix A ({a2, a3}) 3601recv_a [ 1 ] = 3602dpct:: select_from_sub_group ( sg , a [ 2 ], row_load_offset + i ); 3603// Load partial fragment from row1 of matrix A ({a0, a1}) 3604recv_a [ 2 ] = 3605dpct:: select_from_sub_group ( sg , a [ 1 ], row_load_offset + i ); 3606// Load partial fragment from row1 of matrix A ({a2, a3}) 3607recv_a [ 3 ] = 3608dpct:: select_from_sub_group ( sg , a [ 3 ], row_load_offset + i ); 3609 3610// Load partial fragment from col0 of matrix B ({b0, b1}) 3611recv_b [ 0 ] = 3612dpct:: select_from_sub_group ( sg , b [ 0 ], col_load_offset + i ); 3613// Load partial fragment from col0 of matrix B ({b2, b3}) 3614recv_b [ 1 ] = 3615dpct:: select_from_sub_group ( sg , b [ 1 ], col_load_offset + i ); 3616// Load partial fragment from col1 of matrix B ({b0, b1}) 3617recv_b [ 2 ] = 3618dpct:: select_from_sub_group ( sg , b [ 0 ], col_load_offset + 4 + i ); 3619// Load partial fragment from col1 of matrix B ({b2, b3}) 3620recv_b [ 3 ] = 3621dpct:: select_from_sub_group ( sg , b [ 1 ], col_load_offset + 4 + i ); 3622 3623auto ra = reinterpret_cast < ABType *> ( recv_a ); 3624auto rb = reinterpret_cast < ABType *> ( recv_b ); 3625 3626// Each work item calculates a partial product of A & B matrix 3627// fragments and adds it to the corresponding D matrix fragment d0 3628// += row0{ a0, a1, a2, a3 } * col0{ b0, b1, b2, b3 } d1 += row0{ 3629// a0, a1, a2, a3 } * col1{ b0, b1, b2, b3 } d2 += row1{ a0, a1, a2, 3630// a3 } * col0{ b0, b1, b2, b3 } d3 += row1{ a0, a1, a2, a3 } * 3631// col1{ b0, b1, b2, b3 } 3632for (int j = 0 ; j < 4 ; j ++ ) { 3633* d [ 0 ] += static_cast < CDType > ( ra [ j ]) * static_cast < CDType > ( rb [ j ]); 3634* d [ 1 ] += 3635static_cast < CDType > ( ra [ j ]) * static_cast < CDType > ( rb [ j + 4 ]); 3636* d [ 2 ] += 3637static_cast < CDType > ( ra [ j + 4 ]) * static_cast < CDType > ( rb [ j ]); 3638* d [ 3 ] += static_cast < CDType > ( ra [ j + 4 ]) * 3639static_cast < CDType > ( rb [ j + 4 ]); 3640} 3641} 3642} else if constexpr (std:: is_integral_v < ABType > ) { 3643// Init D matrix with fragments of C matrix 3644* d [ 0 ] = c [ 0 ]; 3645* d [ 1 ] = c [ 1 ]; 3646* d [ 2 ] = c [ 2 ]; 3647* d [ 3 ] = c [ 3 ]; 3648 3649// Each sub-group is responsible for computing a fragment size of 16*8 3650// elements of matrix D. 3651// Each work item computes 4 elements of matrix D by gathering 3652// their corresponding row & col matrix fragments of length k (8) 3653// from A & B matrices respectively using below mapping logic: 3654// row0 = (lane >> 2) & row1 = (lane >> 2) + 8 3655// col0 = (lane % 4) * 2 & col1 = (lane % 4) * 2 + 1 3656// As each row & col fragment of A & B matrices is distributed across 3657// 4 work items, each iteration of below loop loads a partial fragment 3658// of matrix A (row) and matrix B (col) using the row & col offsets. 3659for (int i = 0 ; i < 4 ; i ++ ) { 3660typename MMAType < ABType > ::PackType recv_a [ 2 ], recv_b [ 2 ]; 3661 3662// Load partial fragment from row0 of matrix A ({a0, a1, a2, a3}) 3663recv_a [ 0 ] = 3664dpct:: select_from_sub_group ( sg , a [ 0 ], row_load_offset + i ); 3665// Load partial fragment from row1 of matrix A ({a4, a5, a6, a7}) 3666recv_a [ 1 ] = 3667dpct:: select_from_sub_group ( sg , a [ 1 ], row_load_offset + i ); 3668// Load partial fragment from col0 of matrix B ({b0, b1, b2, b3}) 3669recv_b [ 0 ] = 3670dpct:: select_from_sub_group ( sg , b [ 0 ], col_load_offset + i ); 3671// Load partial fragment from col1 of matrix B ({b4, b5, b6, b7}) 3672recv_b [ 1 ] = 3673dpct:: select_from_sub_group ( sg , b [ 0 ], col_load_offset + i + 4 ); 3674 3675auto ra = reinterpret_cast < ABType *> ( recv_a ); 3676auto rb = reinterpret_cast < ABType *> ( recv_b ); 3677 3678// Each work item calculates a partial product of A & B matrix 3679// fragments and adds it to the corresponding D matrix fragment d0 3680// += row0{ a0, a1, a2, a3 } * col0{ b0, b1, b2, b3 } d1 += row0{ 3681// a0, a1, a2, a3 } * col1{ b4, b5, b6, b7 } d2 += row1{ a4, a5, a6, 3682// a7 } * col0{ b0, b1, b2, b3 } d3 += row1{ a4, a5, a6, a7 } * 3683// col1{ b4, b5, b6, b7 } 3684for (int i = 0 ; i < 4 ; i ++ ) { 3685* d [ 0 ] += ra [ i ] * rb [ i ]; 3686* d [ 1 ] += ra [ i ] * rb [ i + 4 ]; 3687* d [ 2 ] += ra [ i + 4 ] * rb [ i ]; 3688* d [ 3 ] += ra [ i + 4 ] * rb [ i + 4 ]; 3689} 3690} 3691} 3692} else if constexpr ( M == 16 && N == 8 && K == 32 ) { 3693if constexpr (std:: is_integral_v < ABType > ) { 3694// Init D matrix with fragments of C matrix 3695* d [ 0 ] = c [ 0 ]; 3696* d [ 1 ] = c [ 1 ]; 3697* d [ 2 ] = c [ 2 ]; 3698* d [ 3 ] = c [ 3 ]; 3699 3700// Each sub-group is responsible for computing a fragment size of 16*8 3701// elements of matrix D. 3702// Each work item computes 4 elements of matrix D by gathering 3703// their corresponding row & col matrix fragments of length k (32) 3704// from A & B matrices respectively using below mapping logic: 3705// row0 = (lane >> 2) & row1 = (lane >> 2) + 8 3706// col0 = ((lane % 4) * 4) + (i & 0x3) & col1 = ((lane % 4) * 4) + (i 3707// & 0x3) As each row & col fragment of A & B matrices is distributed 3708// across 4 work items, each iteration of below loop loads a partial 3709// fragment of matrix A (row) and matrix B (col) using the row & col 3710// offsets. 3711for (int i = 0 ; i < 4 ; i ++ ) { 3712typename MMAType < ABType > ::PackType recv_a [ 2 ], recv_b [ 2 ]; 3713 3714// Load partial fragment from row0 of matrix A ({a0, a1, a2, a3}) 3715recv_a [ 0 ] = 3716dpct:: select_from_sub_group ( sg , a [ 0 ], row_load_offset + i ); 3717// Load partial fragment from row1 of matrix A ({a4, a5, a6, a7}) 3718recv_a [ 1 ] = 3719dpct:: select_from_sub_group ( sg , a [ 1 ], row_load_offset + i ); 3720// Load partial fragment from col0 of matrix B ({b0, b1, b2, b3}) 3721recv_b [ 0 ] = 3722dpct:: select_from_sub_group ( sg , b [ 0 ], col_load_offset + i ); 3723// Load partial fragment from col1 of matrix B ({b0, b1, b2, b3}) 3724recv_b [ 1 ] = 3725dpct:: select_from_sub_group ( sg , b [ 0 ], col_load_offset + i + 4 ); 3726 3727auto a = reinterpret_cast < ABType *> ( recv_a ); 3728auto b = reinterpret_cast < ABType *> ( recv_b ); 3729 3730// Each work item calculates a partial product of A & B matrix 3731// fragments and adds it to the corresponding D matrix fragment d0 3732// += row0{ a0, a1, a2, a3 } * col0{ b0, b1, b2, b3 } d1 += row0{ 3733// a0, a1, a2, a3 } * col1{ b0, b1, b2, b3 } d2 += row1{ a4, a5, a6, 3734// a7 } * col0{ b0, b1, b2, b3 } d3 += row1{ a4, a5, a6, a7 } * 3735// col1{ b0, b1, b2, b3 } 3736for (int j = 0 ; j < 4 ; j ++ ) { 3737* d [ 0 ] += a [ j ] * b [ j ]; 3738* d [ 1 ] += a [ j ] * b [ j + 4 ]; 3739* d [ 2 ] += a [ j + 4 ] * b [ j ]; 3740* d [ 3 ] += a [ j + 4 ] * b [ j + 4 ]; 3741} 3742} 3743 3744for (int i = 0 ; i < 4 ; i ++ ) { 3745typename MMAType < ABType > ::PackType recv_a [ 2 ], recv_b [ 2 ]; 3746 3747// Load partial fragment from row0 of matrix A ({a8, a9, a10, a11}) 3748recv_a [ 0 ] = 3749dpct:: select_from_sub_group ( sg , a [ 2 ], row_load_offset + i ); 3750// Load partial fragment from row1 of matrix A ({a12, a13, a14, 3751// a15}) 3752recv_a [ 1 ] = 3753dpct:: select_from_sub_group ( sg , a [ 3 ], row_load_offset + i ); 3754// Load partial fragment from col0 of matrix B ({b4, b5, b6, b7}) 3755recv_b [ 0 ] = 3756dpct:: select_from_sub_group ( sg , b [ 1 ], col_load_offset + i ); 3757// Load partial fragment from col1 of matrix B ({b4, b5, b6, b7}) 3758recv_b [ 1 ] = 3759dpct:: select_from_sub_group ( sg , b [ 1 ], col_load_offset + i + 4 ); 3760 3761auto a = reinterpret_cast < ABType *> ( recv_a ); 3762auto b = reinterpret_cast < ABType *> ( recv_b ); 3763 3764// Each work item calculates a partial product of A & B matrix 3765// fragments and adds it to the corresponding D matrix fragment d0 3766// += row0{ a8, a9, a10, a11 } * col0{ b4, b5, b6, b7 } d1 += row0{ 3767// a8, a9, a10, a11 } * col1{ b4, b5, b6, b7 } d2 += row1{ a12, a13, 3768// a14, a15 } * col0{ b4, b5, b6, b7 } d3 += row1{ a12, a13, a14, 3769// a15 } * col1{ b4, b5, b6, b7 } 3770for (int j = 0 ; j < 4 ; j ++ ) { 3771* d [ 0 ] += a [ j ] * b [ j ]; 3772* d [ 1 ] += a [ j ] * b [ j + 4 ]; 3773* d [ 2 ] += a [ j + 4 ] * b [ j ]; 3774* d [ 3 ] += a [ j + 4 ] * b [ j + 4 ]; 3775} 3776} 3777} 3778} 3779} 3780} // COPY from DPCT head files 3781 3782#endif // GGML_SYCL_DPCT_HELPER_HPP