33 using HostSizeType = min_ibf_fpga::backend_sycl::HostSizeType;
34 using Chunk = ac_int<chunk_bits, false>;
36 static constexpr bool is_emulator =
45 size_t technical_bins{};
47 size_t const minimalNumberOfMinimizers{};
48 size_t const maximalNumberOfMinimizers{};
49 sycl::queue utilitiesQueue;
51 std::function<void(
void *)> utilities_QueueDeleter = [
this](
void * ptr)
53 sycl::free(ptr, utilitiesQueue);
62 return {sycl::malloc_device<T>(count, utilitiesQueue), utilities_QueueDeleter};
68 return {sycl::malloc_host<T>(count, utilitiesQueue), utilities_QueueDeleter};
71 sycl::queue kernelQueue;
73 size_t const bufferSizeBytes;
74 size_t const numberOfKernelCopys;
80 size_t numberOfQueries;
93 auto device_selector = []()
95 if constexpr (is_emulator)
96 return sycl::ext::intel::fpga_emulator_selector_v;
98 return sycl::ext::intel::fpga_selector_v;
101 auto create_queue = [&device_selector]()
103 if constexpr (profile)
104 return sycl::queue(device_selector,
105 fpga_tools::exception_handler,
106 sycl::property::queue::enable_profiling());
108 return sycl::queue(device_selector, fpga_tools::exception_handler);
111 utilitiesQueue = create_queue();
113 sycl::device dev = utilitiesQueue.get_device();
115 if (!dev.has(sycl::aspect::usm_device_allocations) )
117 std::cerr <<
"ERROR: The selected device does not support USM device allocations\n";
120 if (!dev.has(sycl::aspect::usm_host_allocations) )
122 std::cerr <<
"ERROR: The selected device does not support USM host allocations\n";
126 kernelQueue = create_queue();
131 size_t minimalNumberOfMinimizers,
132 size_t maximalNumberOfMinimizers,
134 uint8_t
const bufferSizeMiB,
135 uint8_t
const numberOfKernelCopys) :
136 minimalNumberOfMinimizers{minimalNumberOfMinimizers},
137 maximalNumberOfMinimizers{maximalNumberOfMinimizers},
138 thresholds_size_t{
std::
move(thresholds)},
139 bufferSizeBytes(bufferSizeMiB * 1'048'576),
140 numberOfKernelCopys{numberOfKernelCopys}
144 std::chrono::steady_clock::time_point start;
146 if constexpr (profile)
151 cereal::BinaryInputArchive archive{archiveStream};
155 technical_bins = seqan::hibf::next_multiple_of_64(index.ibf().bin_count());
157 if constexpr (profile)
160 constructorDuration =
end - start;
164 size_t hash_function_count()
const
166 return index.ibf().hash_function_count();
171 return std::format(
"libraptor_search_fpga_kernel_w{}_k{}_b{}_kernels{}.so",
173 index.shape().size(),
175 numberOfKernelCopys);
180 std::chrono::steady_clock::time_point countStart;
181 std::chrono::steady_clock::time_point hostStart;
183 if constexpr (profile)
187 size_t const data_size_bytes = seqan::hibf::divide_and_ceil(index.ibf().bit_size(), 64u) * 8;
188 size_t const number_of_chunks = seqan::hibf::divide_and_ceil(data_size_bytes,
sizeof(Chunk));
189 Chunk
const * ibfData_host =
reinterpret_cast<Chunk
const *
>(index.ibf().data());
191 auto ibfData_device = malloc_device_utilities<Chunk>(number_of_chunks);
192 setupEvents[1] = utilitiesQueue.copy(ibfData_host, ibfData_device.get(), number_of_chunks);
194 if constexpr (profile)
196 printDuration(
"IBF I/O:\t", countStart, constructorDuration);
201 using RunKernelType = void (*)(sycl::queue &,
203 HostSizeType
const *,
210 HostSizeType
const *,
214 std::string const library_name = get_library_name();
215 void * handle = dlopen(library_name.
c_str(), RTLD_NOW);
225 RunKernelType RunKernel =
reinterpret_cast<RunKernelType
>(dlsym(handle, symbol_name.
c_str()));
227 char * error_message = dlerror();
229 if (error_message != NULL)
234 static_assert(
sizeof(HostSizeType) ==
sizeof(
size_t));
236 auto thresholds_device = malloc_device_utilities<HostSizeType>(thresholds_size_t.
size());
237 HostSizeType
const * thresholds_host =
reinterpret_cast<HostSizeType
const *
>(thresholds_size_t.
data());
238 setupEvents[0] = utilitiesQueue.copy(thresholds_host, thresholds_device.get(), thresholds_size_t.
size());
243 size_t const max_numberOfQueries = bufferSizeBytes / MIN_QUERY_LENGTH;
244 size_t const chunks_per_result = technical_bins / chunk_bits;
246 for (buffer_data & state : double_buffer)
248 state.queries_host = malloc_host_utilities<char>(bufferSizeBytes);
249 state.querySizes_host = malloc_host_utilities<HostSizeType>(max_numberOfQueries);
250 state.results_host = malloc_host_utilities<Chunk>(max_numberOfQueries * chunks_per_result);
252 state.kernelEvents.reserve(numberOfKernelCopys * 2 + 2);
253 state.numberOfQueries = 0;
256 auto const queueToFPGA = [&](buffer_data & state,
size_t computeIteration)
258 if (computeIteration == 0)
260 utilitiesQueue.wait();
261 if constexpr (profile)
263 printDurationFromEvent(
"Thresh. Trans.:\t", setupEvents[0]);
264 printDurationFromEvent(
"IBF Transfer:\t", setupEvents[1]);
268 std::chrono::steady_clock::time_point start;
270 if constexpr (profile)
273 RunKernel(kernelQueue,
274 state.queries_host.get(),
275 state.querySizes_host.get(),
276 state.numberOfQueries,
277 ibfData_device.get(),
278 index.ibf().bin_size(),
280 minimalNumberOfMinimizers,
281 maximalNumberOfMinimizers,
282 thresholds_device.get(),
283 state.results_host.get(),
286 if constexpr (profile)
287 printDuration(
"Queue:\t\t", start);
290 auto const waitOnFPGA = [&](buffer_data & state)
292 std::chrono::steady_clock::time_point start;
294 if constexpr (profile)
297 for (sycl::event e : state.kernelEvents)
300 if constexpr (profile)
301 printDuration(
"Wait:\t\t", start);
304 auto const outputResults = [&](buffer_data & state)
306 std::chrono::steady_clock::time_point start;
308 if constexpr (profile)
311 result_string.clear();
313 size_t const elements_per_result = technical_bins / 64;
314 size_t const elements_per_chunk = chunk_bits / 64;
316 if (elements_per_chunk * chunks_per_result != elements_per_result)
317 throw std::runtime_error(
"outputResults: elements_per_result/chunks_per_result mismatch");
319 for (
size_t queryIndex = 0; queryIndex < state.ids.size(); queryIndex++)
322 result_string += state.ids.at(queryIndex).substr(1, std::string::npos) +
'\t';
324 for (
size_t chunkOffset = 0; chunkOffset < chunks_per_result; ++chunkOffset)
326 Chunk & chunk = state.results_host.get()[queryIndex * chunks_per_result + chunkOffset];
328 for (
size_t elementOffset = 0; elementOffset < elements_per_chunk; ++elementOffset)
330 uint64_t
const & element = ((uint64_t *)&chunk)[elementOffset];
337 for (
size_t byteOffset = 0; byteOffset < 8; ++byteOffset)
339 uint8_t
const & value = ((uint8_t *)&element)[byteOffset];
348 for (
size_t bitOffset = 0; bitOffset < 8; ++bitOffset)
352 result_string +=
std::to_string(chunkOffset * chunk_bits + elementOffset * 64
353 + byteOffset * 8 + bitOffset)
361 if (
auto & last_char = result_string.back(); last_char ==
',')
364 result_string +=
'\n';
367 outputStream << result_string;
369 if constexpr (profile)
370 printDuration(
"Output:\t\t", start);
372 if constexpr (profile)
376 profilingOutput <<
"\n"
377 <<
"Kernels:\t" << getRuntime(state.kernelEvents) <<
" ms\n";
379 for (
size_t kernelId = 0; kernelId < state.kernelEvents.size(); ++kernelId)
380 profilingOutput <<
"Kernel " << kernelId <<
":\t" << getRuntime(state.kernelEvents[kernelId])
383 profilingOutput <<
"\n";
388 state.kernelEvents.clear();
391 std::ifstream inputStream(query_path, std::ios::in | std::ios::binary);
396 size_t computeIteration = 0;
397 size_t currentBufferSize = 0;
399 buffer_data * currentBufferData = double_buffer.
data();
406 if (currentBufferSize + query.
size() > bufferSizeBytes)
408 if (currentBufferData->numberOfQueries == 0)
411 if constexpr (profile)
412 printDuration(
"Host:\t\t", hostStart);
414 queueToFPGA(*currentBufferData, computeIteration);
416 currentBufferData =
std::addressof(double_buffer[computeIteration % 2]);
418 if (computeIteration >= 2)
420 waitOnFPGA(*currentBufferData);
421 outputResults(*currentBufferData);
424 if constexpr (profile)
427 currentBufferData->ids.clear();
428 currentBufferData->numberOfQueries = 0;
430 currentBufferSize = 0;
433 currentBufferData->ids.emplace_back(std::move(
id));
435 currentBufferData->querySizes_host.get()[currentBufferData->numberOfQueries] = query.
size();
438 std::memcpy(currentBufferData->queries_host.get() + currentBufferSize, query.
data(), query.
size());
440 currentBufferSize += query.
size();
441 currentBufferData->numberOfQueries++;
444 for (
size_t i = 0; i < 2; ++i)
449 if (currentBufferData->numberOfQueries)
451 if constexpr (profile)
452 printDuration(
"Host:\t\t", hostStart);
454 queueToFPGA(*currentBufferData, computeIteration);
461 currentBufferData =
std::addressof(double_buffer[(computeIteration + i) % 2]);
462 waitOnFPGA(*currentBufferData);
463 outputResults(*currentBufferData);
466 if constexpr (profile)
467 printDuration(
"Count total:\t", countStart);
471 static void printDuration(
std::string const & label,
472 std::chrono::steady_clock::time_point
const & start,
473 std::chrono::steady_clock::duration duration = std::chrono::steady_clock::duration())
477 duration +=
end - start;
478 auto const runtime = std::chrono::duration_cast<timeUnit>(duration).count();
482 profilingOutput << label << runtime <<
" ms\n";
487 static void printDurationFromEvent(
std::string const & label, sycl::event
const event)
489 uint64_t
const start =
event.get_profiling_info<sycl::info::event_profiling::command_start>();
490 uint64_t
const end =
event.get_profiling_info<sycl::info::event_profiling::command_end>();
492 uint64_t
const duration_ns =
end - start;
493 float const duration_ms =
static_cast<float>(duration_ns) / 1000000;
497 profilingOutput << label << duration_ms <<
" ms\n";
506 auto const comparator_start = [](sycl::event
const & lhs, sycl::event
const & rhs)
508 uint64_t lhsValue, rhsValue;
510 lhsValue = lhs.get_profiling_info<sycl::info::event_profiling::command_start>();
511 rhsValue = rhs.get_profiling_info<sycl::info::event_profiling::command_start>();
513 return lhsValue < rhsValue;
516 auto const comparator_end = [](sycl::event
const & lhs, sycl::event
const & rhs)
518 uint64_t lhsValue, rhsValue;
520 lhsValue = lhs.get_profiling_info<sycl::info::event_profiling::command_end>();
521 rhsValue = rhs.get_profiling_info<sycl::info::event_profiling::command_end>();
523 return lhsValue < rhsValue;
528 [&](
auto const & lhs,
auto const & rhs)
530 return comparator_start(lhs, rhs);
534 [&](
auto const & lhs,
auto const & rhs)
536 return comparator_end(lhs, rhs);
539 return getRuntime(startEvent, endEvent);
542 static double getRuntime(sycl::event
const & event)
544 return getRuntime(event, event);
547 static double getRuntime(sycl::event
const & startEvent, sycl::event
const & endEvent)
551 start = startEvent.get_profiling_info<sycl::info::event_profiling::command_start>();
552 end = endEvent.get_profiling_info<sycl::info::event_profiling::command_end>();
556 return std::chrono::duration_cast<timeUnit>(duration).count();