/*******************************************************************************
* This file is part of SWIFT.
* Copyright (c) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*
******************************************************************************/
/* Config parameters. */
#include
/* This object's header. */
#include "mesh_gravity_sort.h"
/* Local includes. */
#include "align.h"
#include "atomic.h"
#include "error.h"
#include "row_major_id.h"
#include "threadpool.h"
struct mapper_extra_data {
/* Mesh size */
int N;
/* Buckets */
size_t *bucket_counts;
};
/**
* @param Count how may mesh cells will end up in each x-coord bucket.
*/
void bucket_sort_mesh_key_value_rho_count_mapper(void *map_data, int nr_parts,
void *extra_data) {
/* Unpack the data */
const struct mesh_key_value_rho *array_in =
(const struct mesh_key_value_rho *)map_data;
struct mapper_extra_data *data = (struct mapper_extra_data *)extra_data;
const int N = data->N;
size_t *global_bucket_counts = data->bucket_counts;
/* Local buckets */
size_t *local_bucket_counts = (size_t *)calloc(N, sizeof(size_t));
/* Count how many items will land in each bucket. */
for (int i = 0; i < nr_parts; ++i) {
const size_t key = array_in[i].key;
/* Get the x coordinate of this mesh cell in the global mesh
* Note: we don't need to sort more precisely than just
* the x coordinate */
const int mesh_x = get_xcoord_from_padded_row_major_id(key, N);
#ifdef SWIFT_DEBUG_CHECKS
if (mesh_x < 0) error("Invalid mesh cell x-coordinate (too small)");
if (mesh_x >= N) error("Invalid mesh cell x-coordinate (too large)");
#endif
/* Add a contribution to the bucket count */
local_bucket_counts[mesh_x]++;
}
/* Now write back to memory */
for (int i = 0; i < N; ++i) {
atomic_add(&global_bucket_counts[i], local_bucket_counts[i]);
}
/* Clean up */
free(local_bucket_counts);
}
/**
* @param Count how may mesh cells will end up in each x-coord bucket.
*/
void bucket_sort_mesh_key_value_pot_count_mapper(void *map_data, int nr_parts,
void *extra_data) {
/* Unpack the data */
const struct mesh_key_value_pot *array_in =
(const struct mesh_key_value_pot *)map_data;
struct mapper_extra_data *data = (struct mapper_extra_data *)extra_data;
const int N = data->N;
size_t *global_bucket_counts = data->bucket_counts;
/* Local buckets */
size_t *local_bucket_counts = (size_t *)calloc(N, sizeof(size_t));
/* Count how many items will land in each bucket. */
for (int i = 0; i < nr_parts; ++i) {
const size_t key = array_in[i].key;
/* Get the x coordinate of this mesh cell in the global mesh
* Note: we don't need to sort more precisely than just
* the x coordinate */
const int mesh_x = get_xcoord_from_padded_row_major_id(key, N);
#ifdef SWIFT_DEBUG_CHECKS
if (mesh_x < 0) error("Invalid mesh cell x-coordinate (too small)");
if (mesh_x >= N) error("Invalid mesh cell x-coordinate (too large)");
#endif
/* Add a contribution to the bucket count */
local_bucket_counts[mesh_x]++;
}
/* Now write back to memory */
for (int i = 0; i < N; ++i) {
atomic_add(&global_bucket_counts[i], local_bucket_counts[i]);
}
/* Clean up */
free(local_bucket_counts);
}
/**
* @param Count how may mesh cells will end up in each cell index
*/
void bucket_sort_mesh_key_value_pot_index_count_mapper(void *map_data,
int nr_parts,
void *extra_data) {
/* Unpack the data */
const struct mesh_key_value_pot *array_in =
(const struct mesh_key_value_pot *)map_data;
struct mapper_extra_data *data = (struct mapper_extra_data *)extra_data;
const int N = data->N;
size_t *global_bucket_counts = data->bucket_counts;
/* Local buckets */
size_t *local_bucket_counts = (size_t *)calloc(N, sizeof(size_t));
/* Count how many items will land in each bucket. */
for (int i = 0; i < nr_parts; ++i) {
const size_t key = array_in[i].cell_index;
/* Get the cell_index */
const int cell_id = cell_index_extract_patch_index(key);
#ifdef SWIFT_DEBUG_CHECKS
if (cell_id < 0) error("Invalid mesh cell x-coordinate (too small)");
if (cell_id >= N) error("Invalid mesh cell x-coordinate (too large)");
#endif
/* Add a contribution to the bucket count */
local_bucket_counts[cell_id]++;
}
/* Now write back to memory */
for (int i = 0; i < N; ++i) {
atomic_add(&global_bucket_counts[i], local_bucket_counts[i]);
}
/* Clean up */
free(local_bucket_counts);
}
/**
* @brief Bucket sort of the array of mesh cells based on their x-coord.
*
* Note the two mesh_key_value_rho arrays must be aligned on
* SWIFT_CACHE_ALIGNMENT.
*
* @param array_in The unsorted array of mesh-key value pairs.
* @param count The number of elements in the mesh-key value pair arrays.
* @param N The size of the mesh. (i.e. the number of possible x-axis values)
* @param tp The #threadpool object.
* @param array_out The sorted array of mesh-key value pairs (to be filled).
* @param bucket_offsets The offsets in the sorted array where we change x-coord
* (to be filled).
*/
void bucket_sort_mesh_key_value_rho(const struct mesh_key_value_rho *array_in,
const size_t count, const int N,
struct threadpool *tp,
struct mesh_key_value_rho *array_out,
size_t *bucket_offsets) {
/* Create an array of bucket counts and one of offsets */
size_t *bucket_counts = (size_t *)malloc(N * sizeof(size_t));
memset(bucket_counts, 0, N * sizeof(size_t));
struct mapper_extra_data extra_data;
extra_data.N = N;
extra_data.bucket_counts = bucket_counts;
/* Collect the number of items that will end up in each bucket */
threadpool_map(tp, bucket_sort_mesh_key_value_rho_count_mapper,
(void *)array_in, count, sizeof(struct mesh_key_value_rho),
threadpool_auto_chunk_size, &extra_data);
#ifdef SWIFT_DEBUG_CHECKS
size_t count_check = 0;
for (int i = 0; i < N; ++i) count_check += bucket_counts[i];
if (count_check != count) error("Bucket count is not matching");
#endif
/* Now we can build the array of offsets (cumsum of the counts) */
bucket_offsets[0] = 0;
for (int i = 1; i < N; ++i) {
bucket_offsets[i] = bucket_offsets[i - 1] + bucket_counts[i - 1];
}
/* Remind the compiler that the array is nicely aligned */
swift_declare_aligned_ptr(struct mesh_key_value_rho, array_out_aligned,
array_out, SWIFT_CACHE_ALIGNMENT);
swift_declare_aligned_ptr(const struct mesh_key_value_rho, array_in_aligned,
array_in, SWIFT_CACHE_ALIGNMENT);
/* Now, we can do the actual sorting */
for (size_t i = 0; i < count; ++i) {
const size_t key = array_in_aligned[i].key;
const int mesh_x = get_xcoord_from_padded_row_major_id(key, N);
/* Where does this element land? */
const size_t index = bucket_offsets[mesh_x];
/* Copy the element to its correct position */
memcpy(&array_out_aligned[index], &array_in_aligned[i],
sizeof(struct mesh_key_value_rho));
/* Move the start of this bucket by one */
bucket_offsets[mesh_x]++;
}
#ifdef SWIFT_DEBUG_CHECKS
/* Verify that things have indeed been sorted */
int last_mesh_x =
get_xcoord_from_padded_row_major_id(array_out_aligned[0].key, N);
;
for (size_t i = 1; i < count; ++i) {
const size_t key = array_out_aligned[i].key;
const int mesh_x = get_xcoord_from_padded_row_major_id(key, N);
if (mesh_x < last_mesh_x) error("Unsorted array!");
last_mesh_x = mesh_x;
}
#endif
/* Restore the bucket offsets to their former glory */
for (int i = 0; i < N; ++i) {
bucket_offsets[i] -= bucket_counts[i];
}
/* Clean up! */
free(bucket_counts);
}
/**
* @brief Bucket sort of the array of mesh cells based on their x-coord.
*
* Note the two mesh_key_value_pot arrays must be aligned on
* SWIFT_CACHE_ALIGNMENT.
*
* @param array_in The unsorted array of mesh-key value pairs.
* @param count The number of elements in the mesh-key value pair arrays.
* @param N The size of the mesh. (i.e. the number of possible x-axis values)
* @param tp The #threadpool object.
* @param array_out The sorted array of mesh-key value pairs (to be filled).
* @param bucket_offsets The offsets in the sorted array where we change x-coord
* (to be filled).
*/
void bucket_sort_mesh_key_value_pot(const struct mesh_key_value_pot *array_in,
const size_t count, const int N,
struct threadpool *tp,
struct mesh_key_value_pot *array_out,
size_t *bucket_offsets) {
/* Create an array of bucket counts and one of offsets */
size_t *bucket_counts = (size_t *)malloc(N * sizeof(size_t));
memset(bucket_counts, 0, N * sizeof(size_t));
struct mapper_extra_data extra_data;
extra_data.N = N;
extra_data.bucket_counts = bucket_counts;
/* Collect the number of items that will end up in each bucket */
threadpool_map(tp, bucket_sort_mesh_key_value_pot_count_mapper,
(void *)array_in, count, sizeof(struct mesh_key_value_pot),
threadpool_auto_chunk_size, &extra_data);
#ifdef SWIFT_DEBUG_CHECKS
size_t count_check = 0;
for (int i = 0; i < N; ++i) count_check += bucket_counts[i];
if (count_check != count) error("Bucket count is not matching");
#endif
/* Now we can build the array of offsets (cumsum of the counts) */
bucket_offsets[0] = 0;
for (int i = 1; i < N; ++i) {
bucket_offsets[i] = bucket_offsets[i - 1] + bucket_counts[i - 1];
}
/* Remind the compiler that the array is nicely aligned */
swift_declare_aligned_ptr(struct mesh_key_value_pot, array_out_aligned,
array_out, SWIFT_CACHE_ALIGNMENT);
swift_declare_aligned_ptr(const struct mesh_key_value_pot, array_in_aligned,
array_in, SWIFT_CACHE_ALIGNMENT);
/* Now, we can do the actual sorting */
for (size_t i = 0; i < count; ++i) {
const size_t key = array_in_aligned[i].key;
const int mesh_x = get_xcoord_from_padded_row_major_id(key, N);
/* Where does this element land? */
const size_t index = bucket_offsets[mesh_x];
/* Copy the element to its correct position */
memcpy(&array_out_aligned[index], &array_in_aligned[i],
sizeof(struct mesh_key_value_pot));
/* Move the start of this bucket by one */
bucket_offsets[mesh_x]++;
}
#ifdef SWIFT_DEBUG_CHECKS
/* Verify that things have indeed been sorted */
int last_mesh_x =
get_xcoord_from_padded_row_major_id(array_out_aligned[0].key, N);
for (size_t i = 1; i < count; ++i) {
const size_t key = array_out_aligned[i].key;
const int mesh_x = get_xcoord_from_padded_row_major_id(key, N);
if (mesh_x < last_mesh_x) error("Unsorted array!");
last_mesh_x = mesh_x;
}
#endif
/* Restore the bucket offsets to their former glory */
for (int i = 0; i < N; ++i) {
bucket_offsets[i] -= bucket_counts[i];
}
/* Clean up! */
free(bucket_counts);
}
/**
* @brief Bucket sort of the array of mesh cells based on their cell index.
*
* Note the two mesh_key_value_pot arrays must be aligned on
* SWIFT_CACHE_ALIGNMENT.
*
* @param array_in The unsorted array of mesh-key value pairs.
* @param count The number of elements in the mesh-key value pair arrays.
* @param N The number of buckets (i.e. the nr. of local top-level cells).
* @param tp The #threadpool object.
* @param array_out The sorted array of mesh-key value pairs (to be filled).
*/
void bucket_sort_mesh_key_value_pot_index(
const struct mesh_key_value_pot *array_in, const size_t count, const int N,
struct threadpool *tp, struct mesh_key_value_pot *array_out) {
/* Create an array of bucket counts and one of offsets */
size_t *bucket_counts = (size_t *)malloc(N * sizeof(size_t));
size_t *bucket_offsets = (size_t *)malloc(N * sizeof(size_t));
memset(bucket_counts, 0, N * sizeof(size_t));
struct mapper_extra_data extra_data;
extra_data.N = N;
extra_data.bucket_counts = bucket_counts;
/* Collect the number of items that will end up in each bucket */
threadpool_map(tp, bucket_sort_mesh_key_value_pot_index_count_mapper,
(void *)array_in, count, sizeof(struct mesh_key_value_pot),
threadpool_auto_chunk_size, &extra_data);
#ifdef SWIFT_DEBUG_CHECKS
size_t count_check = 0;
for (int i = 0; i < N; ++i) count_check += bucket_counts[i];
if (count_check != count) error("Bucket count is not matching");
#endif
/* Now we can build the array of offsets (cumsum of the counts) */
bucket_offsets[0] = 0;
for (int i = 1; i < N; ++i) {
bucket_offsets[i] = bucket_offsets[i - 1] + bucket_counts[i - 1];
}
/* Remind the compiler that the array is nicely aligned */
swift_declare_aligned_ptr(const struct mesh_key_value_pot, array_in_aligned,
array_in, SWIFT_CACHE_ALIGNMENT);
swift_declare_aligned_ptr(struct mesh_key_value_pot, array_out_aligned,
array_out, SWIFT_CACHE_ALIGNMENT);
/* Now, we can do the actual sorting */
for (size_t i = 0; i < count; ++i) {
const size_t key = array_in_aligned[i].cell_index;
const int cell_id = cell_index_extract_patch_index(key);
/* Where does this element land? */
const size_t index = bucket_offsets[cell_id];
/* Copy the element to its correct position */
memcpy(&array_out_aligned[index], &array_in_aligned[i],
sizeof(struct mesh_key_value_pot));
/* Move the start of this bucket by one */
bucket_offsets[cell_id]++;
}
/* Clean up! */
free(bucket_offsets);
free(bucket_counts);
}