Skip to content

Commit cc4a3ab

Browse files
committed
Benchmark parallel-letter-frequency. Closes exercism#244
1 parent 3bf514e commit cc4a3ab

File tree

4 files changed

+146
-2
lines changed

4 files changed

+146
-2
lines changed

.travis.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ env:
1313
- DENYWARNINGS=
1414
- DENYWARNINGS=1
1515
matrix:
16+
include:
17+
- rust: nightly
18+
env: BENCHMARK=1
19+
script: "./_test/check-exercises.sh"
1620
allow_failures:
1721
- rust: nightly
1822
- rust: beta

_test/check-exercises.sh

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,20 @@ if [ -z "$DENYWARNINGS" ]; then
66
set -e
77
fi
88

9+
if [ -n "$BENCHMARK" ]; then
10+
files=exercises/*/benches
11+
else
12+
files=exercises/*/tests
13+
fi
14+
915
tmp=${TMPDIR:-/tmp/}
1016
mkdir "${tmp}exercises"
1117

1218
exitcode=0
1319

1420
# An exercise worth testing is defined here as any top level directory with
1521
# a 'tests' directory
16-
for exercise in exercises/*/tests; do
22+
for exercise in $files; do
1723
# This assumes that exercises are only one directory deep
1824
# and that the primary module is named the same as the directory
1925
directory=$(dirname "${exercise}");
@@ -38,7 +44,10 @@ for exercise in exercises/*/tests; do
3844
sed -i '/\[ignore\]/d' $test
3945
done
4046

41-
if [ -n "$DENYWARNINGS" ]; then
47+
# Run benchmarks in instead when enabled.
48+
if [ -n "$BENCHMARK" ]; then
49+
cargo bench
50+
elif [ -n "$DENYWARNINGS" ]; then
4251
sed -i -e '1i #![deny(warnings)]' src/lib.rs
4352

4453
# No-run mode so we see no test output.
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Parallel Letter Frequency in Rust
2+
3+
Learn more about concurrency in Rust here:
4+
5+
- [Concurrency](https://doc.rust-lang.org/book/concurrency.html)
6+
7+
## Bonus
8+
9+
This exercise also includes a benchmark, with a sequential implementation as a
10+
baseline. You can compare your solution to the benchmark. Observe the
11+
effect different size inputs have on the performance of each. Can you
12+
surpass the benchmark using concurrent programming techniques?
13+
14+
As of this writing, test::Bencher is unstable and only available on
15+
*nightly* Rust. Run the benchmarks with Cargo:
16+
17+
```
18+
cargo bench
19+
```
20+
21+
If you are using rustup.rs:
22+
23+
```
24+
rustup run nightly cargo bench
25+
```
26+
27+
- [Benchmark tests](https://doc.rust-lang.org/book/benchmark-tests.html)
28+
29+
Learn more about nightly Rust:
30+
31+
- [Nightly Rust](https://doc.rust-lang.org/book/nightly-rust.html)
32+
- [Rustup: Working with nightly](https://github.com/rust-lang-nursery/rustup.rs#working-with-nightly-rust)
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
#![feature(test)]
2+
extern crate parallel_letter_frequency;
3+
extern crate test;
4+
5+
use std::collections::HashMap;
6+
use test::Bencher;
7+
8+
#[bench]
9+
fn bench_tiny_parallel(b: &mut Bencher) {
10+
let tiny = &["a"];
11+
b.iter(|| parallel_letter_frequency::frequency(tiny, 3));
12+
}
13+
14+
#[bench]
15+
fn bench_tiny_sequential(b: &mut Bencher) {
16+
let tiny = &["a"];
17+
b.iter(|| frequency(tiny));
18+
}
19+
20+
#[bench]
21+
fn bench_small_parallel(b: &mut Bencher) {
22+
let texts = all_texts(1);
23+
b.iter(|| parallel_letter_frequency::frequency(&texts, 3));
24+
}
25+
26+
#[bench]
27+
fn bench_small_sequential(b: &mut Bencher) {
28+
let texts = all_texts(1);
29+
b.iter(|| frequency(&texts));
30+
}
31+
32+
#[bench]
33+
fn bench_large_parallel(b: &mut Bencher) {
34+
let texts = all_texts(30);
35+
b.iter(|| parallel_letter_frequency::frequency(&texts, 3));
36+
}
37+
38+
#[bench]
39+
fn bench_large_sequential(b: &mut Bencher) {
40+
let texts = all_texts(30);
41+
b.iter(|| frequency(&texts));
42+
}
43+
44+
/// Simple sequential char frequency. Can it be beat?
45+
pub fn frequency(texts: &[&str]) -> HashMap<char, usize> {
46+
let mut map = HashMap::new();
47+
48+
for line in texts {
49+
for chr in line.chars().filter(|c| c.is_alphabetic()) {
50+
if let Some(c) = chr.to_lowercase().next() {
51+
(*map.entry(c).or_insert(0)) += 1;
52+
}
53+
}
54+
}
55+
56+
map
57+
}
58+
59+
fn all_texts(repeat: usize) -> Vec<&'static str> {
60+
[ODE_AN_DIE_FREUDE, WILHELMUS, STAR_SPANGLED_BANNER]
61+
.iter()
62+
.cycle()
63+
.take(3 * repeat)
64+
.flat_map(|anthem| anthem.iter().cloned())
65+
.collect()
66+
}
67+
68+
// Poem by Friedrich Schiller. The corresponding music is the European Anthem.
69+
pub const ODE_AN_DIE_FREUDE: [&'static str; 8] = [
70+
"Freude schöner Götterfunken",
71+
"Tochter aus Elysium,",
72+
"Wir betreten feuertrunken,",
73+
"Himmlische, dein Heiligtum!",
74+
"Deine Zauber binden wieder",
75+
"Was die Mode streng geteilt;",
76+
"Alle Menschen werden Brüder,",
77+
"Wo dein sanfter Flügel weilt."];
78+
79+
// Dutch national anthem
80+
pub const WILHELMUS: [&'static str; 8] = [
81+
"Wilhelmus van Nassouwe",
82+
"ben ik, van Duitsen bloed,",
83+
"den vaderland getrouwe",
84+
"blijf ik tot in den dood.",
85+
"Een Prinse van Oranje",
86+
"ben ik, vrij, onverveerd,",
87+
"den Koning van Hispanje",
88+
"heb ik altijd geëerd."];
89+
90+
// American national anthem
91+
pub const STAR_SPANGLED_BANNER: [&'static str; 8] = [
92+
"O say can you see by the dawn's early light,",
93+
"What so proudly we hailed at the twilight's last gleaming,",
94+
"Whose broad stripes and bright stars through the perilous fight,",
95+
"O'er the ramparts we watched, were so gallantly streaming?",
96+
"And the rockets' red glare, the bombs bursting in air,",
97+
"Gave proof through the night that our flag was still there;",
98+
"O say does that star-spangled banner yet wave,",
99+
"O'er the land of the free and the home of the brave?"];

0 commit comments

Comments
 (0)