Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add parallel letter frequency #1638

Merged
merged 8 commits into from
Mar 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -765,6 +765,14 @@
],
"difficulty": 6
},
{
"slug": "parallel-letter-frequency",
"name": "Parallel Letter Frequency",
"uuid": "527fdec4-a24e-491d-8f2d-7496054b6e88",
"practices": [],
"prerequisites": [],
"difficulty": 6
},
{
"slug": "accumulate",
"name": "Accumulate",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Instructions Append

## Benchmarking

If you are solving this exercise locally, there is an included `parallel_letter_frequency_benchmark_test.rb` file which will compare your parallel solution to a sequential one.
You can run it via `ruby parallel_letter_frequency_benchmark_test.rb`.
The output will show execution times of each implementation over a range of large text counts in a tabular format.
Feel free to change the test cases.
You may want to investigate what performance impact length of text vs number of texts has on the execution time for each implementation.

**Note:** For smaller sets of text, the sequential count _may_ be faster due to processing overhead costs.

## Further Reading
ccadden marked this conversation as resolved.
Show resolved Hide resolved

- [Ruby `Thread` Documentation](https://docs.ruby-lang.org/en/master/Thread.html)
- [Ruby `Thread::Queue` Documentation](https://docs.ruby-lang.org/en/master/Thread/Queue.html)
- [Ruby `Fiber` Documentation](https://docs.ruby-lang.org/en/master/Fiber.html)
- [Ruby `Ractor` Documentation](https://docs.ruby-lang.org/en/master/Ractor.html)
- [`Minitest::Benchmark` Documentation](https://ruby-doc.org/3.0.6/gems/minitest/Minitest/Benchmark.html)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Instructions

Count the frequency of letters in texts using parallel computation.

Parallelism is about doing things in parallel that can also be done sequentially.
A common example is counting the frequency of letters.
Create a function that returns the total frequency of each letter in a list of texts and that employs parallelism.
kotp marked this conversation as resolved.
Show resolved Hide resolved
18 changes: 18 additions & 0 deletions exercises/practice/parallel-letter-frequency/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"authors": [
"mr-sigma",
"kotp"
],
"files": {
"solution": [
"parallel_letter_frequency.rb"
],
"test": [
"parallel_letter_frequency_test.rb"
],
"example": [
".meta/example.rb"
]
},
"blurb": "Count the frequency of letters in texts using parallel computation."
}
23 changes: 23 additions & 0 deletions exercises/practice/parallel-letter-frequency/.meta/example.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
class ParallelLetterFrequency
def self.count(texts)
ractors = (0...texts.length).map do |i|
Ractor.new(texts[i]) do |text|
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is Ractor a gem that is not installed by default?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ractor may be a gem, but it is included in the core/standard libraries.

text.downcase.each_grapheme_cluster.select do |cluster|
cluster.match?(/\p{Alpha}/)
end.tally
end
end

tally = Hash.new(0)

until ractors.empty?
ractor, result = Ractor.select(*ractors)
ractors.delete ractor
result.each do |key, value|
tally[key] += value
end
end

tally
end
end
49 changes: 49 additions & 0 deletions exercises/practice/parallel-letter-frequency/.meta/tests.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# This is an auto-generated file.
#
# Regenerating this file via `configlet sync` will:
# - Recreate every `description` key/value pair
# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
# - Preserve any other key/value pair
#
# As user-added comments (using the # character) will be removed when this file
# is regenerated, comments can be added via a `comment` key.

[c054d642-c1fa-4234-8007-9339f2337886]
description = "no texts"

[818031be-49dc-4675-b2f9-c4047f638a2a]
description = "one text with one letter"

[c0b81d1b-940d-4cea-9f49-8445c69c17ae]
description = "one text with multiple letters"

[708ff1e0-f14a-43fd-adb5-e76750dcf108]
description = "two texts with one letter"

[1b5c28bb-4619-4c9d-8db9-a4bb9c3bdca0]
description = "two texts with multiple letters"

[6366e2b8-b84c-4334-a047-03a00a656d63]
description = "ignore letter casing"

[92ebcbb0-9181-4421-a784-f6f5aa79f75b]
description = "ignore whitespace"

[bc5f4203-00ce-4acc-a5fa-f7b865376fd9]
description = "ignore punctuation"

[68032b8b-346b-4389-a380-e397618f6831]
description = "ignore numbers"

[aa9f97ac-3961-4af1-88e7-6efed1bfddfd]
description = "Unicode letters"

[7b1da046-701b-41fc-813e-dcfb5ee51813]
description = "combination of lower- and uppercase letters, punctuation and white space"

[4727f020-df62-4dcf-99b2-a6e58319cb4f]
description = "large texts"

[adf8e57b-8e54-4483-b6b8-8b32c115884c]
description = "many small texts"
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
The cabin; by the stern windows; Ahab sitting alone, and gazing out.

I leave a white and turbid wake; pale waters, paler cheeks, where’er I sail. The envious billows sidelong swell to whelm my track; let them; but first I pass.

Yonder, by ever-brimming goblet’s rim, the warm waves blush like wine. The gold brow plumbs the blue. The diver sun—slow dived from noon—goes down; my soul mounts up! she wearies with her endless hill. Is, then, the crown too heavy that I wear? this Iron Crown of Lombardy. Yet is it bright with many a gem; I the wearer, see not its far flashings; but darkly feel that I wear that, that dazzlingly confounds. ’Tis iron—that I know—not gold. ’Tis split, too—that I feel; the jagged edge galls me so, my brain seems to beat against the solid metal; aye, steel skull, mine; the sort that needs no helmet in the most brain-battering fight!

Dry heat upon my brow? Oh! time was, when as the sunrise nobly spurred me, so the sunset soothed. No more. This lovely light, it lights not me; all loveliness is anguish to me, since I can ne’er enjoy. Gifted with the high perception, I lack the low, enjoying power; damned, most subtly and most malignantly! damned in the midst of Paradise! Good night—good night! (waving his hand, he moves from the window.)

’Twas not so hard a task. I thought to find one stubborn, at the least; but my one cogged circle fits into all their various wheels, and they revolve. Or, if you will, like so many ant-hills of powder, they all stand before me; and I their match. Oh, hard! that to fire others, the match itself must needs be wasting! What I’ve dared, I’ve willed; and what I’ve willed, I’ll do! They think me mad—Starbuck does; but I’m demoniac, I am madness maddened! That wild madness that’s only calm to comprehend itself! The prophecy was that I should be dismembered; and—Aye! I lost this leg. I now prophesy that I will dismember my dismemberer. Now, then, be the prophet and the fulfiller one. That’s more than ye, ye great gods, ever were. I laugh and hoot at ye, ye cricket-players, ye pugilists, ye deaf Burkes and blinded Bendigoes! I will not say as schoolboys do to bullies—Take some one of your own size; don’t pommel me! No, ye’ve knocked me down, and I am up again; but ye have run and hidden. Come forth from behind your cotton bags! I have no long gun to reach ye. Come, Ahab’s compliments to ye; come and see if ye can swerve me. Swerve me? ye cannot swerve me, else ye swerve yourselves! man has ye there. Swerve me? The path to my fixed purpose is laid with iron rails, whereon my soul is grooved to run. Over unsounded gorges, through the rifled hearts of mountains, under torrents’ beds, unerringly I rush! Naught’s an obstacle, naught’s an angle to the iron way!
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
By the Mainmast; Starbuck leaning against it.

My soul is more than matched; she’s overmanned; and by a madman! Insufferable sting, that sanity should ground arms on such a field! But he drilled deep down, and blasted all my reason out of me! I think I see his impious end; but feel that I must help him to it. Will I, nill I, the ineffable thing has tied me to him; tows me with a cable I have no knife to cut. Horrible old man! Who’s over him, he cries;—aye, he would be a democrat to all above; look, how he lords it over all below! Oh! I plainly see my miserable office,—to obey, rebelling; and worse yet, to hate with touch of pity! For in his eyes I read some lurid woe would shrivel me up, had I it. Yet is there hope. Time and tide flow wide. The hated whale has the round watery world to swim in, as the small gold-fish has its glassy globe. His heaven-insulting purpose, God may wedge aside. I would up heart, were it not like lead. But my whole clock’s run down; my heart the all-controlling weight, I have no key to lift again.

[A burst of revelry from the forecastle.]

Oh, God! to sail with such a heathen crew that have small touch of human mothers in them! Whelped somewhere by the sharkish sea. The white whale is their demigorgon. Hark! the infernal orgies! that revelry is forward! mark the unfaltering silence aft! Methinks it pictures life. Foremost through the sparkling sea shoots on the gay, embattled, bantering bow, but only to drag dark Ahab after it, where he broods within his sternward cabin, builded over the dead water of the wake, and further on, hunted by its wolfish gurglings. The long howl thrills me through! Peace! ye revellers, and set the watch! Oh, life! ’tis in an hour like this, with soul beat down and held to knowledge,—as wild, untutored things are forced to feed—Oh, life! ’tis now that I do feel the latent horror in thee! but ’tis not me! that horror’s out of me! and with the soft feeling of the human in me, yet will I try to fight ye, ye grim, phantom futures! Stand by me, hold me, bind me, O ye blessed influences!
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
Fore-Top.

(Stubb solus, and mending a brace.)

Ha! ha! ha! ha! hem! clear my throat!—I’ve been thinking over it ever since, and that ha, ha’s the final consequence. Why so? Because a laugh’s the wisest, easiest answer to all that’s queer; and come what will, one comfort’s always left—that unfailing comfort is, it’s all predestinated. I heard not all his talk with Starbuck; but to my poor eye Starbuck then looked something as I the other evening felt. Be sure the old Mogul has fixed him, too. I twigged it, knew it; had had the gift, might readily have prophesied it—for when I clapped my eye upon his skull I saw it. Well, Stubb, wise Stubb—that’s my title—well, Stubb, what of it, Stubb? Here’s a carcase. I know not all that may be coming, but be it what it will, I’ll go to it laughing. Such a waggish leering as lurks in all your horribles! I feel funny. Fa, la! lirra, skirra! What’s my juicy little pear at home doing now? Crying its eyes out?—Giving a party to the last arrived harpooneers, I dare say, gay as a frigate’s pennant, and so am I—fa, la! lirra, skirra! Oh—

We’ll drink to-night with hearts as light,
To love, as gay and fleeting
As bubbles that swim, on the beaker’s brim,
And break on the lips while meeting.

A brave stave that—who calls? Mr. Starbuck? Aye, aye, sir—(Aside) he’s my superior, he has his too, if I’m not mistaken.—Aye, aye, sir, just through with this job—coming.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Ruby is an interpreted, high-level, general-purpose programming language which supports multiple programming paradigms. It was designed with an emphasis on programming productivity and simplicity. In Ruby, everything is an object, including primitive data types. It was developed in the mid-1990s by Yukihiro "Matz" Matsumoto in Japan.

Ruby is dynamically typed and uses garbage collection and just-in-time compilation. It supports multiple programming paradigms, including procedural, object-oriented, and functional programming. According to the creator, Ruby was influenced by Perl, Smalltalk, Eiffel, Ada, BASIC, Java, and Lisp.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
=begin
Write your code for the 'Parallel Letter Frequency' exercise in this file. Make
the tests in `parallel_letter_frequency_test.rb` pass.

To get started with TDD, see the `README.md` file in your
`ruby/parallel_letter_frequency` directory.
=end
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
require 'minitest/autorun'
require 'minitest/benchmark'
require_relative 'parallel_letter_frequency'

class ParallelLetterFrequencyBenchmarkTest < Minitest::Benchmark
def self.bench_range
bench_exp(1, 100_000)
end

def setup
@strings = {}
self.class.bench_range.each do |n|
@strings[n] = Array.new(10, 'a' * n)
end
end

def bench_sequential
assert_performance ->(_, _) { true } do |n|
sequential_letter_frequency(@strings[n])
end
end

def bench_parallel
assert_performance ->(_, _) { true } do |n|
ParallelLetterFrequency.count(@strings[n])
end
end

def sequential_letter_frequency(texts)
tally = Hash.new(0)
texts.each do |text|
text.each_grapheme_cluster do |cluster|
tally[cluster] += 1
end
end

tally
end
end
Loading