tatami_mult
Multiply tatami matrices
Loading...
Searching...
No Matches
row_to_column.hpp
Go to the documentation of this file.
1#ifndef TATAMI_MULT_DENSE_MATRIX_DENSE_ROW_ROW_TO_COLUMN_HPP
2#define TATAMI_MULT_DENSE_MATRIX_DENSE_ROW_ROW_TO_COLUMN_HPP
3
4#include <vector>
5#include <cstddef>
6#include <optional>
7
8#include "sanisizer/sanisizer.hpp"
9#include "tatami/tatami.hpp"
10
11#include "../utils.hpp"
12#include "../../utils.hpp"
13
19namespace tatami_mult {
20
21/* See https://github.com/tatami-inc/test-multiplication/tree/master/dense_row/dense_matrix
22 * for an explanation of the choice of algorithm.
23 */
24
49
67template<typename LeftValue_, typename LeftIndex_, typename RightColumns_, class GetRightRow_, typename Output_>
70 const RightColumns_ right_columns,
71 GetRightRow_ get_right_row,
72 Output_* const output,
74) {
75 const auto left_NR = left.nrow();
76 const auto common_dim = left.ncol();
77
78 if (options.primary_block_size == 1) {
79 tatami::parallelize([&](int, LeftIndex_ start, LeftIndex_ length) -> void {
80 auto ext = tatami::consecutive_extractor<false>(left, true, start, length);
82
83 // Use a temporary buffer to mimic an output row.
84 // This gives us contiguous writes in the innermost loop while mitigating false sharing.
85 auto tmp_output = tatami::create_container_of_Index_size<std::vector<Output_> >(right_columns);
86
87 for (LeftIndex_ lr = 0; lr < length; ++lr) {
88 const auto left_ptr = ext->fetch(buffer.data());
89 std::fill(tmp_output.begin(), tmp_output.end(), 0);
90 for (LeftIndex_ cd = 0; cd < common_dim; ++cd) {
91 const Output_ mult = left_ptr[cd];
92 const auto rightrow = get_right_row(cd);
93 for (RightColumns_ rc = 0; rc < right_columns; ++rc) {
94 tmp_output[rc] += static_cast<Output_>(rightrow[rc]) * mult;
95 }
96 }
97 for (RightColumns_ rc = 0; rc < right_columns; ++rc) {
98 output[sanisizer::nd_offset<std::size_t>(start + lr, left_NR, rc)] = tmp_output[rc];
99 }
100 }
101 }, left_NR, options.num_threads);
102
103 } else {
104 tatami::parallelize([&](int, LeftIndex_ start, LeftIndex_ length) -> void {
105 auto left_ext = tatami::consecutive_extractor<false>(left, true, start, length);
106 std::vector<std::vector<LeftValue_> > left_buffers;
107 std::vector<const LeftValue_*> left_ptrs;
108
109 std::vector<Output_> tmp_output;
110 {
111 const LeftIndex_ max_block_rows = sanisizer::min(length, options.primary_block_size);
112 left_buffers.reserve(max_block_rows);
113 for (LeftIndex_ b = 0; b < max_block_rows; ++b) {
114 left_buffers.emplace_back(tatami::cast_Index_to_container_size<std::vector<LeftValue_> >(common_dim));
115 }
116 sanisizer::resize(left_ptrs, max_block_rows);
117
118 // Creating a block to hold the output during the updates over all 'c'.
119 // This enables contiguous writes in the innermost loop while also avoiding false sharing.
120 // Hopefully std::vector can store this - I'd be very surprised if size_type != size_t, but we check anyway.
121 sanisizer::resize(tmp_output, sanisizer::product_unsafe<std::size_t>(max_block_rows, right_columns));
122 }
123
124 LeftIndex_ lr = 0;
125 while (lr < length) {
126 const LeftIndex_ lr_num = sanisizer::min(options.primary_block_size, length - lr);
127 for (LeftIndex_ lr_counter = 0; lr_counter < lr_num; ++lr_counter) {
128 left_ptrs[lr_counter] = left_ext->fetch(left_buffers[lr_counter].data());
129 }
130
131 const auto out_space = sanisizer::product_unsafe<std::size_t>(lr_num, right_columns);
132 std::fill_n(tmp_output.data(), out_space, 0);
133
134 LeftIndex_ cd = 0;
135 while (cd < common_dim) {
136 const LeftIndex_ cd_end = cd + sanisizer::min(options.primary_block_size, common_dim - cd);
137 RightColumns_ rc = 0;
138 while (rc < right_columns) {
139 const RightColumns_ rc_end = rc + sanisizer::min(options.secondary_block_size, right_columns - rc);
140
141 for (LeftIndex_ lr_counter = 0; lr_counter < lr_num; ++lr_counter) {
142 const auto matrow = left_ptrs[lr_counter];
143 const auto prod = tmp_output.data() + sanisizer::product_unsafe<std::size_t>(lr_counter, right_columns);
144 for (auto cd_copy = cd; cd_copy < cd_end; ++cd_copy) {
145 const auto mult = matrow[cd_copy];
146 const auto rightrow = get_right_row(cd_copy);
147 for (auto rc_copy = rc; rc_copy < rc_end; ++rc_copy) {
148 prod[rc_copy] += mult * rightrow[rc_copy];
149 }
150 }
151 }
152
153 rc = rc_end;
154 }
155 cd = cd_end;
156 }
157
158 // Transposition using square blocks of the smaller (primary) block size.
159 RightColumns_ rct = 0;
160 while (rct < right_columns) {
161 const RightColumns_ rct_end = rct + sanisizer::min(options.primary_block_size, right_columns - rct);
162 for (LeftIndex_ lr_counter = 0; lr_counter < lr_num; ++lr_counter) {
163 for (auto rct_copy = rct; rct_copy < rct_end; ++rct_copy) {
164 const auto val = tmp_output[sanisizer::nd_offset<std::size_t>(rct_copy, right_columns, lr_counter)];
165 output[sanisizer::nd_offset<std::size_t>(start + lr + lr_counter, left_NR, rct_copy)] = val;
166 }
167 }
168 rct = rct_end;
169 }
170
171 lr += lr_num;
172 }
173 }, left_NR, options.num_threads);
174 }
175}
176
197template<typename LeftValue_, typename LeftIndex_, typename RightValue_, typename RightIndex_, typename Output_>
201 Output_* const output,
203) {
204 const auto common_dim = left.ncol();
207 const auto right_NC = right.ncol();
208 populate_dense_buffers(true, common_dim, right_NC, right, right_buffers, right_ptrs, options.num_threads);
209
211 left,
212 right_NC,
213 [&](const LeftIndex_ cd) -> const RightValue_* {
214 return right_ptrs[cd];
215 },
216 output,
217 options
218 );
219}
220
221}
222
223#endif
virtual Index_ ncol() const=0
virtual Index_ nrow() const=0
Multiplication of tatami matrices.
Definition column_to_column.hpp:19
void multiply_dense_row_with_dense_row_matrix_to_column_output(const tatami::Matrix< LeftValue_, LeftIndex_ > &left, const RightColumns_ right_columns, GetRightRow_ get_right_row, Output_ *const output, const MultiplyDenseRowWithDenseRowMatrixToColumnOutputOptions &options)
Definition row_to_column.hpp:68
int parallelize(Function_ fun, const Index_ tasks, const int workers)
I< decltype(std::declval< Container_ >().size())> cast_Index_to_container_size(const Index_ x)
Container_ create_container_of_Index_size(const Index_ x, Args_ &&... args)
auto consecutive_extractor(const Matrix< Value_, Index_ > &matrix, const bool row, const Index_ iter_start, const Index_ iter_length, Args_ &&... args)
Options for multiply_dense_row_with_dense_row_matrix_to_column_output().
Definition row_to_column.hpp:28