Skip to content

Commit d94fe30

Browse files
committed
turned inefficient sorting routines generic. Ranks are now of type usize, because that is the indexing type anyway
1 parent 101c54a commit d94fe30

File tree

4 files changed

+54
-25
lines changed

4 files changed

+54
-25
lines changed

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ rust-version = "1.57"
99
[dependencies]
1010
mpi = { version = "0.6", features = ["user-operations", "derive"], git = "https://github.com/Cydhra/rsmpi", branch="sized_process" }
1111
rand = "0.8.5"
12+
num = "0.4"
1213

1314
[dev-dependencies]
1415
rusty-fork = "0.3.0"

README.md

+4-1
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,13 @@ or by confusing data types.
2222
#### Inefficient Ranking and Sorting
2323
A few inefficient routines for ranking and sorting are provided,
2424
which are reused in more efficient algorithms as the base case for recursions.
25+
The implementations (short of `matrix_rank` for now) are generic;
26+
the ranking variants return `usize`, because that is the indexing type which makes sense for ranks.
2527
* `inefficient_sort` sends all data to one processor, which will sort and redistribute it. It is the most
2628
inefficient and slowest algorithm, but works in all cases
2729
* `inefficient_rank` sends all data to one processor, which will calculate ranks and return them to the original
28-
processors. Otherwise it is the same design as `inefficient_sort`
30+
processors.
31+
Otherwise, it is the same design as `inefficient_sort`
2932
* `matrix_rank` requires a square number of processors, but is theoretically more efficient than alternatives
3033

3134
#### Sample Sort

src/inefficient_sort.rs

+48-23
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
use mpi::collective::SystemOperation;
21
use std::borrow::Borrow;
32

3+
use mpi::collective::SystemOperation;
44
use mpi::datatype::{Partition, PartitionMut};
55
use mpi::traits::*;
66
use mpi::Rank;
7+
use num::Zero;
78

