@@ -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