Uni20
Asynchronous tensor and runtime library.
Loading...
Searching...
No Matches
linalg/backends/cuda/transform.hpp
Go to the documentation of this file.
1#pragma once
2
19
20#include <array>
21#include <concepts>
22#include <cstddef>
23#include <limits>
24#include <memory>
25#include <optional>
26#include <tuple>
27#include <type_traits>
28#include <utility>
29
30namespace uni20::linalg
31{
32namespace detail::cuda_reference
33{
34
35template <class Mdspec> using transform_scalar_t = std::remove_cv_t<typename std::remove_cvref_t<Mdspec>::element_type>;
36
37template <class OutputMdspec, class... InputMdspecs>
38concept RawCudaOverwriteMdspecs =
40 is_raw_mutable_cuda_mdspec<OutputMdspec> && (is_raw_const_cuda_mdspec<InputMdspecs> && ...) &&
41 ((std::remove_cvref_t<OutputMdspec>::rank() == std::remove_cvref_t<InputMdspecs>::rank()) && ...) &&
42 (std::same_as<transform_scalar_t<OutputMdspec>, transform_scalar_t<InputMdspecs>> && ...) &&
43 (std::remove_cvref_t<OutputMdspec>::rank() == 0 ||
45
46template <class OutputMdspec, class Value>
47concept SupportedFillMdspec = RawCudaOverwriteMdspecs<OutputMdspec> &&
48 std::same_as<transform_scalar_t<OutputMdspec>, std::remove_cvref_t<Value>> &&
49 supports_elementwise_arithmetic<transform_scalar_t<OutputMdspec>>;
50
51template <class OutputMdspec, class InputMdspec, class Function>
52concept SupportedStatelessUnaryMdspecs =
53 RawCudaOverwriteMdspecs<OutputMdspec, InputMdspec> && RegisteredStatelessUnary<Function> &&
54 supports_elementwise_arithmetic<transform_scalar_t<OutputMdspec>>;
55
56template <class OutputMdspec, class InputMdspec, class Factor>
57concept SupportedScaleMdspecs =
58 RawCudaOverwriteMdspecs<OutputMdspec, InputMdspec> &&
59 supports_elementwise_scale<transform_scalar_t<OutputMdspec>, std::remove_cvref_t<Factor>>;
60
61template <class OutputMdspec, class LhsMdspec, class RhsMdspec, class Function>
62concept SupportedStatelessBinaryMdspecs =
63 RawCudaOverwriteMdspecs<OutputMdspec, LhsMdspec, RhsMdspec> && RegisteredStatelessBinary<Function> &&
64 supports_elementwise_arithmetic<transform_scalar_t<OutputMdspec>>;
65
66template <class OutputMdspec, class Factor>
67concept SupportedInplaceScaleMdspec =
68 RawCudaOverwriteMdspecs<OutputMdspec> &&
69 supports_elementwise_scale<transform_scalar_t<OutputMdspec>, std::remove_cvref_t<Factor>>;
70
71template <class OutputMdspec, class InputMdspec, class Function>
72concept SupportedInplaceStatelessBinaryMdspecs =
73 RawCudaOverwriteMdspecs<OutputMdspec, InputMdspec> && RegisteredStatelessBinary<Function> &&
74 supports_elementwise_arithmetic<transform_scalar_t<OutputMdspec>>;
75
76template <class OutputMdspec, class InputMdspec, class Factor>
77concept SupportedAddScaledMdspecs =
78 RawCudaOverwriteMdspecs<OutputMdspec, InputMdspec> &&
79 supports_elementwise_scale<transform_scalar_t<OutputMdspec>, std::remove_cvref_t<Factor>>;
80
81template <class Scalar, std::size_t InputCount, std::size_t MaximumRank> struct OverwriteTransformPlan
82{
83 using scalar_type = Scalar;
84 static constexpr std::size_t input_count = InputCount;
85 static constexpr std::size_t maximum_rank = MaximumRank;
86
88 uni20::cuda::CudaBuffer<Scalar>* output_buffer = nullptr;
89 std::array<uni20::cuda::CudaBuffer<Scalar> const*, InputCount> input_buffers{};
90 std::size_t output_offset = 0;
91 std::array<std::size_t, InputCount> input_offsets{};
92 std::size_t output_span_size = 0;
93 std::array<std::size_t, InputCount> input_span_sizes{};
94 LoweredStridedElementwisePlan<InputCount + 1, MaximumRank> elementwise_plan;
95 bool has_work = false;
96};
97
98template <class Scalar> using FillPlan = OverwriteTransformPlan<Scalar, 0, elementwise_arithmetic_maximum_rank>;
99
100template <class Scalar>
101using UnaryArithmeticPlan = OverwriteTransformPlan<Scalar, 1, elementwise_arithmetic_maximum_rank>;
102
103template <class Scalar, class Factor>
104struct ScalePlan : OverwriteTransformPlan<Scalar, 1, elementwise_scale_maximum_rank>
105{
106 Factor factor{};
107};
108
109template <class Scalar>
110using BinaryArithmeticPlan = OverwriteTransformPlan<Scalar, 2, elementwise_arithmetic_maximum_rank>;
111
112template <class Scalar, class Factor>
113struct InplaceScalePlan : OverwriteTransformPlan<Scalar, 0, elementwise_arithmetic_maximum_rank>
114{
115 Factor factor{};
116};
117
118template <class Scalar>
119using InplaceBinaryPlan = OverwriteTransformPlan<Scalar, 1, elementwise_arithmetic_maximum_rank>;
120
121template <class Scalar, class Factor>
122struct AddScaledPlan : OverwriteTransformPlan<Scalar, 1, elementwise_arithmetic_maximum_rank>
123{
124 Factor factor{};
125};
126
127template <std::size_t MaximumRank, class OutputMdspec, class... InputMdspecs>
129 ((std::remove_cvref_t<OutputMdspec>::rank() == std::remove_cvref_t<InputMdspecs>::rank()) && ...) &&
130 (std::remove_cvref_t<OutputMdspec>::rank() == 0 ||
132[[nodiscard]] bool try_make_elementwise_transform_plan(
133 OutputMdspec const& output, LoweredStridedElementwisePlan<sizeof...(InputMdspecs) + 1, MaximumRank>& plan,
134 InputMdspecs const&... inputs)
135{
136 auto host_plan = make_multi_iteration_plan(std::tuple{output.mapping(), inputs.mapping()...});
137 return try_lower_strided_elementwise_plan<MaximumRank>(host_plan, plan);
138}
139
140template <class Mdspec> [[nodiscard]] auto required_span_size(Mdspec const& mdspec) -> std::optional<std::size_t>
141{
142 auto const required = mdspec.mapping().required_span_size();
143 if (std::cmp_less(required, 0) || std::cmp_greater(required, std::numeric_limits<std::size_t>::max()))
144 return std::nullopt;
145 return static_cast<std::size_t>(required);
146}
147
148template <class Plan, class OutputMdspec, class... InputMdspecs>
149 requires RawCudaOverwriteMdspecs<OutputMdspec, InputMdspecs...> && (Plan::input_count == sizeof...(InputMdspecs))
150[[nodiscard]] auto prepare_overwrite_transform(Plan plan, OutputMdspec& output, InputMdspecs&... inputs)
151{
152 using output_type = std::remove_cvref_t<OutputMdspec>;
153
154 if constexpr (sizeof...(InputMdspecs) > 0)
155 {
156 bool extents_match = true;
157 auto check_extents = [&](auto const& input) {
158 for (std::size_t axis = 0; axis < output_type::rank(); ++axis)
159 {
160 if (output.extent(axis) != input.extent(axis)) extents_match = false;
161 }
162 };
163 (check_extents(inputs), ...);
164 if (!extents_match)
165 {
167 return plan;
168 }
169 }
170
171 if (!output.mapping().is_unique() ||
172 !try_make_elementwise_transform_plan<Plan::maximum_rank>(output, plan.elementwise_plan, inputs...))
173 {
175 return plan;
176 }
177
178 plan.has_work = plan.elementwise_plan.element_count() != 0;
179 if (!plan.has_work)
180 {
181 plan.attempt = KernelAttempt::success;
182 return plan;
183 }
184
185 auto const output_span_size = required_span_size(output);
186 if (!output_span_size)
187 {
189 return plan;
190 }
191 plan.output_span_size = *output_span_size;
192
193 auto output_descriptor = output.data_descriptor();
194 plan.output_buffer = std::addressof(output_descriptor.buffer());
195 plan.output_offset = output_descriptor.element_offset();
196 CHECK(plan.output_offset <= plan.output_buffer->size() &&
197 plan.output_span_size <= plan.output_buffer->size() - plan.output_offset,
198 plan.output_offset, plan.output_span_size, plan.output_buffer->size());
199
200 if constexpr (sizeof...(InputMdspecs) > 0)
201 {
202 std::size_t input_index = 0;
203 auto capture_input = [&](auto& input) {
204 auto const input_span_size = required_span_size(input);
205 if (!input_span_size) return false;
206
207 auto input_descriptor = input.data_descriptor();
208 auto const* input_buffer = std::addressof(input_descriptor.buffer());
209 auto const input_offset = input_descriptor.element_offset();
210 CHECK(input_offset <= input_buffer->size() && *input_span_size <= input_buffer->size() - input_offset,
211 input_offset, *input_span_size, input_buffer->size());
212
213 plan.input_buffers[input_index] = input_buffer;
214 plan.input_offsets[input_index] = input_offset;
215 plan.input_span_sizes[input_index] = *input_span_size;
216 ++input_index;
217 return true;
218 };
219 if (!(capture_input(inputs) && ...))
220 {
222 return plan;
223 }
224 }
225
226 for (std::size_t input = 0; input < Plan::input_count; ++input)
227 {
228 if (plan.output_buffer->device() != plan.input_buffers[input]->device())
229 {
231 return plan;
232 }
233 if (plan.output_buffer == plan.input_buffers[input] && plan.output_offset == plan.input_offsets[input])
234 {
236 return plan;
237 }
238 }
239
240 plan.attempt = KernelAttempt::success;
241 return plan;
242}
243
244template <class Function, class OutputMdspec, class InputMdspec>
245 requires SupportedStatelessUnaryMdspecs<OutputMdspec, InputMdspec, Function>
246[[nodiscard]] auto prepare_stateless_unary(OutputMdspec& output, InputMdspec& input)
247{
248 using scalar_type = transform_scalar_t<OutputMdspec>;
249 return prepare_overwrite_transform(UnaryArithmeticPlan<scalar_type>{}, output, input);
250}
251
252template <class OutputMdspec, class Value>
253 requires SupportedFillMdspec<OutputMdspec, Value>
254[[nodiscard]] auto prepare_fill(OutputMdspec& output)
255{
256 using scalar_type = transform_scalar_t<OutputMdspec>;
257 return prepare_overwrite_transform(FillPlan<scalar_type>{}, output);
258}
259
260template <class OutputMdspec, class InputMdspec, class Factor>
261 requires SupportedScaleMdspecs<OutputMdspec, InputMdspec, Factor>
262[[nodiscard]] auto prepare_scale(OutputMdspec& output, InputMdspec& input, Factor factor)
263{
264 using scalar_type = transform_scalar_t<OutputMdspec>;
265 using factor_type = std::remove_cvref_t<Factor>;
266 ScalePlan<scalar_type, factor_type> plan;
267 plan.factor = std::move(factor);
268 return prepare_overwrite_transform(std::move(plan), output, input);
269}
270
271template <class OutputMdspec, class Factor>
272 requires SupportedInplaceScaleMdspec<OutputMdspec, Factor>
273[[nodiscard]] auto prepare_inplace_scale(OutputMdspec& output, Factor factor)
274{
275 using scalar_type = transform_scalar_t<OutputMdspec>;
276 using factor_type = std::remove_cvref_t<Factor>;
277 InplaceScalePlan<scalar_type, factor_type> plan;
278 plan.factor = std::move(factor);
279 return prepare_overwrite_transform(std::move(plan), output);
280}
281
282template <class Function, class OutputMdspec, class InputMdspec>
283 requires SupportedInplaceStatelessBinaryMdspecs<OutputMdspec, InputMdspec, Function>
284[[nodiscard]] auto prepare_inplace_stateless_binary(OutputMdspec& output, InputMdspec& input)
285{
286 using scalar_type = transform_scalar_t<OutputMdspec>;
287 return prepare_overwrite_transform(InplaceBinaryPlan<scalar_type>{}, output, input);
288}
289
290template <class OutputMdspec, class InputMdspec, class Factor>
291 requires SupportedAddScaledMdspecs<OutputMdspec, InputMdspec, Factor>
292[[nodiscard]] auto prepare_add_scaled(OutputMdspec& output, InputMdspec& input, Factor factor)
293{
294 using scalar_type = transform_scalar_t<OutputMdspec>;
295 using factor_type = std::remove_cvref_t<Factor>;
296 AddScaledPlan<scalar_type, factor_type> plan;
297 plan.factor = std::move(factor);
298 return prepare_overwrite_transform(std::move(plan), output, input);
299}
300
301template <class Function, class OutputMdspec, class LhsMdspec, class RhsMdspec>
302 requires SupportedStatelessBinaryMdspecs<OutputMdspec, LhsMdspec, RhsMdspec, Function>
303[[nodiscard]] auto prepare_stateless_binary(OutputMdspec& output, LhsMdspec& lhs, RhsMdspec& rhs)
304{
305 using scalar_type = transform_scalar_t<OutputMdspec>;
306 return prepare_overwrite_transform(BinaryArithmeticPlan<scalar_type>{}, output, lhs, rhs);
307}
308
309template <class Plan, class Launch> void execute_overwrite_transform(Plan const& plan, Launch&& launch)
310{
311 using scalar_type = typename Plan::scalar_type;
312 CHECK(kernel_attempt_succeeded(plan.attempt));
313 if (!plan.has_work) return;
314 CHECK(plan.output_buffer != nullptr);
315 for (auto const* input_buffer : plan.input_buffers)
316 CHECK(input_buffer != nullptr);
317
318 auto stream = plan.output_buffer->resources().streams().acquire();
319 int const device = plan.output_buffer->device().ordinal();
320 CHECK_EQUAL(stream.device(), device);
321 uni20::cuda::ScopedDevice guard(device);
322
323 auto output_access = plan.output_buffer->write_synchronized_with(stream);
324 auto* output = output_access.data() + plan.output_offset;
325 std::array<std::optional<uni20::cuda::ReadAccess<scalar_type>>, Plan::input_count> input_accesses;
326 std::array<scalar_type const*, Plan::input_count> input_data{};
327
328 for (std::size_t input = 0; input < Plan::input_count; ++input)
329 {
330 auto const* input_buffer = plan.input_buffers[input];
331 if (input_buffer == plan.output_buffer)
332 {
333 input_data[input] = output_access.data() + plan.input_offsets[input];
334 continue;
335 }
336
337 std::size_t prior = 0;
338 for (; prior < input; ++prior)
339 if (plan.input_buffers[prior] == input_buffer) break;
340
341 if (prior == input)
342 {
343 input_accesses[input].emplace(input_buffer->read_synchronized_with(stream));
344 input_data[input] = input_accesses[input]->data() + plan.input_offsets[input];
345 }
346 else
347 {
348 CHECK(input_accesses[prior].has_value());
349 input_data[input] = input_accesses[prior]->data() + plan.input_offsets[input];
350 }
351 }
352
353 std::forward<Launch>(launch)(output, input_data, stream.native_handle(), device);
354
355 uni20::cuda::AccessCompletion completion(stream);
356 completion.release(output_access);
357 for (auto& input_access : input_accesses)
358 if (input_access) completion.release(*input_access);
359}
360
361template <class Scalar, RegisteredStatelessUnary Function>
362void execute_stateless_unary(UnaryArithmeticPlan<Scalar> const& plan, Function function)
363{
364 execute_overwrite_transform(plan, [&](Scalar* output, auto const& inputs, cudaStream_t stream, int device) {
365 if constexpr (supports_elementwise_arithmetic<Scalar>)
366 {
367 plan.elementwise_plan.visit([&](auto const& elementwise_plan) {
368 enqueue_elementwise_unary(output, inputs[0], function, elementwise_plan, stream, device);
369 });
370 }
371 else
372 {
373 PANIC("unsupported scalar reached CUDA reference unary arithmetic");
374 }
375 });
376}
377
378template <class Scalar> void execute_fill(FillPlan<Scalar> const& plan, Scalar value)
379{
380 execute_overwrite_transform(plan, [&](Scalar* output, auto const&, cudaStream_t stream, int device) {
381 if constexpr (supports_elementwise_arithmetic<Scalar>)
382 {
383 plan.elementwise_plan.visit([&](auto const& elementwise_plan) {
384 enqueue_elementwise_fill(output, value, elementwise_plan, stream, device);
385 });
386 }
387 else
388 {
389 PANIC("unsupported scalar reached CUDA reference elementwise fill");
390 }
391 });
392}
393
394template <class Scalar, class Factor> void execute_scale(ScalePlan<Scalar, Factor> const& plan)
395{
396 execute_overwrite_transform(plan, [&](Scalar* output, auto const& inputs, cudaStream_t stream, int device) {
397 if constexpr (supports_elementwise_scale<Scalar, Factor>)
398 {
399 plan.elementwise_plan.visit([&](auto const& elementwise_plan) {
400 enqueue_elementwise_scale(output, inputs[0], plan.factor, elementwise_plan, stream, device);
401 });
402 }
403 else
404 {
405 PANIC("unsupported scalar reached CUDA reference elementwise scaling");
406 }
407 });
408}
409
410template <class Scalar, RegisteredStatelessBinary Function>
411void execute_stateless_binary(BinaryArithmeticPlan<Scalar> const& plan, Function function)
412{
413 execute_overwrite_transform(plan, [&](Scalar* output, auto const& inputs, cudaStream_t stream, int device) {
414 if constexpr (supports_elementwise_arithmetic<Scalar>)
415 {
416 plan.elementwise_plan.visit([&](auto const& elementwise_plan) {
417 enqueue_elementwise_binary(output, inputs[0], inputs[1], function, elementwise_plan, stream, device);
418 });
419 }
420 else
421 {
422 PANIC("unsupported scalar reached CUDA reference binary arithmetic");
423 }
424 });
425}
426
427template <class Scalar, class Factor> void execute_inplace_scale(InplaceScalePlan<Scalar, Factor> const& plan)
428{
429 execute_overwrite_transform(plan, [&](Scalar* output, auto const&, cudaStream_t stream, int device) {
430 if constexpr (supports_elementwise_scale<Scalar, Factor>)
431 {
432 plan.elementwise_plan.visit([&](auto const& elementwise_plan) {
433 enqueue_elementwise_inplace_scale(output, plan.factor, elementwise_plan, stream, device);
434 });
435 }
436 else
437 {
438 PANIC("unsupported scalar reached CUDA reference in-place scaling");
439 }
440 });
441}
442
443template <class Scalar, RegisteredStatelessBinary Function>
444void execute_inplace_stateless_binary(InplaceBinaryPlan<Scalar> const& plan, Function function)
445{
446 execute_overwrite_transform(plan, [&](Scalar* output, auto const& inputs, cudaStream_t stream, int device) {
447 if constexpr (supports_elementwise_arithmetic<Scalar>)
448 {
449 plan.elementwise_plan.visit([&](auto const& elementwise_plan) {
450 enqueue_elementwise_inplace_binary(output, inputs[0], function, elementwise_plan, stream, device);
451 });
452 }
453 else
454 {
455 PANIC("unsupported scalar reached CUDA reference in-place binary arithmetic");
456 }
457 });
458}
459
460template <class Scalar, class Factor> void execute_add_scaled(AddScaledPlan<Scalar, Factor> const& plan)
461{
462 execute_overwrite_transform(plan, [&](Scalar* output, auto const& inputs, cudaStream_t stream, int device) {
463 if constexpr (supports_elementwise_scale<Scalar, Factor>)
464 {
465 plan.elementwise_plan.visit([&](auto const& elementwise_plan) {
466 enqueue_elementwise_add_scaled(output, inputs[0], plan.factor, elementwise_plan, stream, device);
467 });
468 }
469 else
470 {
471 PANIC("unsupported scalar reached CUDA reference add-scaled update");
472 }
473 });
474}
475
476} // namespace detail::cuda_reference
477
479template <class Value, uni20::MutableMdspecLike OutputMdspec>
480consteval auto kernel_accepts_types(CudaReferenceBackend const&, transform_op<constant<Value>> const&, OutputMdspec&)
481{
482 if constexpr (detail::cuda_reference::SupportedFillMdspec<OutputMdspec, Value>)
483 return kernel_types_maybe;
484 else
485 return kernel_types_no;
486}
487
489template <class Value, class OutputMdspec>
490 requires detail::cuda_reference::SupportedFillMdspec<OutputMdspec, Value>
492{
493 auto preparation = detail::cuda_reference::prepare_fill<OutputMdspec, Value>(output);
494 if (!kernel_attempt_succeeded(preparation.attempt)) return preparation.attempt;
495 detail::cuda_reference::execute_fill(preparation, operation.function.value);
497}
498
500template <detail::cuda_reference::RegisteredStatelessUnary Function, uni20::MutableMdspecLike OutputMdspec,
501 uni20::MdspecLike InputMdspec>
502consteval auto kernel_accepts_types(CudaReferenceBackend const&, transform_op<Function> const&, OutputMdspec&,
503 InputMdspec&)
504{
505 if constexpr (detail::cuda_reference::SupportedStatelessUnaryMdspecs<OutputMdspec, InputMdspec, Function>)
506 return kernel_types_maybe;
507 else
508 return kernel_types_no;
509}
510
512template <detail::cuda_reference::RegisteredStatelessUnary Function, class OutputMdspec, class InputMdspec>
513 requires detail::cuda_reference::SupportedStatelessUnaryMdspecs<OutputMdspec, InputMdspec, Function>
515 InputMdspec& input)
516{
517 auto preparation = detail::cuda_reference::prepare_stateless_unary<Function>(output, input);
518 if (!kernel_attempt_succeeded(preparation.attempt)) return preparation.attempt;
519 detail::cuda_reference::execute_stateless_unary(preparation, operation.function);
521}
522
524template <class Factor, uni20::MutableMdspecLike OutputMdspec, uni20::MdspecLike InputMdspec>
525consteval auto kernel_accepts_types(CudaReferenceBackend const&, transform_op<scale<Factor>> const&, OutputMdspec&,
526 InputMdspec&)
527{
528 if constexpr (detail::cuda_reference::SupportedScaleMdspecs<OutputMdspec, InputMdspec, Factor>)
529 return kernel_types_maybe;
530 else
531 return kernel_types_no;
532}
533
535template <class Factor, class OutputMdspec, class InputMdspec>
536 requires detail::cuda_reference::SupportedScaleMdspecs<OutputMdspec, InputMdspec, Factor>
538 InputMdspec& input)
539{
540 auto preparation = detail::cuda_reference::prepare_scale(output, input, operation.function.factor);
541 if (!kernel_attempt_succeeded(preparation.attempt)) return preparation.attempt;
542 detail::cuda_reference::execute_scale(preparation);
544}
545
547template <detail::cuda_reference::RegisteredStatelessBinary Function, uni20::MutableMdspecLike OutputMdspec,
548 uni20::MdspecLike LhsMdspec, uni20::MdspecLike RhsMdspec>
549consteval auto kernel_accepts_types(CudaReferenceBackend const&, transform_op<Function> const&, OutputMdspec&,
550 LhsMdspec&, RhsMdspec&)
551{
552 if constexpr (detail::cuda_reference::SupportedStatelessBinaryMdspecs<OutputMdspec, LhsMdspec, RhsMdspec, Function>)
553 return kernel_types_maybe;
554 else
555 return kernel_types_no;
556}
557
559template <detail::cuda_reference::RegisteredStatelessBinary Function, class OutputMdspec, class LhsMdspec,
560 class RhsMdspec>
561 requires detail::cuda_reference::SupportedStatelessBinaryMdspecs<OutputMdspec, LhsMdspec, RhsMdspec, Function>
563 LhsMdspec& lhs, RhsMdspec& rhs)
564{
565 auto preparation = detail::cuda_reference::prepare_stateless_binary<Function>(output, lhs, rhs);
566 if (!kernel_attempt_succeeded(preparation.attempt)) return preparation.attempt;
567 detail::cuda_reference::execute_stateless_binary(preparation, operation.function);
569}
570
572template <class Factor, uni20::MutableMdspecLike OutputMdspec>
574 OutputMdspec&)
575{
576 if constexpr (detail::cuda_reference::SupportedInplaceScaleMdspec<OutputMdspec, Factor>)
577 return kernel_types_maybe;
578 else
579 return kernel_types_no;
580}
581
583template <class Factor, class OutputMdspec>
584 requires detail::cuda_reference::SupportedInplaceScaleMdspec<OutputMdspec, Factor>
586 OutputMdspec& output)
587{
588 auto preparation = detail::cuda_reference::prepare_inplace_scale(output, operation.function.factor);
589 if (!kernel_attempt_succeeded(preparation.attempt)) return preparation.attempt;
590 detail::cuda_reference::execute_inplace_scale(preparation);
592}
593
595template <detail::cuda_reference::RegisteredStatelessBinary Function, uni20::MutableMdspecLike OutputMdspec,
596 uni20::MdspecLike InputMdspec>
598 InputMdspec&)
599{
600 if constexpr (detail::cuda_reference::SupportedInplaceStatelessBinaryMdspecs<OutputMdspec, InputMdspec, Function>)
601 return kernel_types_maybe;
602 else
603 return kernel_types_no;
604}
605
607template <detail::cuda_reference::RegisteredStatelessBinary Function, class OutputMdspec, class InputMdspec>
608 requires detail::cuda_reference::SupportedInplaceStatelessBinaryMdspecs<OutputMdspec, InputMdspec, Function>
610 InputMdspec& input)
611{
612 auto preparation = detail::cuda_reference::prepare_inplace_stateless_binary<Function>(output, input);
613 if (!kernel_attempt_succeeded(preparation.attempt)) return preparation.attempt;
614 detail::cuda_reference::execute_inplace_stateless_binary(preparation, operation.function);
616}
617
619template <class Factor, uni20::MutableMdspecLike OutputMdspec, uni20::MdspecLike InputMdspec>
621 OutputMdspec&, InputMdspec&)
622{
623 if constexpr (detail::cuda_reference::SupportedAddScaledMdspecs<OutputMdspec, InputMdspec, Factor>)
624 return kernel_types_maybe;
625 else
626 return kernel_types_no;
627}
628
630template <class Factor, class OutputMdspec, class InputMdspec>
631 requires detail::cuda_reference::SupportedAddScaledMdspecs<OutputMdspec, InputMdspec, Factor>
633 OutputMdspec& output, InputMdspec& input)
634{
635 auto preparation = detail::cuda_reference::prepare_add_scaled(output, input, operation.function.factor);
636 if (!kernel_attempt_succeeded(preparation.attempt)) return preparation.attempt;
637 detail::cuda_reference::execute_add_scaled(preparation);
639}
640
641} // namespace uni20::linalg
Typed CUDA device buffers and scoped stream access guards.
One operation-tail completion shared by multiple CUDA buffer accesses.
Definition buffer.hpp:1160
Move-only logical CUDA buffer and completion state.
Definition buffer.hpp:222
Temporarily select one CUDA device and restore the previous device on destruction.
Definition runtime.hpp:44
Mdspan metadata whose data handle is immediate or available through a data descriptor.
Definition mdspan/concepts.hpp:304
MdspecLike types whose eventual storage supports writes.
Definition mdspan/concepts.hpp:398
An mdspec-like type whose mapping exposes multidimensional strides.
Definition mdspan/concepts.hpp:427
CUDA Tensor storage with deferred device-memory data descriptors.
locale_t value
Definition data_table.cpp:49
Operation-tag backend dispatch over tensor-view operands.
Compiled CUDA entry points for named stateless arithmetic transforms.
Named backend-neutral function objects for elementwise transforms.
Compiled CUDA entry points for elementwise scaling.
auto make_multi_iteration_plan(std::tuple< Mappings... > const &mappings)
Build a merged iteration plan for multiple tensors sharing the same extents.
Definition iteration_plan.hpp:263
Helpers for constructing merged iteration plans over strided tensors.
Mdspan concept and accessor extensions for Uni20.
Shared CUDA reference-backend traits for raw buffer mdspecs.
std::pair< int, int > size(std::FILE *stream)
Return the terminal size associated with a C stream.
Definition terminal.cpp:504
ReadBuffer< T > && release(ReadBuffer< T > &in)
Adaptor for transferring ReadBuffer ownership into an await expression.
Definition buffers.hpp:227
stream
Selects the ordinary display stream for a message.
Definition display.hpp:25
Definition linalg/async.hpp:28
constexpr bool kernel_attempt_succeeded(KernelAttempt attempt) noexcept
Return whether a backend performed the operation.
Definition kernel_attempt.hpp:40
KernelAttempt try_kernel(BlasBackend, gemm_op const &, C &c, S const &alpha, A &a, B &b, S const &beta)
Pack logical host values and execute GEMM through value-preserving provider conversion.
Definition linalg/backends/blas/binary80.hpp:30
KernelAttempt
Result of one runtime kernel backend attempt.
Definition kernel_attempt.hpp:27
constexpr KernelAcceptance< KernelTypeAcceptance::no > kernel_types_no
Report that a backend rejects the argument types.
Definition dispatch_error.hpp:34
consteval auto kernel_accepts_types(BlasBackend, gemm_op const &, C &, S const &, A &, B &, S const &)
Accept host-readable native binary80 operands, including transforming accessors.
Definition linalg/backends/blas/binary80.hpp:19
constexpr KernelAcceptance< KernelTypeAcceptance::maybe > kernel_types_maybe
Report that runtime operand values determine backend acceptance.
Definition dispatch_error.hpp:37
Backend-independent operation values used with normalized kernel operands.
Backend value for generic CUDA runtime kernels and transfers.
Definition backend_selector.hpp:48
Add one retained scalar multiple to an existing element value.
Definition elementwise_functions.hpp:86
Nullary elementwise function that returns one retained value.
Definition elementwise_functions.hpp:20
Multiply one element value by a retained scalar factor.
Definition elementwise_functions.hpp:68
Callable-carrying elementwise update operation.
Definition operation_tags.hpp:61
Callable-carrying elementwise overwrite operation.
Definition operation_tags.hpp:48
#define CHECK(cond,...)
Definition trace.hpp:299
#define PANIC(...)
Definition trace.hpp:382
#define CHECK_EQUAL(a, b,...)
Definition trace.hpp:309