89
/// A very inefficient sorting algorithm that just gathers all data, sorts it locally and
910
/// re-distributes it. This is technically worse than the theoretical alternative and matrix-sort
@@ -15,10 +16,15 @@ use mpi::Rank;
1516
/// - `comm` mpi communicator
1617
/// - `data` partial data of this process. Must not be very much and must be of equal size on all
1718
/// processes. The buffer will be overwritten with the sorted data
18-
pub fn inefficient_sort(comm: &dyn Communicator, data: &mut [u64]) {
19+
pub fn inefficient_sort<T>(comm: &dyn Communicator, data: &mut [T])
20+
where
21+
T: Clone + Ord + Zero,
22+
[T]: Buffer + BufferMut,
23+
Vec<T>: Buffer + BufferMut,
24+
{
1925
let rank = comm.rank() as usize;
2026
let world_size = comm.size() as usize;
21-
let mut recv_buffer = vec![0u64; data.len() * world_size];
27+
let mut recv_buffer = vec![T::zero(); data.len() * world_size];
2228
let root = comm.process_at_rank(0);
2329

2430
if rank == 0 {
@@ -42,7 +48,12 @@ pub fn inefficient_sort(comm: &dyn Communicator, data: &mut [u64]) {
4248
///
4349
/// # Returns
4450
/// A vector containing the p-th part of the sorted data, where p is the count of processes
45-
pub fn inefficient_sort_var(comm: &dyn Communicator, data: &[u64]) -> Vec<u64> {
51+
pub fn inefficient_sort_var<T>(comm: &dyn Communicator, data: &[T]) -> Vec<T>
52+
where
53+
T: Clone + Ord + Zero,
54+
[T]: Buffer + BufferMut,
55+
Vec<T>: Buffer + BufferMut,
56+
{
4657
let rank = comm.rank() as usize;
4758
let world_size = comm.size() as usize;
4859

@@ -59,7 +70,7 @@ pub fn inefficient_sort_var(comm: &dyn Communicator, data: &[u64]) -> Vec<u64> {
5970
}
6071

6172
let mut recv_buffer =
62-
vec![0u64; (displs[world_size - 1] + counts[world_size - 1]) as usize];
73+
vec![T::zero(); (displs[world_size - 1] + counts[world_size - 1]) as usize];
6374
let mut partition = PartitionMut::new(&mut recv_buffer, counts.borrow(), displs.borrow());
6475
comm.this_process()
6576
.gather_varcount_into_root(data, &mut partition);
@@ -82,7 +93,7 @@ pub fn inefficient_sort_var(comm: &dyn Communicator, data: &[u64]) -> Vec<u64> {
8293
let mut data_size: i32 = 0;
8394
comm.this_process()
8495
.scatter_into_root(&counts, &mut data_size);
85-
let mut data = vec![0u64; data_size as usize];
96+
let mut data = vec![T::zero(); data_size as usize];
8697
comm.this_process()
8798
.scatter_varcount_into_root(&partition, &mut data);
8899
data
@@ -92,7 +103,7 @@ pub fn inefficient_sort_var(comm: &dyn Communicator, data: &[u64]) -> Vec<u64> {
92103

93104
let mut data_size: i32 = 0;
94105
comm.process_at_rank(0).scatter_into(&mut data_size);
95-
let mut data = vec![0u64; data_size as usize];
106+
let mut data = vec![T::zero(); data_size as usize];
96107
comm.process_at_rank(0).scatter_varcount_into(&mut data);
97108
data
98109
}
@@ -101,16 +112,19 @@ pub fn inefficient_sort_var(comm: &dyn Communicator, data: &[u64]) -> Vec<u64> {
101112
/// Rank data locally with stable tie breaking. The resulting ranks are a permutation of the series ``0..n``. The
102113
/// ranking requires time ``O(n*log(n))``. The resulting vector contains the ranks in the order of elements in ``data``,
103114
/// with ties broken in order of appearance.
104-
fn local_rank(data: &[u64]) -> Vec<u64> {
115+
fn local_rank<T>(data: &[T]) -> Vec<usize>
116+
where
117+
T: Clone + Ord,
118+
{
105119
let mut sorted_data = data.to_vec();
106120
let mut ranking = Vec::with_capacity(data.len());
107121
sorted_data.sort_unstable();
108122

109123
let mut tie_breaker = vec![0usize; data.len()];
110124

111125
for i in 0..data.len() {
112-
let partition = sorted_data.partition_point(|&x| x < data[i]);
113-
ranking.push((partition + tie_breaker[partition]) as u64);
126+
let partition = sorted_data.partition_point(|x| x.clone() < data[i]);
127+
ranking.push(partition + tie_breaker[partition]);
114128
tie_breaker[partition] += 1;
115129
}
116130

@@ -125,15 +139,20 @@ fn local_rank(data: &[u64]) -> Vec<u64> {
125139
/// - `comm` mpi communicator
126140
/// - `data` partial data of this process
127141
/// - `ranking` output parameter for the ranking, must be of equal size as the data slice
128-
pub fn inefficient_rank(comm: &dyn Communicator, data: &[u64], ranking: &mut [u64]) {
142+
pub fn inefficient_rank<T>(comm: &dyn Communicator, data: &[T], ranking: &mut [usize])
143+
where
144+
T: Clone + Ord + Zero,
145+
[T]: Buffer + BufferMut,
146+
Vec<T>: Buffer + BufferMut,
147+
{
129148
assert_eq!(data.len(), ranking.len());
130149

131150
let rank = comm.rank() as usize;
132151
let world_size = comm.size() as usize;
133152
let root = comm.process_at_rank(0);
134153

135154
if rank == 0 {
136-
let mut recv_buffer = vec![0u64; data.len() * world_size];
155+
let mut recv_buffer = vec![T::zero(); data.len() * world_size];
137156
root.gather_into_root(data, &mut recv_buffer);
138157
let all_rankings = local_rank(&recv_buffer);
139158
root.scatter_into_root(&all_rankings, ranking);
@@ -150,7 +169,12 @@ pub fn inefficient_rank(comm: &dyn Communicator, data: &[u64], ranking: &mut [u6
150169
/// - `comm` mpi communicator
151170
/// - `data` partial data of this process
152171
/// - `ranking` output parameter for the ranking
153-
pub fn inefficient_rank_var(comm: &dyn Communicator, data: &[u64], ranking: &mut [u64]) {
172+
pub fn inefficient_rank_var<T>(comm: &dyn Communicator, data: &[T], ranking: &mut [usize])
173+
where
174+
T: Clone + Ord + Zero,
175+
[T]: Buffer + BufferMut,
176+
Vec<T>: Buffer + BufferMut,
177+
{
154178
assert_eq!(data.len(), ranking.len());
155179

156180
let world_size = comm.size() as usize;
@@ -168,7 +192,7 @@ pub fn inefficient_rank_var(comm: &dyn Communicator, data: &[u64], ranking: &mut
168192

169193
// collect data
170194
let mut all_data_vec =
171-
vec![0u64; (displs[displs.len() - 1] + counts[counts.len() - 1]) as usize];
195+
vec![T::zero(); (displs[displs.len() - 1] + counts[counts.len() - 1]) as usize];
172196
let mut all_data = PartitionMut::new(&mut all_data_vec, counts.borrow(), displs.borrow());
173197
root_process.gather_varcount_into_root(data, &mut all_data);
174198

@@ -281,10 +305,11 @@ pub fn matrix_rank(comm: &dyn Communicator, data: &[u64], ranks: &mut [u64]) {
281305
/// They are not exhaustive and only check for obvious regressions.
282306
#[cfg(test)]
283307
mod tests {
308+
use rusty_fork::rusty_fork_test;
309+
284310
use crate::{
285311
inefficient_rank, inefficient_rank_var, inefficient_sort, inefficient_sort_var, matrix_rank,
286312
};
287-
use rusty_fork::rusty_fork_test;
288313

289314
rusty_fork_test! {
290315
#[test]
@@ -323,15 +348,15 @@ mod tests {
323348
rusty_fork_test! {
324349
#[test]
325350
fn test_inefficient_rank() {
326-
let data = [234, 23, 4, 235, 24];
327-
let mut ranking = vec![0u64; data.len()];
351+
let data = [234u64, 23, 4, 235, 24];
352+
let mut ranking = vec![0usize; data.len()];
328353

329354
let universe = mpi::initialize().unwrap();
330355
let world = universe.world();
331356

332357
inefficient_rank(&world, &data, &mut ranking);
333358

334-
let expected = [3, 1, 0, 4, 2];
359+
let expected = [3usize, 1, 0, 4, 2];
335360
assert_eq!(expected.len(), ranking.len());
336361
expected
337362
.iter()
@@ -344,7 +369,7 @@ mod tests {
344369
#[test]
345370
fn test_inefficient_rank_var_with_ties() {
346371
let data = [1, 1, 4, 5, 24];
347-
let mut ranking = vec![0u64; data.len()];
372+
let mut ranking = vec![0usize; data.len()];
348373

349374
let universe = mpi::initialize().unwrap();
350375
let world = universe.world();
@@ -360,7 +385,7 @@ mod tests {
360385
#[test]
361386
fn test_inefficient_rank_var() {
362387
let data = [234, 23, 4, 235, 24];
363-
let mut ranking = vec![0u64; data.len()];
388+
let mut ranking = vec![0usize; data.len()];
364389

365390
let universe = mpi::initialize().unwrap();
366391
let world = universe.world();
@@ -379,7 +404,7 @@ mod tests {
379404
#[test]
380405
fn test_inefficient_rank_with_ties() {
381406
let data = [1, 1, 4, 5, 24];
382-
let mut ranking = vec![0u64; data.len()];
407+
let mut ranking = vec![0usize; data.len()];
383408

384409
let universe = mpi::initialize().unwrap();
385410
let world = universe.world();
@@ -394,15 +419,15 @@ mod tests {
394419
rusty_fork_test! {
395420
#[test]
396421
fn test_matrix_rank() {
397-
let data = [1, 2, 3, 4, 5, 6, 7, 8, 9];
422+
let data = [1u64, 2, 3, 4, 5, 6, 7, 8, 9];
398423
let mut ranking = vec![0u64; data.len()];
399424

400425
let universe = mpi::initialize().unwrap();
401426
let world = universe.world();
402427

403428
matrix_rank(&world, &data, &mut ranking);
404429

405-
let expected = [0, 1, 2, 3, 4, 5, 6, 7, 8];
430+
let expected = [0u64, 1, 2, 3, 4, 5, 6, 7, 8];
406431
assert_eq!(expected.len(), ranking.len());
407432
expected
408433
.iter()

src/selection.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ pub fn parallel_select_k(comm: &dyn Communicator, data: &[u64], k: usize) -> Vec
9595
return data
9696
.iter()
9797
.zip(ranking.iter())
98-
.filter(|(_, rank)| **rank < k as u64)
98+
.filter(|(_, &rank)| rank < k as usize)
9999
.map(|(itm, _)| *itm)
100100
.collect();
101101
}

0 commit comments

Comments
 (0)