cumo 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop_todo.yml +590 -152
- data/CHANGELOG.md +29 -0
- data/README.md +140 -0
- data/bench/cumo_probe.rb +1 -1
- data/cumo.gemspec +1 -1
- data/ext/cumo/cuda/cudnn.c +18 -0
- data/ext/cumo/cuda/cudnn_impl.cpp +60 -14
- data/ext/cumo/cuda/driver.c +11 -9
- data/ext/cumo/cuda/runtime.c +56 -0
- data/ext/cumo/cumo.c +4 -0
- data/ext/cumo/extconf.rb +2 -0
- data/ext/cumo/include/cumo/bit_reduce_kernel.h +6 -6
- data/ext/cumo/include/cumo/cuda/cudnn.h +40 -0
- data/ext/cumo/include/cumo/cuda/runtime.h +2 -0
- data/ext/cumo/include/cumo/intern.h +9 -0
- data/ext/cumo/include/cumo/narray.h +1 -0
- data/ext/cumo/include/cumo/reduce_kernel.h +32 -28
- data/ext/cumo/include/cumo/row_kernel.h +170 -0
- data/ext/cumo/include/cumo/row_method.h +48 -0
- data/ext/cumo/include/cumo/template_kernel.h +26 -0
- data/ext/cumo/include/cumo/types/bf16_def.h +82 -0
- data/ext/cumo/include/cumo/types/bf16_def_kernel.h +28 -0
- data/ext/cumo/include/cumo/types/bf16_macro.h +186 -0
- data/ext/cumo/include/cumo/types/bf16_macro_kernel.h +213 -0
- data/ext/cumo/include/cumo/types/bfloat.h +22 -0
- data/ext/cumo/include/cumo/types/bfloat_kernel.h +16 -0
- data/ext/cumo/include/cumo/types/bit.h +2 -0
- data/ext/cumo/include/cumo/types/bit_kernel.h +2 -0
- data/ext/cumo/include/cumo/types/complex_macro.h +2 -0
- data/ext/cumo/include/cumo/types/complex_macro_kernel.h +2 -0
- data/ext/cumo/include/cumo/types/float_def.h +8 -0
- data/ext/cumo/include/cumo/types/float_def_kernel.h +8 -0
- data/ext/cumo/include/cumo/types/float_macro.h +19 -17
- data/ext/cumo/include/cumo/types/float_macro_kernel.h +19 -19
- data/ext/cumo/include/cumo/types/half_macro.h +15 -17
- data/ext/cumo/include/cumo/types/half_macro_kernel.h +15 -0
- data/ext/cumo/include/cumo/types/robj_macro.h +2 -0
- data/ext/cumo/include/cumo/types/xint_macro.h +2 -6
- data/ext/cumo/include/cumo/types/xint_macro_kernel.h +2 -0
- data/ext/cumo/include/cumo.h +2 -2
- data/ext/cumo/narray/gen/def/bfloat.rb +56 -0
- data/ext/cumo/narray/gen/def/bit.rb +11 -1
- data/ext/cumo/narray/gen/def/dcomplex.rb +11 -1
- data/ext/cumo/narray/gen/def/dfloat.rb +10 -1
- data/ext/cumo/narray/gen/def/hfloat.rb +10 -1
- data/ext/cumo/narray/gen/def/int16.rb +11 -1
- data/ext/cumo/narray/gen/def/int32.rb +11 -1
- data/ext/cumo/narray/gen/def/int64.rb +11 -1
- data/ext/cumo/narray/gen/def/int8.rb +11 -1
- data/ext/cumo/narray/gen/def/robject.rb +11 -1
- data/ext/cumo/narray/gen/def/scomplex.rb +11 -1
- data/ext/cumo/narray/gen/def/sfloat.rb +10 -1
- data/ext/cumo/narray/gen/def/uint16.rb +11 -1
- data/ext/cumo/narray/gen/def/uint32.rb +11 -1
- data/ext/cumo/narray/gen/def/uint64.rb +11 -1
- data/ext/cumo/narray/gen/def/uint8.rb +11 -1
- data/ext/cumo/narray/gen/narray_def.rb +0 -5
- data/ext/cumo/narray/gen/spec.rb +11 -14
- data/ext/cumo/narray/gen/tmpl/accum_arg.c +1 -1
- data/ext/cumo/narray/gen/tmpl/accum_binary_kernel.cu +9 -9
- data/ext/cumo/narray/gen/tmpl/accum_index.c +1 -1
- data/ext/cumo/narray/gen/tmpl/batch_norm.c +3 -3
- data/ext/cumo/narray/gen/tmpl/batch_norm_backward.c +3 -3
- data/ext/cumo/narray/gen/tmpl/complex_accum_kernel.cu +15 -5
- data/ext/cumo/narray/gen/tmpl/conv.c +88 -121
- data/ext/cumo/narray/gen/tmpl/conv_grad_w.c +89 -81
- data/ext/cumo/narray/gen/tmpl/conv_transpose.c +88 -121
- data/ext/cumo/narray/gen/tmpl/cum.c +8 -7
- data/ext/cumo/narray/gen/tmpl/cum_kernel.cu +7 -6
- data/ext/cumo/narray/gen/tmpl/fixed_batch_norm.c +3 -3
- data/ext/cumo/narray/gen/tmpl/float_accum_kernel.cu +22 -10
- data/ext/cumo/narray/gen/tmpl/gemm.c +11 -35
- data/ext/cumo/narray/gen/tmpl/layer_norm.c +91 -0
- data/ext/cumo/narray/gen/tmpl/layer_norm_kernel.cu +54 -0
- data/ext/cumo/narray/gen/tmpl/lib.c +1 -0
- data/ext/cumo/narray/gen/tmpl/lib_kernel.cu +1 -0
- data/ext/cumo/narray/gen/tmpl/median.c +1 -1
- data/ext/cumo/narray/gen/tmpl/poly.c +0 -3
- data/ext/cumo/narray/gen/tmpl/poly_kernel.cu +1 -1
- data/ext/cumo/narray/gen/tmpl/pooling_backward.c +2 -1
- data/ext/cumo/narray/gen/tmpl/pooling_forward.c +2 -1
- data/ext/cumo/narray/gen/tmpl/rand.c +9 -9
- data/ext/cumo/narray/gen/tmpl/rand_kernel.cu +9 -9
- data/ext/cumo/narray/gen/tmpl/rand_norm.c +4 -4
- data/ext/cumo/narray/gen/tmpl/rand_norm_kernel.cu +7 -7
- data/ext/cumo/narray/gen/tmpl/real_accum_kernel.cu +6 -6
- data/ext/cumo/narray/gen/tmpl/softmax.c +56 -0
- data/ext/cumo/narray/gen/tmpl/softmax_kernel.cu +157 -0
- data/ext/cumo/narray/gen/tmpl/sort.c +9 -32
- data/ext/cumo/narray/gen/tmpl/sort_index.c +16 -95
- data/ext/cumo/narray/gen/tmpl/store_array.c +12 -9
- data/ext/cumo/narray/gen/tmpl_bit/mask.c +11 -11
- data/ext/cumo/narray/gen/tmpl_bit/store_array.c +5 -9
- data/ext/cumo/narray/gen/tmpl_bit/where.c +50 -11
- data/ext/cumo/narray/gen/tmpl_bit/where2.c +11 -12
- data/ext/cumo/narray/gen/tmpl_bit/where_kernel.cu +1 -2
- data/ext/cumo/narray/index.c +2 -1
- data/ext/cumo/narray/math.c +2 -0
- data/ext/cumo/narray/narray.c +27 -1
- data/ext/cumo/narray/ndloop.c +73 -4
- data/ext/cumo/narray/sort_kernel.cu +61 -41
- data/ext/cumo/narray/struct.c +3 -0
- data/lib/cumo/cuda/cudnn.rb +1 -1
- data/lib/cumo/narray/extra.rb +2 -2
- data/test/bfloat_test.rb +238 -0
- data/test/cuda/memory_pool_test.rb +8 -7
- data/test/cuda/runtime_test.rb +25 -0
- data/test/cudnn_test.rb +108 -24
- data/test/extra_test.rb +1 -1
- data/test/fused_test.rb +311 -0
- data/test/math_test.rb +77 -2
- data/test/narray_alt_coverage_test.rb +1 -0
- data/test/narray_test.rb +70 -10
- data/test/test_helper.rb +1 -0
- metadata +20 -4
- data/ext/cumo/narray/gen/tmpl/qsort.c +0 -205
|
@@ -602,7 +602,7 @@ struct reduce_combine {
|
|
|
602
602
|
// the partials so the caller can combine them with its own second pass. The
|
|
603
603
|
// caller frees the returned buffer.
|
|
604
604
|
template <typename TypeIn, typename TypeReduce, typename ReductionImpl, bool ARG = false>
|
|
605
|
-
TypeReduce* reduce_partial_pass(cumo_na_reduction_arg_t arg, cumo_reduce_addr_t ad, int64_t n_split, int64_t reduce_total_size, cumo_na_reduction_arg_t* arg2, ReductionImpl& impl) {
|
|
605
|
+
TypeReduce* reduce_partial_pass(cumo_na_reduction_arg_t arg, cumo_reduce_addr_t ad, int64_t n_split, int64_t reduce_total_size, cumo_na_reduction_arg_t* arg2, ReductionImpl& impl, char* held = 0) {
|
|
606
606
|
int64_t chunk = (reduce_total_size + n_split - 1) / n_split;
|
|
607
607
|
int64_t partial_total_size = arg.out_indexer.total_size * n_split;
|
|
608
608
|
TypeReduce* partial = reinterpret_cast<TypeReduce*>(cumo_cuda_runtime_malloc(sizeof(TypeReduce) * partial_total_size));
|
|
@@ -618,7 +618,7 @@ TypeReduce* reduce_partial_pass(cumo_na_reduction_arg_t arg, cumo_reduce_addr_t
|
|
|
618
618
|
} else {
|
|
619
619
|
reduction_partial_kernel<false,ARG,TypeIn,TypeReduce,ReductionImpl><<<grid_size, max_block_size, shared_mem_size>>>(arg, ad, partial, n_split, chunk, out_block_size, reduce_block_size, impl);
|
|
620
620
|
}
|
|
621
|
-
|
|
621
|
+
cumo_check_launch_holding(partial, held);
|
|
622
622
|
|
|
623
623
|
arg2->in.ptr = reinterpret_cast<char*>(partial);
|
|
624
624
|
arg2->in.step[0] = sizeof(TypeReduce);
|
|
@@ -636,7 +636,7 @@ static inline bool zip_axes_are_flat(const cumo_reduce_addr_t& ad, const cumo_re
|
|
|
636
636
|
|
|
637
637
|
// First pass of a split zip reduction. See reduce_partial_pass above.
|
|
638
638
|
template <typename TypeIn, typename TypeReduce, typename ReductionImpl>
|
|
639
|
-
TypeReduce* reduce_zip_partial_pass(cumo_na_reduction_arg_t arg, cumo_na_iarray_t in2, cumo_reduce_addr_t ad, cumo_reduce_addr_t ad2, int64_t n_split, int64_t reduce_total_size, cumo_na_reduction_arg_t* arg2, ReductionImpl& impl) {
|
|
639
|
+
TypeReduce* reduce_zip_partial_pass(cumo_na_reduction_arg_t arg, cumo_na_iarray_t in2, cumo_reduce_addr_t ad, cumo_reduce_addr_t ad2, int64_t n_split, int64_t reduce_total_size, cumo_na_reduction_arg_t* arg2, ReductionImpl& impl, char* held = 0) {
|
|
640
640
|
int64_t chunk = (reduce_total_size + n_split - 1) / n_split;
|
|
641
641
|
int64_t partial_total_size = arg.out_indexer.total_size * n_split;
|
|
642
642
|
TypeReduce* partial = reinterpret_cast<TypeReduce*>(cumo_cuda_runtime_malloc(sizeof(TypeReduce) * partial_total_size));
|
|
@@ -652,7 +652,7 @@ TypeReduce* reduce_zip_partial_pass(cumo_na_reduction_arg_t arg, cumo_na_iarray_
|
|
|
652
652
|
} else {
|
|
653
653
|
reduction_zip_partial_kernel<false,TypeIn,TypeReduce,ReductionImpl><<<grid_size, max_block_size, shared_mem_size>>>(arg, in2, ad, ad2, partial, n_split, chunk, out_block_size, reduce_block_size, impl);
|
|
654
654
|
}
|
|
655
|
-
|
|
655
|
+
cumo_check_launch_holding(partial, held);
|
|
656
656
|
|
|
657
657
|
arg2->in.ptr = reinterpret_cast<char*>(partial);
|
|
658
658
|
arg2->in.step[0] = sizeof(TypeReduce);
|
|
@@ -664,8 +664,12 @@ TypeReduce* reduce_zip_partial_pass(cumo_na_reduction_arg_t arg, cumo_na_iarray_
|
|
|
664
664
|
|
|
665
665
|
} // cumo_detail
|
|
666
666
|
|
|
667
|
+
// held0 and held1 are every scratch buffer still outstanding, the caller's
|
|
668
|
+
// included. The checks free them before they raise, since rb_raise is a longjmp
|
|
669
|
+
// and nothing written after it runs. The split forms pass their own partial as
|
|
670
|
+
// well as whatever they were handed.
|
|
667
671
|
template <typename TypeIn, typename TypeOut, typename ReductionImpl>
|
|
668
|
-
void cumo_reduce(cumo_na_reduction_arg_t arg, ReductionImpl&& impl) {
|
|
672
|
+
void cumo_reduce(cumo_na_reduction_arg_t arg, ReductionImpl&& impl, char* held0 = 0, char* held1 = 0) {
|
|
669
673
|
if (arg.out_indexer.total_size == 0) {
|
|
670
674
|
return;
|
|
671
675
|
}
|
|
@@ -686,7 +690,7 @@ void cumo_reduce(cumo_na_reduction_arg_t arg, ReductionImpl&& impl) {
|
|
|
686
690
|
} else {
|
|
687
691
|
cumo_detail::reduction_kernel<false,TypeIn,TypeOut,ReductionImpl><<<grid_size, block_size, shared_mem_size>>>(arg, ad, out_block_size, reduce_block_size, impl);
|
|
688
692
|
}
|
|
689
|
-
|
|
693
|
+
cumo_check_launch_holding(held0, held1);
|
|
690
694
|
}
|
|
691
695
|
|
|
692
696
|
// Runs the reduce axis of one output across several blocks and combines their
|
|
@@ -695,7 +699,7 @@ void cumo_reduce(cumo_na_reduction_arg_t arg, ReductionImpl&& impl) {
|
|
|
695
699
|
// case. Only for an impl whose Identity ignores its index argument — see
|
|
696
700
|
// reduce_combine above.
|
|
697
701
|
template <typename TypeIn, typename TypeOut, typename ReductionImpl>
|
|
698
|
-
void cumo_reduce_split(cumo_na_reduction_arg_t arg, ReductionImpl&& impl) {
|
|
702
|
+
void cumo_reduce_split(cumo_na_reduction_arg_t arg, ReductionImpl&& impl, char* held = 0) {
|
|
699
703
|
using TypeReduce = decltype(impl.Identity(0));
|
|
700
704
|
|
|
701
705
|
if (arg.out_indexer.total_size == 0) {
|
|
@@ -711,20 +715,20 @@ void cumo_reduce_split(cumo_na_reduction_arg_t arg, ReductionImpl&& impl) {
|
|
|
711
715
|
|
|
712
716
|
int64_t n_split = cumo_detail::reduce_split_count(reduce_total_size, out_block_num);
|
|
713
717
|
if (n_split < 2) {
|
|
714
|
-
cumo_reduce<TypeIn, TypeOut, ReductionImpl>(arg, std::forward<ReductionImpl>(impl));
|
|
718
|
+
cumo_reduce<TypeIn, TypeOut, ReductionImpl>(arg, std::forward<ReductionImpl>(impl), held);
|
|
715
719
|
return;
|
|
716
720
|
}
|
|
717
721
|
|
|
718
722
|
cumo_na_reduction_arg_t arg2 = arg;
|
|
719
|
-
TypeReduce* partial = cumo_detail::reduce_partial_pass<TypeIn, TypeReduce, ReductionImpl>(arg, ad, n_split, reduce_total_size, &arg2, impl);
|
|
720
|
-
cumo_reduce<TypeReduce, TypeOut, cumo_detail::reduce_combine<ReductionImpl>>(arg2, cumo_detail::reduce_combine<ReductionImpl>{impl});
|
|
723
|
+
TypeReduce* partial = cumo_detail::reduce_partial_pass<TypeIn, TypeReduce, ReductionImpl>(arg, ad, n_split, reduce_total_size, &arg2, impl, held);
|
|
724
|
+
cumo_reduce<TypeReduce, TypeOut, cumo_detail::reduce_combine<ReductionImpl>>(arg2, cumo_detail::reduce_combine<ReductionImpl>{impl}, reinterpret_cast<char*>(partial), held);
|
|
721
725
|
cumo_cuda_runtime_free(reinterpret_cast<char*>(partial));
|
|
722
726
|
}
|
|
723
727
|
|
|
724
728
|
// Variant of cumo_reduce reading two inputs, for mulsum. in2 describes the same
|
|
725
729
|
// shape as arg.in, since the one in_indexer addresses both.
|
|
726
730
|
template <typename TypeIn, typename TypeOut, typename ReductionImpl>
|
|
727
|
-
void cumo_reduce_zip(cumo_na_reduction_arg_t arg, cumo_na_iarray_t in2, ReductionImpl&& impl) {
|
|
731
|
+
void cumo_reduce_zip(cumo_na_reduction_arg_t arg, cumo_na_iarray_t in2, ReductionImpl&& impl, char* held0 = 0, char* held1 = 0) {
|
|
728
732
|
if (arg.out_indexer.total_size == 0) {
|
|
729
733
|
return;
|
|
730
734
|
}
|
|
@@ -748,13 +752,13 @@ void cumo_reduce_zip(cumo_na_reduction_arg_t arg, cumo_na_iarray_t in2, Reductio
|
|
|
748
752
|
} else {
|
|
749
753
|
cumo_detail::reduction_zip_kernel<false,TypeIn,TypeOut,ReductionImpl><<<grid_size, block_size, shared_mem_size>>>(arg, in2, ad, ad2, out_block_size, reduce_block_size, impl);
|
|
750
754
|
}
|
|
751
|
-
|
|
755
|
+
cumo_check_launch_holding(held0, held1);
|
|
752
756
|
}
|
|
753
757
|
|
|
754
758
|
// cumo_reduce_split for a zip reduction. The first pass reads both operands and
|
|
755
759
|
// the combine pass has only accumulators left, so it is the plain one.
|
|
756
760
|
template <typename TypeIn, typename TypeOut, typename ReductionImpl>
|
|
757
|
-
void cumo_reduce_zip_split(cumo_na_reduction_arg_t arg, cumo_na_iarray_t in2, ReductionImpl&& impl) {
|
|
761
|
+
void cumo_reduce_zip_split(cumo_na_reduction_arg_t arg, cumo_na_iarray_t in2, ReductionImpl&& impl, char* held = 0) {
|
|
758
762
|
using TypeReduce = decltype(impl.Identity(0));
|
|
759
763
|
|
|
760
764
|
if (arg.out_indexer.total_size == 0) {
|
|
@@ -773,13 +777,13 @@ void cumo_reduce_zip_split(cumo_na_reduction_arg_t arg, cumo_na_iarray_t in2, Re
|
|
|
773
777
|
|
|
774
778
|
int64_t n_split = cumo_detail::reduce_split_count(reduce_total_size, out_block_num);
|
|
775
779
|
if (n_split < 2) {
|
|
776
|
-
cumo_reduce_zip<TypeIn, TypeOut, ReductionImpl>(arg, in2, std::forward<ReductionImpl>(impl));
|
|
780
|
+
cumo_reduce_zip<TypeIn, TypeOut, ReductionImpl>(arg, in2, std::forward<ReductionImpl>(impl), held);
|
|
777
781
|
return;
|
|
778
782
|
}
|
|
779
783
|
|
|
780
784
|
cumo_na_reduction_arg_t combine = arg;
|
|
781
|
-
TypeReduce* partial = cumo_detail::reduce_zip_partial_pass<TypeIn, TypeReduce, ReductionImpl>(arg, in2, ad, ad2, n_split, reduce_total_size, &combine, impl);
|
|
782
|
-
cumo_reduce<TypeReduce, TypeOut, cumo_detail::reduce_combine<ReductionImpl>>(combine, cumo_detail::reduce_combine<ReductionImpl>{impl});
|
|
785
|
+
TypeReduce* partial = cumo_detail::reduce_zip_partial_pass<TypeIn, TypeReduce, ReductionImpl>(arg, in2, ad, ad2, n_split, reduce_total_size, &combine, impl, held);
|
|
786
|
+
cumo_reduce<TypeReduce, TypeOut, cumo_detail::reduce_combine<ReductionImpl>>(combine, cumo_detail::reduce_combine<ReductionImpl>{impl}, reinterpret_cast<char*>(partial), held);
|
|
783
787
|
cumo_cuda_runtime_free(reinterpret_cast<char*>(partial));
|
|
784
788
|
}
|
|
785
789
|
|
|
@@ -787,7 +791,7 @@ void cumo_reduce_zip_split(cumo_na_reduction_arg_t arg, cumo_na_iarray_t in2, Re
|
|
|
787
791
|
// See reduction_pair_kernel above. out2 has to describe the same shape as
|
|
788
792
|
// arg.out, since the one out_indexer addresses both.
|
|
789
793
|
template <typename TypeIn, typename TypeOut, typename ReductionImpl>
|
|
790
|
-
void cumo_reduce_pair(cumo_na_reduction_arg_t arg, cumo_na_iarray_t out2, ReductionImpl&& impl) {
|
|
794
|
+
void cumo_reduce_pair(cumo_na_reduction_arg_t arg, cumo_na_iarray_t out2, ReductionImpl&& impl, char* held0 = 0, char* held1 = 0) {
|
|
791
795
|
if (arg.out_indexer.total_size == 0) {
|
|
792
796
|
return;
|
|
793
797
|
}
|
|
@@ -809,12 +813,12 @@ void cumo_reduce_pair(cumo_na_reduction_arg_t arg, cumo_na_iarray_t out2, Reduct
|
|
|
809
813
|
} else {
|
|
810
814
|
cumo_detail::reduction_pair_kernel<false,TypeIn,TypeOut,ReductionImpl><<<grid_size, block_size, shared_mem_size>>>(arg, out2, ad, out_block_size, reduce_block_size, impl);
|
|
811
815
|
}
|
|
812
|
-
|
|
816
|
+
cumo_check_launch_holding(held0, held1);
|
|
813
817
|
}
|
|
814
818
|
|
|
815
819
|
// cumo_reduce_split for the two-output form. Same constraint on Identity.
|
|
816
820
|
template <typename TypeIn, typename TypeOut, typename ReductionImpl>
|
|
817
|
-
void cumo_reduce_pair_split(cumo_na_reduction_arg_t arg, cumo_na_iarray_t out2, ReductionImpl&& impl) {
|
|
821
|
+
void cumo_reduce_pair_split(cumo_na_reduction_arg_t arg, cumo_na_iarray_t out2, ReductionImpl&& impl, char* held = 0) {
|
|
818
822
|
using TypeReduce = decltype(impl.Identity(0));
|
|
819
823
|
|
|
820
824
|
if (arg.out_indexer.total_size == 0) {
|
|
@@ -830,20 +834,20 @@ void cumo_reduce_pair_split(cumo_na_reduction_arg_t arg, cumo_na_iarray_t out2,
|
|
|
830
834
|
|
|
831
835
|
int64_t n_split = cumo_detail::reduce_split_count(reduce_total_size, out_block_num);
|
|
832
836
|
if (n_split < 2) {
|
|
833
|
-
cumo_reduce_pair<TypeIn, TypeOut, ReductionImpl>(arg, out2, std::forward<ReductionImpl>(impl));
|
|
837
|
+
cumo_reduce_pair<TypeIn, TypeOut, ReductionImpl>(arg, out2, std::forward<ReductionImpl>(impl), held);
|
|
834
838
|
return;
|
|
835
839
|
}
|
|
836
840
|
|
|
837
841
|
cumo_na_reduction_arg_t arg2 = arg;
|
|
838
|
-
TypeReduce* partial = cumo_detail::reduce_partial_pass<TypeIn, TypeReduce, ReductionImpl>(arg, ad, n_split, reduce_total_size, &arg2, impl);
|
|
839
|
-
cumo_reduce_pair<TypeReduce, TypeOut, cumo_detail::reduce_combine<ReductionImpl>>(arg2, out2, cumo_detail::reduce_combine<ReductionImpl>{impl});
|
|
842
|
+
TypeReduce* partial = cumo_detail::reduce_partial_pass<TypeIn, TypeReduce, ReductionImpl>(arg, ad, n_split, reduce_total_size, &arg2, impl, held);
|
|
843
|
+
cumo_reduce_pair<TypeReduce, TypeOut, cumo_detail::reduce_combine<ReductionImpl>>(arg2, out2, cumo_detail::reduce_combine<ReductionImpl>{impl}, reinterpret_cast<char*>(partial), held);
|
|
840
844
|
cumo_cuda_runtime_free(reinterpret_cast<char*>(partial));
|
|
841
845
|
}
|
|
842
846
|
|
|
843
847
|
// Variant of cumo_reduce for arg-reductions (argmax/argmin), which returns
|
|
844
848
|
// indices along the reduction axis. See reduction_arg_kernel above.
|
|
845
849
|
template <typename TypeIn, typename TypeOut, typename ReductionImpl>
|
|
846
|
-
void cumo_reduce_arg(cumo_na_reduction_arg_t arg, ReductionImpl&& impl) {
|
|
850
|
+
void cumo_reduce_arg(cumo_na_reduction_arg_t arg, ReductionImpl&& impl, char* held0 = 0, char* held1 = 0) {
|
|
847
851
|
if (arg.out_indexer.total_size == 0) {
|
|
848
852
|
return;
|
|
849
853
|
}
|
|
@@ -864,14 +868,14 @@ void cumo_reduce_arg(cumo_na_reduction_arg_t arg, ReductionImpl&& impl) {
|
|
|
864
868
|
} else {
|
|
865
869
|
cumo_detail::reduction_arg_kernel<false,TypeIn,TypeOut,ReductionImpl><<<grid_size, block_size, shared_mem_size>>>(arg, ad, out_block_size, reduce_block_size, impl);
|
|
866
870
|
}
|
|
867
|
-
|
|
871
|
+
cumo_check_launch_holding(held0, held1);
|
|
868
872
|
}
|
|
869
873
|
|
|
870
874
|
// cumo_reduce_split for the arg form. The partials already carry indices along
|
|
871
875
|
// the reduce axis, so the second pass combines them with the plain kernel.
|
|
872
876
|
// Only for an impl whose Identity ignores its index argument.
|
|
873
877
|
template <typename TypeIn, typename TypeOut, typename ReductionImpl>
|
|
874
|
-
void cumo_reduce_arg_split(cumo_na_reduction_arg_t arg, ReductionImpl&& impl) {
|
|
878
|
+
void cumo_reduce_arg_split(cumo_na_reduction_arg_t arg, ReductionImpl&& impl, char* held = 0) {
|
|
875
879
|
using TypeReduce = decltype(impl.Identity(0));
|
|
876
880
|
|
|
877
881
|
if (arg.out_indexer.total_size == 0) {
|
|
@@ -887,13 +891,13 @@ void cumo_reduce_arg_split(cumo_na_reduction_arg_t arg, ReductionImpl&& impl) {
|
|
|
887
891
|
|
|
888
892
|
int64_t n_split = cumo_detail::reduce_split_count(reduce_total_size, out_block_num);
|
|
889
893
|
if (n_split < 2) {
|
|
890
|
-
cumo_reduce_arg<TypeIn, TypeOut, ReductionImpl>(arg, std::forward<ReductionImpl>(impl));
|
|
894
|
+
cumo_reduce_arg<TypeIn, TypeOut, ReductionImpl>(arg, std::forward<ReductionImpl>(impl), held);
|
|
891
895
|
return;
|
|
892
896
|
}
|
|
893
897
|
|
|
894
898
|
cumo_na_reduction_arg_t arg2 = arg;
|
|
895
|
-
TypeReduce* partial = cumo_detail::reduce_partial_pass<TypeIn, TypeReduce, ReductionImpl, true>(arg, ad, n_split, reduce_total_size, &arg2, impl);
|
|
896
|
-
cumo_reduce<TypeReduce, TypeOut, cumo_detail::reduce_combine<ReductionImpl>>(arg2, cumo_detail::reduce_combine<ReductionImpl>{impl});
|
|
899
|
+
TypeReduce* partial = cumo_detail::reduce_partial_pass<TypeIn, TypeReduce, ReductionImpl, true>(arg, ad, n_split, reduce_total_size, &arg2, impl, held);
|
|
900
|
+
cumo_reduce<TypeReduce, TypeOut, cumo_detail::reduce_combine<ReductionImpl>>(arg2, cumo_detail::reduce_combine<ReductionImpl>{impl}, reinterpret_cast<char*>(partial), held);
|
|
897
901
|
cumo_cuda_runtime_free(reinterpret_cast<char*>(partial));
|
|
898
902
|
}
|
|
899
903
|
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
#ifndef CUMO_ROW_KERNEL_H
|
|
2
|
+
#define CUMO_ROW_KERNEL_H
|
|
3
|
+
|
|
4
|
+
#include "cumo/reduce_kernel.h"
|
|
5
|
+
|
|
6
|
+
// Reducing each row of an array to a few numbers and then walking the row again
|
|
7
|
+
// to write it out is the shape layer norm and softmax share. One block takes one
|
|
8
|
+
// row, so what the reduction found stays in shared memory between the two passes
|
|
9
|
+
// instead of going back to global memory as an array of its own: written out of
|
|
10
|
+
// mean, minus, divide and the rest, a layer norm costs nine kernels and eight
|
|
11
|
+
// intermediate arrays.
|
|
12
|
+
//
|
|
13
|
+
// Impl is a reduction impl as reduce_kernel.h means it, whose MapOut answers
|
|
14
|
+
// whatever the second pass needs. Apply turns one element into one element,
|
|
15
|
+
// taking its position along the row so that a per-column operand can be read.
|
|
16
|
+
//
|
|
17
|
+
// Two things Impl has to hold to, neither of which the compiler can check.
|
|
18
|
+
// It must not read the index its Identity and MapIn are handed: the kernel
|
|
19
|
+
// below always passes 0, while the split path passes the element's real offset,
|
|
20
|
+
// so an impl that reads it answers differently depending on which path a shape
|
|
21
|
+
// takes. And it must be default constructible, since cumo_reduce_split's
|
|
22
|
+
// reduce_combine names its accumulator as decltype(Impl().Identity(0)).
|
|
23
|
+
|
|
24
|
+
namespace cumo_detail {
|
|
25
|
+
|
|
26
|
+
template <typename TypeIn, typename Impl, typename Apply>
|
|
27
|
+
__global__ void row_reduce_apply_kernel(
|
|
28
|
+
const TypeIn* x, TypeIn* y, uint64_t rows, uint64_t cols, Impl impl, Apply apply)
|
|
29
|
+
{
|
|
30
|
+
typedef decltype(impl.Identity(0)) Accum;
|
|
31
|
+
static_assert(alignof(Accum) <= 8,
|
|
32
|
+
"the shared memory below is declared __align__(8), as every other "
|
|
33
|
+
"dynamic one in cumo is, so a wider accumulator would land unaligned");
|
|
34
|
+
|
|
35
|
+
extern __shared__ __align__(8) char cumo_row_smem[];
|
|
36
|
+
Accum* sdata = reinterpret_cast<Accum*>(cumo_row_smem);
|
|
37
|
+
unsigned int tid = threadIdx.x;
|
|
38
|
+
|
|
39
|
+
for (uint64_t row = blockIdx.x; row < rows; row += gridDim.x) {
|
|
40
|
+
const TypeIn* xr = x + row * cols;
|
|
41
|
+
TypeIn* yr = y + row * cols;
|
|
42
|
+
Accum accum = impl.Identity(0);
|
|
43
|
+
|
|
44
|
+
for (uint64_t i = tid; i < cols; i += blockDim.x) {
|
|
45
|
+
impl.Reduce(impl.MapIn(xr[i], 0), accum);
|
|
46
|
+
}
|
|
47
|
+
// The tree leaves the row's total in sdata[0] behind a barrier of its
|
|
48
|
+
// own, and the second pass needs it in every thread, so the broadcast is
|
|
49
|
+
// a read of that slot rather than the value handed back.
|
|
50
|
+
reduce_in_block(accum, sdata, tid, 1, blockDim.x, true, impl);
|
|
51
|
+
auto stats = impl.MapOut(sdata[0]);
|
|
52
|
+
|
|
53
|
+
for (uint64_t i = tid; i < cols; i += blockDim.x) {
|
|
54
|
+
yr[i] = apply(xr[i], i, stats);
|
|
55
|
+
}
|
|
56
|
+
// Nobody may still be in the pass above when the next row rewrites
|
|
57
|
+
// sdata. Removing this leaves every value test passing and is only
|
|
58
|
+
// caught by compute-sanitizer --tool racecheck.
|
|
59
|
+
__syncthreads();
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// The second half of the split path. blockIdx.y names the row, so finding one
|
|
64
|
+
// costs no division, and this path is only taken where rows is small.
|
|
65
|
+
template <typename TypeIn, typename Stats, typename Apply>
|
|
66
|
+
__global__ void row_apply_kernel(
|
|
67
|
+
const TypeIn* x, TypeIn* y, const Stats* stats, uint64_t cols, Apply apply)
|
|
68
|
+
{
|
|
69
|
+
uint64_t row = blockIdx.y;
|
|
70
|
+
const TypeIn* xr = x + row * cols;
|
|
71
|
+
TypeIn* yr = y + row * cols;
|
|
72
|
+
Stats st = stats[row];
|
|
73
|
+
|
|
74
|
+
for (uint64_t i = blockIdx.x * blockDim.x + threadIdx.x; i < cols;
|
|
75
|
+
i += (uint64_t)blockDim.x * gridDim.x) {
|
|
76
|
+
yr[i] = apply(xr[i], i, st);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
} // namespace cumo_detail
|
|
81
|
+
|
|
82
|
+
// Reduces each row of a contiguous rows x cols array with impl and writes the
|
|
83
|
+
// row back through apply. Both arrays are laid out the same way and neither may
|
|
84
|
+
// be the other.
|
|
85
|
+
template <typename TypeIn, typename Impl, typename Apply>
|
|
86
|
+
void cumo_row_reduce_apply(
|
|
87
|
+
char* px, char* py, uint64_t rows, uint64_t cols, Impl impl, Apply apply)
|
|
88
|
+
{
|
|
89
|
+
typedef decltype(impl.Identity(0)) Accum;
|
|
90
|
+
typedef decltype(impl.MapOut(impl.Identity(0))) Stats;
|
|
91
|
+
// A block that takes several rows amortizes its own launch, so the grid is
|
|
92
|
+
// held below the row count on purpose. This is a tuning number and not the
|
|
93
|
+
// ceiling for this axis, which is CUMO_MAX_GRID_DIM: a million rows of 8
|
|
94
|
+
// take 1286.0us at a block per row and 1177.5 here, and two million of 4
|
|
95
|
+
// take 2357.5 against 2094.4.
|
|
96
|
+
static const uint64_t max_row_blocks = 65536;
|
|
97
|
+
int64_t want;
|
|
98
|
+
unsigned int block_dim, grid_dim;
|
|
99
|
+
size_t shared_mem_size;
|
|
100
|
+
|
|
101
|
+
if (rows == 0 || cols == 0) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// A row gets one block, so a row too long for one block to walk leaves the
|
|
106
|
+
// rest of the device idle unless there are other rows to fill it: one row of
|
|
107
|
+
// a million takes 695.8us that way, against 69.6 for the nine kernels a
|
|
108
|
+
// layer norm otherwise costs. Past that the reduction goes through the split
|
|
109
|
+
// machinery, which walks the row with the whole device, and a second kernel
|
|
110
|
+
// applies what it found.
|
|
111
|
+
if (rows < (uint64_t)cumo_detail::min_grid_size &&
|
|
112
|
+
cols > (uint64_t)(cumo_detail::max_block_size * cumo_detail::min_reduce_per_thread)) {
|
|
113
|
+
cumo_na_reduction_arg_t arg;
|
|
114
|
+
Stats* stats = (Stats*)cumo_cuda_runtime_malloc(rows * sizeof(Stats));
|
|
115
|
+
// rows is below min_grid_size to be here, which is well inside the y
|
|
116
|
+
// limit, but that is a threshold from reduce_kernel.h and not a promise
|
|
117
|
+
// about this axis, so the clamp is written out rather than assumed.
|
|
118
|
+
dim3 apply_grid((unsigned int)cumo_get_grid_dim(cols),
|
|
119
|
+
(unsigned int)(rows < CUMO_MAX_GRID_DIM_Y ? rows : CUMO_MAX_GRID_DIM_Y));
|
|
120
|
+
unsigned int apply_block = (unsigned int)cumo_get_block_dim(cols);
|
|
121
|
+
|
|
122
|
+
memset(&arg, 0, sizeof(arg));
|
|
123
|
+
arg.in.ptr = px;
|
|
124
|
+
arg.in.step[0] = (ssize_t)(cols * sizeof(TypeIn));
|
|
125
|
+
arg.in.step[1] = (ssize_t)sizeof(TypeIn);
|
|
126
|
+
arg.in_indexer.ndim = 2;
|
|
127
|
+
arg.in_indexer.total_size = rows * cols;
|
|
128
|
+
arg.in_indexer.shape[0] = rows;
|
|
129
|
+
arg.in_indexer.shape[1] = cols;
|
|
130
|
+
arg.out.ptr = (char*)stats;
|
|
131
|
+
arg.out.step[0] = (ssize_t)sizeof(Stats);
|
|
132
|
+
arg.out_indexer.ndim = 1;
|
|
133
|
+
arg.out_indexer.total_size = rows;
|
|
134
|
+
arg.out_indexer.shape[0] = rows;
|
|
135
|
+
|
|
136
|
+
cumo_reduce_split<TypeIn, Stats, Impl>(arg, Impl(impl), (char*)stats);
|
|
137
|
+
cumo_detail::row_apply_kernel<TypeIn, Stats, Apply><<<apply_grid, apply_block>>>(
|
|
138
|
+
(const TypeIn*)px, (TypeIn*)py, stats, cols, apply);
|
|
139
|
+
cumo_check_launch_holding(stats);
|
|
140
|
+
cumo_cuda_runtime_free((char*)stats);
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// How many threads to spend on a row depends on how many rows there are to
|
|
145
|
+
// go around. Past enough rows to fill the device, giving each thread a run
|
|
146
|
+
// of the row rather than an element or two keeps the tree short: 4096 rows
|
|
147
|
+
// of 768 take 84.7us at a thread per element and 31.4 at sixteen. A single
|
|
148
|
+
// row has no other block to overlap with and wants every thread it can use,
|
|
149
|
+
// where the same sixteen cost 5.6us against 2.8.
|
|
150
|
+
if (rows < (uint64_t)cumo_detail::min_grid_size) {
|
|
151
|
+
want = cumo_detail::round_up_to_power_of_2((int64_t)cols);
|
|
152
|
+
} else {
|
|
153
|
+
want = cumo_detail::round_up_to_power_of_2(
|
|
154
|
+
((int64_t)cols + cumo_detail::min_reduce_per_thread - 1) / cumo_detail::min_reduce_per_thread);
|
|
155
|
+
}
|
|
156
|
+
// reduce_in_block walks a power-of-two tree, and at one thread it returns
|
|
157
|
+
// without writing sdata at all, which would leave the broadcast reading
|
|
158
|
+
// whatever shared memory held. A whole warp is the smallest block here.
|
|
159
|
+
if (want < cumo_detail::warp_size) want = cumo_detail::warp_size;
|
|
160
|
+
if (want > cumo_detail::max_block_size) want = cumo_detail::max_block_size;
|
|
161
|
+
block_dim = (unsigned int)want;
|
|
162
|
+
grid_dim = (unsigned int)(rows < max_row_blocks ? rows : max_row_blocks);
|
|
163
|
+
shared_mem_size = block_dim * sizeof(Accum);
|
|
164
|
+
|
|
165
|
+
cumo_detail::row_reduce_apply_kernel<TypeIn, Impl, Apply><<<grid_dim, block_dim, shared_mem_size>>>(
|
|
166
|
+
(const TypeIn*)px, (TypeIn*)py, rows, cols, impl, apply);
|
|
167
|
+
cumo_cuda_runtime_check_kernel_launch();
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
#endif // CUMO_ROW_KERNEL_H
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
#ifndef CUMO_ROW_METHOD_H
|
|
2
|
+
#define CUMO_ROW_METHOD_H
|
|
3
|
+
|
|
4
|
+
#include <ruby.h>
|
|
5
|
+
#include "cumo/narray.h"
|
|
6
|
+
#include "cumo/check.h"
|
|
7
|
+
|
|
8
|
+
/* Take every pointer, then measure, then run these. Taking one runs allocate,
|
|
9
|
+
* which is Ruby and free to resize an operand, replace its buffer, or free one
|
|
10
|
+
* whose pointer was already taken, and the last of those leaves a size that
|
|
11
|
+
* still matches. Re-taking a pointer answers a different one only in that case.
|
|
12
|
+
*/
|
|
13
|
+
#define CUMO_ROW_CHECK_READ_BUFFER(a, ptr, what) \
|
|
14
|
+
do { \
|
|
15
|
+
if (cumo_na_get_offset_pointer_for_read(a) != (ptr)) { \
|
|
16
|
+
rb_raise(rb_eRuntimeError, \
|
|
17
|
+
"the buffer of %s was replaced while it was measured", \
|
|
18
|
+
(what)); \
|
|
19
|
+
} \
|
|
20
|
+
} while (0)
|
|
21
|
+
|
|
22
|
+
#define CUMO_ROW_CHECK_WRITE_BUFFER(a, ptr, what) \
|
|
23
|
+
do { \
|
|
24
|
+
if (cumo_na_get_offset_pointer_for_write(a) != (ptr)) { \
|
|
25
|
+
rb_raise(rb_eRuntimeError, \
|
|
26
|
+
"the buffer of %s was replaced while it was measured", \
|
|
27
|
+
(what)); \
|
|
28
|
+
} \
|
|
29
|
+
} while (0)
|
|
30
|
+
|
|
31
|
+
#define CUMO_ROW_CHECK_SAME_SHAPE(nout, nin) \
|
|
32
|
+
do { \
|
|
33
|
+
cumo_narray_t *cumo_row_out = (nout); \
|
|
34
|
+
cumo_narray_t *cumo_row_in = (nin); \
|
|
35
|
+
int cumo_row_i; \
|
|
36
|
+
if (cumo_row_out->ndim != cumo_row_in->ndim) { \
|
|
37
|
+
rb_raise(cumo_na_eShapeError, \
|
|
38
|
+
"self or the result was reshaped while it was measured"); \
|
|
39
|
+
} \
|
|
40
|
+
for (cumo_row_i = 0; cumo_row_i < cumo_row_in->ndim; ++cumo_row_i) { \
|
|
41
|
+
if (cumo_row_out->shape[cumo_row_i] != cumo_row_in->shape[cumo_row_i]) { \
|
|
42
|
+
rb_raise(cumo_na_eShapeError, \
|
|
43
|
+
"self or the result was reshaped while it was measured"); \
|
|
44
|
+
} \
|
|
45
|
+
} \
|
|
46
|
+
} while (0)
|
|
47
|
+
|
|
48
|
+
#endif /* CUMO_ROW_METHOD_H */
|
|
@@ -176,14 +176,40 @@ cumo_get_block_dim(size_t n)
|
|
|
176
176
|
return (block_dim == 0) ? 1 : block_dim;
|
|
177
177
|
}
|
|
178
178
|
|
|
179
|
+
#if defined(__cplusplus)
|
|
180
|
+
extern "C" {
|
|
181
|
+
#endif
|
|
182
|
+
|
|
179
183
|
// Raises the error a kernel launch reported, if any. Defined in cuda/runtime.c
|
|
180
184
|
// because raising needs ruby.h, which the .cu translation units do not include.
|
|
181
185
|
void cumo_cuda_runtime_check_kernel_launch(void);
|
|
182
186
|
|
|
187
|
+
// The forms to call while scratch is held, which free what they are given
|
|
188
|
+
// before they raise. Pass every buffer that is still outstanding, including the
|
|
189
|
+
// caller's: a raise here leaves through longjmp and nothing below it runs.
|
|
190
|
+
void cumo_cuda_runtime_check_kernel_launch_holding(char *p0, char *p1, char *p2, char *p3, char *p4);
|
|
191
|
+
void cumo_cuda_runtime_check_taken_status_holding(int status, char *p0, char *p1, char *p2, char *p3, char *p4);
|
|
192
|
+
void cumo_cuda_runtime_return_scratch(char *ptr, int wait_for_stream, cudaError_t *status);
|
|
193
|
+
|
|
194
|
+
#if defined(__cplusplus)
|
|
195
|
+
static inline void cumo_check_launch_holding(void *p0 = 0, void *p1 = 0, void *p2 = 0, void *p3 = 0, void *p4 = 0)
|
|
196
|
+
{
|
|
197
|
+
cumo_cuda_runtime_check_kernel_launch_holding((char*)p0, (char*)p1, (char*)p2, (char*)p3, (char*)p4);
|
|
198
|
+
}
|
|
199
|
+
static inline void cumo_check_status_holding(cudaError_t status, void *p0 = 0, void *p1 = 0, void *p2 = 0, void *p3 = 0, void *p4 = 0)
|
|
200
|
+
{
|
|
201
|
+
cumo_cuda_runtime_check_taken_status_holding((int)status, (char*)p0, (char*)p1, (char*)p2, (char*)p3, (char*)p4);
|
|
202
|
+
}
|
|
203
|
+
#endif
|
|
204
|
+
|
|
183
205
|
// Scratch memory for a kernel that needs somewhere to put partial results.
|
|
184
206
|
// Declared here rather than including cuda/memory_pool.h, which needs ruby.h.
|
|
185
207
|
char* cumo_cuda_runtime_malloc(size_t size);
|
|
186
208
|
void cumo_cuda_runtime_free(char *ptr);
|
|
187
209
|
|
|
210
|
+
#if defined(__cplusplus)
|
|
211
|
+
}
|
|
212
|
+
#endif
|
|
213
|
+
|
|
188
214
|
|
|
189
215
|
#endif /* ifndef CUMO_TEMPLATE_KERNEL_H */
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
#ifndef CUMO_BF16_DEF_H
|
|
2
|
+
#define CUMO_BF16_DEF_H
|
|
3
|
+
|
|
4
|
+
// A struct rather than a bare uint16, so that C code which reaches for an
|
|
5
|
+
// arithmetic operator on an element fails to compile instead of computing on
|
|
6
|
+
// the bit pattern. Its layout matches __nv_bfloat16, which the kernels use.
|
|
7
|
+
typedef struct { unsigned short x; } cumo_bfloat;
|
|
8
|
+
|
|
9
|
+
static inline float cumo_bfloat2float(cumo_bfloat b)
|
|
10
|
+
{
|
|
11
|
+
union { unsigned int u; float f; } o;
|
|
12
|
+
o.u = (unsigned int)b.x << 16;
|
|
13
|
+
return o.f;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
static inline cumo_bfloat cumo_float2bfloat(float f)
|
|
17
|
+
{
|
|
18
|
+
union { unsigned int u; float f; } v;
|
|
19
|
+
cumo_bfloat b;
|
|
20
|
+
|
|
21
|
+
v.f = f;
|
|
22
|
+
// bfloat16 keeps the exponent of a float, so every finite value is the top
|
|
23
|
+
// 16 bits rounded to nearest even. A NaN carries its payload in the low
|
|
24
|
+
// bits, and truncating one leaves an all-ones exponent over a zero
|
|
25
|
+
// mantissa, which reads as an infinity.
|
|
26
|
+
if ((v.u & 0x7f800000u) == 0x7f800000u && (v.u & 0x007fffffu) != 0u) {
|
|
27
|
+
b.x = (unsigned short)(((v.u >> 16) & 0x8000u) | 0x7fc0u);
|
|
28
|
+
return b;
|
|
29
|
+
}
|
|
30
|
+
b.x = (unsigned short)((v.u + 0x7fffu + ((v.u >> 16) & 1u)) >> 16);
|
|
31
|
+
return b;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
static inline cumo_bfloat cumo_double2bfloat(double d)
|
|
35
|
+
{
|
|
36
|
+
union { unsigned long long u; double d; } v;
|
|
37
|
+
cumo_bfloat b;
|
|
38
|
+
unsigned long long u, sign, mant, round_bits, tie;
|
|
39
|
+
unsigned int shift, out;
|
|
40
|
+
int exp;
|
|
41
|
+
|
|
42
|
+
// Going through float would round twice, and the first rounding can land
|
|
43
|
+
// on a tie the second one then moves the wrong way.
|
|
44
|
+
v.d = d;
|
|
45
|
+
u = v.u;
|
|
46
|
+
sign = (u >> 48) & 0x8000ull;
|
|
47
|
+
u &= 0x7fffffffffffffffull;
|
|
48
|
+
|
|
49
|
+
if (u > 0x7ff0000000000000ull) {
|
|
50
|
+
b.x = (unsigned short)(sign | 0x7fc0u);
|
|
51
|
+
return b;
|
|
52
|
+
}
|
|
53
|
+
if (u >= 0x47f0000000000000ull) {
|
|
54
|
+
b.x = (unsigned short)(sign | 0x7f80u);
|
|
55
|
+
return b;
|
|
56
|
+
}
|
|
57
|
+
if (u < 0x3790000000000000ull) {
|
|
58
|
+
b.x = (unsigned short)sign;
|
|
59
|
+
return b;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
exp = (int)(u >> 52) - 1023;
|
|
63
|
+
mant = u & 0xfffffffffffffull;
|
|
64
|
+
if (exp >= -126) {
|
|
65
|
+
out = (unsigned int)(exp + 127) << 7;
|
|
66
|
+
shift = 45;
|
|
67
|
+
} else {
|
|
68
|
+
mant |= 0x10000000000000ull;
|
|
69
|
+
shift = (unsigned int)(-81 - exp);
|
|
70
|
+
out = 0;
|
|
71
|
+
}
|
|
72
|
+
round_bits = mant & ((1ull << shift) - 1ull);
|
|
73
|
+
tie = 1ull << (shift - 1);
|
|
74
|
+
out += (unsigned int)(mant >> shift);
|
|
75
|
+
if (round_bits > tie || (round_bits == tie && ((mant >> shift) & 1ull))) {
|
|
76
|
+
out += 1;
|
|
77
|
+
}
|
|
78
|
+
b.x = (unsigned short)(sign | out);
|
|
79
|
+
return b;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
#endif // CUMO_BF16_DEF_H
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
#ifndef CUMO_BF16_DEF_KERNEL_H
|
|
2
|
+
#define CUMO_BF16_DEF_KERNEL_H
|
|
3
|
+
|
|
4
|
+
#include <cuda_bf16.h>
|
|
5
|
+
|
|
6
|
+
typedef __nv_bfloat16 cumo_bfloat;
|
|
7
|
+
|
|
8
|
+
// The arithmetic on __nv_bfloat16 starts at sm_80, but the conversions have no
|
|
9
|
+
// such floor, so every operation is computed in float. For + - * / and sqrt
|
|
10
|
+
// that answers what the native instruction would: a float carries three times
|
|
11
|
+
// the mantissa bits of a bfloat16, so the second rounding cannot land on a tie
|
|
12
|
+
// the first one moved.
|
|
13
|
+
__host__ __device__ static inline float cumo_bfloat2float(cumo_bfloat b)
|
|
14
|
+
{
|
|
15
|
+
return __bfloat162float(b);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
__host__ __device__ static inline cumo_bfloat cumo_float2bfloat(float f)
|
|
19
|
+
{
|
|
20
|
+
return __float2bfloat16(f);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
__host__ __device__ static inline cumo_bfloat cumo_double2bfloat(double d)
|
|
24
|
+
{
|
|
25
|
+
return __double2bfloat16(d);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
#endif // CUMO_BF16_DEF_KERNEL_H
|