Skip to content

Commit 0e4fb2f

Browse files
authored
Add function transposeCtoF for transposing multi-d array (#4681)
1 parent 2d8c2e7 commit 0e4fb2f

1 file changed

Lines changed: 161 additions & 0 deletions

File tree

Src/Base/AMReX_BaseFabUtility.H

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,167 @@ void fill (BaseFab<STRUCT>& aos_fab, F const& f)
9595
}
9696
}
9797

98+
//! Transpose 3D array (nx,ny,nz) from row-major (i.e. C order) to
99+
//! column-major (Fortran order). The input's unit stride direction is z,
100+
//! whereas the output's unit stride direction is x.
101+
template <typename T>
102+
void transposeCtoF (T const* pi, T* po, int nx, int ny, int nz)
103+
{
104+
AMREX_ALWAYS_ASSERT(pi != po);
105+
106+
#if defined(AMREX_USE_CUDA) || defined(AMREX_USE_HIP)
107+
108+
constexpr int tile_dim = 32;
109+
constexpr int block_rows = 16;
110+
constexpr int nthreads = tile_dim*block_rows;
111+
112+
// Each block has tile_dim x block_rows threads. They work on a tile_dim
113+
// x tile_dim tile.
114+
115+
dim3 block{unsigned(tile_dim), unsigned(block_rows), 1};
116+
dim3 grid{unsigned((nx+tile_dim-1)/tile_dim),
117+
unsigned((nz+tile_dim-1)/tile_dim), unsigned(ny)};
118+
119+
AMREX_LAUNCH_KERNEL(nthreads, grid, block, 0, Gpu::gpuStream(),
120+
[=] AMREX_GPU_DEVICE ()
121+
{
122+
__shared__ T tile[tile_dim][tile_dim+1]; // +1 to avoid bank conflicts
123+
124+
int k = blockIdx.y * tile_dim + threadIdx.x; // for loading z-direction
125+
int i = blockIdx.x * tile_dim + threadIdx.y; // for loading x-direction
126+
127+
int j = blockIdx.z; // for y-direction
128+
129+
if (k < nz) {
130+
for (int it = 0; it < tile_dim; it += block_rows, i += block_rows) {
131+
if (i < nx) {
132+
// x z
133+
tile[threadIdx.y+it][threadIdx.x] = pi[k + (j+i*std::size_t(ny))*nz];
134+
}
135+
}
136+
}
137+
138+
__syncthreads();
139+
140+
i = blockIdx.x * tile_dim + threadIdx.x; // for storing x-direction
141+
k = blockIdx.y * tile_dim + threadIdx.y; // for storing z-direction
142+
143+
if (i < nx) {
144+
for (int it = 0; it < tile_dim; it += block_rows, k += block_rows) {
145+
if (k < nz) {
146+
po[i + (j+k*std::size_t(ny))*nx] = tile[threadIdx.x][threadIdx.y+it];
147+
}
148+
}
149+
}
150+
});
151+
AMREX_GPU_ERROR_CHECK();
152+
Gpu::streamSynchronize();
153+
154+
#elif defined(AMREX_USE_SYCL)
155+
156+
constexpr int tile_dim = 32;
157+
constexpr int block_rows = 8;
158+
159+
// Each block has tile_dim x block_rows threads. They work on a tile_dim
160+
// x tile_dim tile.
161+
162+
sycl::range<3> block{std::size_t(1), std::size_t(block_rows), std::size_t(tile_dim)};
163+
sycl::range<3> grid{std::size_t(ny), std::size_t((nz+tile_dim-1)/tile_dim),
164+
std::size_t((nx+tile_dim-1)/tile_dim)};
165+
sycl::range<3> global_size{grid[0]*block[0],
166+
grid[1]*block[1],
167+
grid[2]*block[2]};
168+
169+
auto& q = *(Gpu::gpuStream().queue);
170+
try {
171+
q.submit([&] (sycl::handler& h)
172+
{
173+
auto tile = sycl::local_accessor<T,2>(sycl::range<2>(tile_dim,tile_dim+1),h);
174+
175+
h.parallel_for(sycl::nd_range<3>(global_size, block),
176+
[=] (sycl::nd_item<3> item)
177+
{
178+
auto group = item.get_group();
179+
dim3 blockIdx{unsigned(group.get_group_id(2)),
180+
unsigned(group.get_group_id(1)),
181+
unsigned(group.get_group_id(0))};
182+
dim3 threadIdx{unsigned(item.get_local_id(2)),
183+
unsigned(item.get_local_id(1)),
184+
unsigned(item.get_local_id(0))};
185+
186+
int k = blockIdx.y * tile_dim + threadIdx.x; // for loading z-direction
187+
int i = blockIdx.x * tile_dim + threadIdx.y; // for loading x-direction
188+
189+
int j = blockIdx.z; // for y-direction
190+
191+
if (k < nz) {
192+
for (int it = 0; it < tile_dim; it += block_rows, i += block_rows) {
193+
if (i < nx) {
194+
// x z
195+
tile[threadIdx.y+it][threadIdx.x] = pi[k + (j+i*std::size_t(ny))*nz];
196+
}
197+
}
198+
}
199+
200+
item.barrier(sycl::access::fence_space::local_space);
201+
202+
i = blockIdx.x * tile_dim + threadIdx.x; // for storing x-direction
203+
k = blockIdx.y * tile_dim + threadIdx.y; // for storing z-direction
204+
205+
if (i < nx) {
206+
for (int it = 0; it < tile_dim; it += block_rows, k += block_rows) {
207+
if (k < nz) {
208+
po[i + (j+k*std::size_t(ny))*nx] = tile[threadIdx.x][threadIdx.y+it];
209+
}
210+
}
211+
}
212+
});
213+
});
214+
} catch (sycl::exception const& ex) {
215+
amrex::Abort(std::string("transposeCtoF: ")+ex.what()+"!!!!!");
216+
}
217+
Gpu::streamSynchronize();
218+
219+
#else
220+
221+
constexpr int bx = 32;
222+
constexpr int bz = 32;
223+
224+
std::size_t nxy = std::size_t(nx) * ny;
225+
std::size_t nyz = std::size_t(ny) * nz;
226+
227+
#ifdef AMREX_USE_OMP
228+
#pragma omp parallel for collapse(3)
229+
#endif
230+
for (int j = 0; j < ny; ++j) {
231+
for (int k0 = 0; k0 < nz; k0 += bz) {
232+
for (int i0 = 0; i0 < nx; i0 += bx) {
233+
int imax = std::min(i0+bx, nx);
234+
int kmax = std::min(k0+bz, nz);
235+
auto * AMREX_RESTRICT pdst = po + j*std::size_t(nx);
236+
auto const* AMREX_RESTRICT psrc = pi + j*std::size_t(nz);
237+
for (int i = i0; i < imax; ++i) {
238+
AMREX_PRAGMA_SIMD
239+
for (int k = k0; k < kmax; ++k) {
240+
pdst[i + k*nxy] = psrc[k + i*nyz];
241+
}
242+
}
243+
}
244+
}
245+
}
246+
247+
#endif
248+
}
249+
250+
//! Transpose 2D array (nx,ny) from row-major (i.e. C order) to column-major
251+
//! (Fortran order). The input's unit stride direction is y, whereas the
252+
//! output's unit stride direction is x.
253+
template <typename T>
254+
void transposeCtoF (T const* pi, T* po, int nx, int ny)
255+
{
256+
transposeCtoF(pi, po, nx, 1, ny);
257+
}
258+
98259
}
99260

100261
#endif

0 commit comments

Comments
 (0)