Skip to content

Instantly share code, notes, and snippets.

@therealadam
Last active December 12, 2015 02:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save therealadam/4698972 to your computer and use it in GitHub Desktop.
Save therealadam/4698972 to your computer and use it in GitHub Desktop.
I took producer.rb, added threads and made it two orders of magnitude slower. Thus was born, lol.rb. (NOW LOLFAST)
require 'thread'
require 'ffaker'
require 'json'
def new_message
text = Faker::HipsterIpsum.sentences(3)
metrics = {
temp: rand(110), # degrees F
mass: rand(100), # kilograms
speed: rand(70) # miles per hour
}
{metrics: metrics, body: text}
end
def send_message(socket, msg)
encoded = JSON.dump(messages: msg)
socket.write(encoded)
end
FRAME_SIZE = 100
if __FILE__ == $0
# s = Socket.new('localhost', 4242)
s = $stdout
messages = Queue.new
producer = Thread.new do
loop do
messages.push(new_message)
end
end
begin
loop do
frame = FRAME_SIZE.times.map { messages.pop }
send_message(s, frame)
end
ensure
s.close
producer.join
end
end
Using local file /Users/adam/.rbenv/versions/1.9.3-p327-perf/bin/ruby.
Using local file /tmp/profile.
Total: 1837 samples
754 41.0% 41.0% 1340 72.9% Enumerable#sort_by
317 17.3% 58.3% 1507 82.0% Range#each
299 16.3% 74.6% 299 16.3% garbage_collector
199 10.8% 85.4% 199 10.8% Float#<=>
81 4.4% 89.8% 81 4.4% Kernel.rand
34 1.9% 91.7% 71 3.9% Array#map
31 1.7% 93.4% 1496 81.4% Faker::Lorem#sentence
15 0.8% 94.2% 1433 78.0% Module#random_pick
15 0.8% 95.0% 15 0.8% String#initialize_copy
14 0.8% 95.8% 23 1.3% Kernel#rand
13 0.7% 96.5% 36 2.0% Kernel#dup
13 0.7% 97.2% 13 0.7% String#capitalize!
8 0.4% 97.6% 19 1.0% Kernel#initialize_dup
6 0.3% 97.9% 14 0.8% Array#join
6 0.3% 98.3% 6 0.3% File.file?
5 0.3% 98.5% 1524 83.0% Object#new_message
4 0.2% 98.7% 4 0.2% Array#[]
4 0.2% 99.0% 1430 77.8% Faker::HipsterIpsum#words
4 0.2% 99.2% 1504 81.9% Faker::Lorem#sentences
\342\230\201
# encoding: UTF-8
require 'socket'
require 'ffaker'
require 'json'
ZERO_LENGTH = 1024 * 8
def new_message
text = Faker::HipsterIpsum.sentences(3)
random = File.read('/dev/zero', ZERO_LENGTH)
metrics = {
temp: rand(110), # degrees F
mass: rand(100), # kilograms
speed: rand(70) # miles per hour
}
{metrics: metrics, random: random, body: text}
end
def send_message(socket, msg)
encoded = JSON.dump(messages: msg)
socket.write(encoded)
end
FRAME_SIZE = 100
if __FILE__ == $0
s = TCPSocket.new('localhost', 4242)
# s = $stdout
begin
loop do
frame = FRAME_SIZE.times.map { new_message }
send_message(s, frame)
end
ensure
s.close
end
end
require 'ffaker'
require 'json'
def new_message
text = Faker::HipsterIpsum.sentences(3)
metrics = {
temp: rand(110), # degrees F
mass: rand(100), # kilograms
speed: rand(70) # miles per hour
}
{metrics: metrics, body: text}
end
def send_message(socket, msg)
encoded = JSON.dump(messages: msg)
socket.write(encoded)
end
FRAME_SIZE = 100
if __FILE__ == $0
# s = Socket.new('localhost', 4242)
s = $stdout
begin
loop do
frame = FRAME_SIZE.times.map { new_message }
send_message(s, frame)
end
ensure
s.close
end
end
Using local file /Users/adam/.rbenv/versions/1.9.3-p327-perf/bin/ruby.
Using local file /tmp/profile.
Total: 947 samples
370 39.1% 39.1% 725 76.6% Enumerable#sort_by
195 20.6% 59.7% 818 86.4% Range#each
118 12.5% 72.1% 118 12.5% Float#<=>
64 6.8% 78.9% 64 6.8% garbage_collector
43 4.5% 83.4% 43 4.5% Kernel.rand
23 2.4% 85.9% 23 2.4% IO#write
13 1.4% 87.2% 13 1.4% Array#join
12 1.3% 88.5% 26 2.7% Kernel#dup
11 1.2% 89.7% 37 3.9% Array#map
11 1.2% 90.8% 816 86.2% Faker::Lorem#sentence
10 1.1% 91.9% 10 1.1% String#initialize_copy
9 1.0% 92.8% 10 1.1% Kernel#rand
7 0.7% 93.6% 15 1.6% JSON::Ext::Generator::State#generate
6 0.6% 94.2% 10 1.1% Kernel#eval
6 0.6% 94.8% 774 81.7% Module#random_pick
6 0.6% 95.5% 829 87.5% Object#new_message
6 0.6% 96.1% 6 0.6% String#capitalize!
5 0.5% 96.6% 779 82.3% Faker::ArrayUtils#random_pick
4 0.4% 97.0% 4 0.4% Range.allocate
\342\230\201
@therealadam
Copy link
Author

Judging by the similarities of the profiles, I think both are bottlenecking around ffaker, but the threaded version has some contention that isn't jumping out at me.

Note this is test harness for spewing data (relatively) quickly with Ruby alone, so it doesn't need to be rocket scienced.

@JEG2
Copy link

JEG2 commented Feb 2, 2013

Did you actually run this with the commented out socket line, instead of just $stdout? Was the performance profile pretty much that same?

@JEG2
Copy link

JEG2 commented Feb 2, 2013

I wouldn't expect a big gain here.

You are just using two threads, one for building messages and one for sending. The only potential sleep time is when you write to the IO. So, in that one small window, that thread might sleep and allow some message creation.

However, you introduced a fair bit of infrastructure to get just this far. Threads have to be spun up and time sliced, plus we have enqueuing and dequeuing overhead. You are clearly paying a price for that.

One thought is that the price paid is just higher than any small gain achieved. Another possibility is that the GIL interaction with $stdout is tricky. I think those are my best two ideas.

Have you tried it on JRuby, where some real concurrency might be achievable?

I apologize if none of this helps. I'm not a threading pro and I'm mostly just thinking out loud.

@therealadam
Copy link
Author

@JEG2 I didn't do anything beyond profiling it to see if the bottleneck was different. I think I should only have two threads: one for producing messages, and another for IO. IF I was IO-bound, I believe this would effectively circumvent the GIL. However, I'm clearly bound on generating ipsum text.

FWIW, I introduced framing/batching messages because without it the producer was painfully slow.

I added threads on a lark to see if it was marginally faster. My surprise is how much slower it is.

@therealadam
Copy link
Author

@JEG2 check out lolput.rb. I cheated and got a lot closer to my baseline benchmark. On my laptop, this produces does about 75-78 MB/s of throughput:

# Producer
$ cat /dev/zero | nc localhost 4242

# Consumer
$ nc localhost 4242 | pv > /dev/null

lolput.rb does 42-45 MB/s. It's now much closer to my baseline. Generating a string of zeroes in the kernel is FAST guys!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment