Skip to content

Commit 1dbcaac

Browse files
ahanssiebenschlaefervaeng
authored
Add parallel-letter-frequency (#800)
Co-authored-by: Matthias <[email protected]> Co-authored-by: Christian Willner <[email protected]>
1 parent d39a65a commit 1dbcaac

File tree

13 files changed

+18687
-0
lines changed

13 files changed

+18687
-0
lines changed

config.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1169,6 +1169,14 @@
11691169
"prerequisites": [],
11701170
"difficulty": 5
11711171
},
1172+
{
1173+
"slug": "parallel-letter-frequency",
1174+
"name": "Parallel Letter Frequency",
1175+
"uuid": "0a029212-bc55-4559-b3c6-2c26bec0d560",
1176+
"practices": [],
1177+
"prerequisites": [],
1178+
"difficulty": 6
1179+
},
11721180
{
11731181
"slug": "pig-latin",
11741182
"name": "Pig Latin",
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# Instructions append
2+
3+
## Additional Notes for C++ Implementation
4+
5+
There are several ways how to achieve parallelism in C++.
6+
Exercism's online runner supports C++17, so you can use execution policies from the [algorithms library][algorithm].
7+
Another option is manually managing [threads][thread].
8+
However, note that spawning a thread is quite expensive (using a thread [pool][pool] would help).
9+
10+
When working locally, you can of course use whatever you want.
11+
However, a solution using non-standard libraries will most probably not work with the Exercism online runner.
12+
13+
### Benchmark
14+
15+
When working locally, you can optionally run a benchmark to get an idea about the speedup of different implementations.
16+
Activating the CMake flag `EXERCISM_INCLUDE_BENCHMARK` enables the benchmark:
17+
```
18+
cmake -DEXERCISM_RUN_ALL_TESTS=1 -DEXERCISM_INCLUDE_BENCHMARK=1 .
19+
```
20+
21+
### Compiler support for parallel algorithms
22+
23+
GCC's implementation of the C++ standard library (`libstdc++`) relies on [TBB][tbb].
24+
If TBB is not available, a fall back to a sequential version will be used, even when parallel execution is requested.
25+
26+
On Ubuntu, you need to install the `libtbb-dev` package:
27+
```
28+
apt-get install libtbb-dev
29+
```
30+
31+
On macOS, you can use [Homebrew][homebrew] to install TBB:
32+
```
33+
brew install tbb
34+
```
35+
36+
Clang `libc++` as of version 17 has experimental, partial support for parallel algorithms.
37+
To switch it on, the `-fexperimental-library` compiler flags needs to be given.
38+
39+
Apple Clang 15 and earlier _do not_ support parallel algorithms.
40+
41+
On Linux and macOS we recommend using GCC (along with the default `libstdc++`) for this exercise.
42+
43+
Microsoft's MSVC supports parallel algorithms at least since VS 2017 15.7 without having to install any additional library.
44+
45+
[algorithm]: https://en.cppreference.com/w/cpp/algorithm
46+
[thread]: https://en.cppreference.com/w/cpp/thread/thread
47+
[pool]: https://en.wikipedia.org/wiki/Thread_pool
48+
[tbb]: https://en.wikipedia.org/wiki/Threading_Building_Blocks
49+
[homebrew]: https://brew.sh/
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Instructions
2+
3+
Count the frequency of letters in texts using parallel computation.
4+
5+
Parallelism is about doing things in parallel that can also be done sequentially.
6+
A common example is counting the frequency of letters.
7+
Create a function that returns the total frequency of each letter in a list of texts and that employs parallelism.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"authors": [
3+
"ahans"
4+
],
5+
"files": {
6+
"solution": [
7+
"parallel_letter_frequency.cpp",
8+
"parallel_letter_frequency.h"
9+
],
10+
"test": [
11+
"parallel_letter_frequency_test.cpp"
12+
],
13+
"example": [
14+
".meta/example.cpp",
15+
".meta/example.h"
16+
]
17+
},
18+
"blurb": "Count the frequency of letters in texts using parallel computation."
19+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#include "parallel_letter_frequency.h"
2+
3+
#include <algorithm>
4+
#include <cctype>
5+
6+
// Apple Clang does not support C++17 parallel algorithms.
7+
#ifdef __apple_build_version__
8+
#define PAR_UNSEQ
9+
#else
10+
#include <execution>
11+
#define PAR_UNSEQ std::execution::par_unseq,
12+
#endif
13+
14+
namespace parallel_letter_frequency {
15+
16+
namespace {
17+
18+
[[nodiscard]] std::unordered_map<char, size_t> frequency(
19+
std::string_view const text) {
20+
std::unordered_map<char, size_t> freq;
21+
for (unsigned char c : text) freq[std::tolower(c)] += 1;
22+
return freq;
23+
}
24+
25+
} // namespace
26+
27+
std::unordered_map<char, size_t> frequency(
28+
std::vector<std::string_view> const& texts) {
29+
std::vector<std::unordered_map<char, size_t>> freqs(texts.size());
30+
// determine frequencies of texts in parallel
31+
std::transform(PAR_UNSEQ texts.cbegin(), texts.cend(), freqs.begin(),
32+
[](auto text) { return frequency(text); });
33+
34+
// combine individual frequencies (this happens sequentially)
35+
std::unordered_map<char, size_t> totals{};
36+
for (auto const& single_text_freqs : freqs) {
37+
for (auto const [c, count] : single_text_freqs) {
38+
totals[c] += count;
39+
}
40+
}
41+
42+
return totals;
43+
}
44+
45+
} // namespace parallel_letter_frequency
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#if !defined(PARALLEL_LETTER_FREQUENCY_H)
2+
#define PARALLEL_LETTER_FREQUENCY_H
3+
4+
#include <string_view>
5+
#include <unordered_map>
6+
#include <vector>
7+
8+
namespace parallel_letter_frequency {
9+
10+
[[nodiscard]] std::unordered_map<char, size_t> frequency(
11+
std::vector<std::string_view> const& texts);
12+
13+
}
14+
15+
#endif
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# This is an auto-generated file.
2+
#
3+
# Regenerating this file via `configlet sync` will:
4+
# - Recreate every `description` key/value pair
5+
# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
6+
# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
7+
# - Preserve any other key/value pair
8+
#
9+
# As user-added comments (using the # character) will be removed when this file
10+
# is regenerated, comments can be added via a `comment` key.
11+
12+
[c054d642-c1fa-4234-8007-9339f2337886]
13+
description = "no texts"
14+
15+
[818031be-49dc-4675-b2f9-c4047f638a2a]
16+
description = "one text with one letter"
17+
18+
[c0b81d1b-940d-4cea-9f49-8445c69c17ae]
19+
description = "one text with multiple letters"
20+
21+
[708ff1e0-f14a-43fd-adb5-e76750dcf108]
22+
description = "two texts with one letter"
23+
24+
[1b5c28bb-4619-4c9d-8db9-a4bb9c3bdca0]
25+
description = "two texts with multiple letters"
26+
27+
[6366e2b8-b84c-4334-a047-03a00a656d63]
28+
description = "ignore letter casing"
29+
30+
[92ebcbb0-9181-4421-a784-f6f5aa79f75b]
31+
description = "ignore whitespace"
32+
33+
[bc5f4203-00ce-4acc-a5fa-f7b865376fd9]
34+
description = "ignore punctuation"
35+
36+
[68032b8b-346b-4389-a380-e397618f6831]
37+
description = "ignore numbers"
38+
39+
[aa9f97ac-3961-4af1-88e7-6efed1bfddfd]
40+
description = "Unicode letters"
41+
include = false
42+
43+
[7b1da046-701b-41fc-813e-dcfb5ee51813]
44+
description = "combination of lower- and uppercase letters, punctuation and white space"
45+
46+
[4727f020-df62-4dcf-99b2-a6e58319cb4f]
47+
description = "large texts"
48+
49+
[adf8e57b-8e54-4483-b6b8-8b32c115884c]
50+
description = "many small texts"
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# Get the exercise name from the current directory
2+
get_filename_component(exercise ${CMAKE_CURRENT_SOURCE_DIR} NAME)
3+
4+
# Basic CMake project
5+
cmake_minimum_required(VERSION 3.5.1)
6+
7+
# Name the project after the exercise
8+
project(${exercise} CXX)
9+
10+
# Get a source filename from the exercise name by replacing -'s with _'s
11+
string(REPLACE "-" "_" file ${exercise})
12+
13+
# Implementation could be only a header
14+
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${file}.cpp)
15+
set(exercise_cpp ${file}.cpp)
16+
else()
17+
set(exercise_cpp "")
18+
endif()
19+
20+
# Use the common Catch library?
21+
if(EXERCISM_COMMON_CATCH)
22+
# For Exercism track development only
23+
add_executable(${exercise} ${file}_test.cpp ${exercise_cpp} ${file}.h $<TARGET_OBJECTS:catchlib>)
24+
elseif(EXERCISM_TEST_SUITE)
25+
# The Exercism test suite is being run, the Docker image already
26+
# includes a pre-built version of Catch.
27+
find_package(Catch2 REQUIRED)
28+
add_executable(${exercise} ${file}_test.cpp ${exercise_cpp} ${file}.h)
29+
target_link_libraries(${exercise} PRIVATE Catch2::Catch2WithMain)
30+
# When Catch is installed system wide we need to include a different
31+
# header, we need this define to use the correct one.
32+
target_compile_definitions(${exercise} PRIVATE EXERCISM_TEST_SUITE)
33+
else()
34+
# Build executable from sources and headers
35+
add_executable(${exercise} ${file}_test.cpp ${exercise_cpp} ${file}.h test/tests-main.cpp)
36+
endif()
37+
38+
set_target_properties(${exercise} PROPERTIES
39+
CXX_STANDARD 17
40+
CXX_STANDARD_REQUIRED OFF
41+
CXX_EXTENSIONS OFF
42+
)
43+
44+
set(CMAKE_BUILD_TYPE Release)
45+
46+
if("${CMAKE_CXX_COMPILER_ID}" MATCHES "(GNU|Clang)")
47+
find_package(TBB)
48+
if (${TBB_FOUND})
49+
target_link_libraries(${exercise} PRIVATE TBB::tbb)
50+
endif()
51+
set_target_properties(${exercise} PROPERTIES
52+
COMPILE_FLAGS "-Wall -Wextra -Wpedantic -Werror"
53+
)
54+
endif()
55+
56+
if(${EXERCISM_INCLUDE_BENCHMARK})
57+
target_compile_definitions(${exercise} PRIVATE EXERCISM_INCLUDE_BENCHMARK CATCH_CONFIG_ENABLE_BENCHMARKING)
58+
endif()
59+
60+
# Configure to run all the tests?
61+
if(${EXERCISM_RUN_ALL_TESTS})
62+
target_compile_definitions(${exercise} PRIVATE EXERCISM_RUN_ALL_TESTS)
63+
endif()
64+
65+
# Tell MSVC not to warn us about unchecked iterators in debug builds
66+
if(${MSVC})
67+
set_target_properties(${exercise} PROPERTIES
68+
COMPILE_DEFINITIONS_DEBUG _SCL_SECURE_NO_WARNINGS)
69+
endif()
70+
71+
# Run the tests on every build
72+
add_custom_target(test_${exercise} ALL DEPENDS ${exercise} COMMAND ${exercise})
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#include "parallel_letter_frequency.h"
2+
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#if !defined(PARALLEL_LETTER_FREQUENCY_H)
2+
#define PARALLEL_LETTER_FREQUENCY_H
3+
4+
namespace parallel_letter_frequency {
5+
6+
}
7+
8+
#endif
9+

0 commit comments

Comments
 (0)