# Speed curiosity

**URL:** <https://www.ruby-forum.com/t/speed-curiosity/147316>\
**Category:** Mongrel\
**Created:** [August 30, 2008, 10:57pm UTC](https://www.ruby-forum.com/t/speed-curiosity/147316 "2008-08-30T22:57:04Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![rogerdpack](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/r/45deac/32.png) [@rogerdpack](https://www.ruby-forum.com/u/rogerdpack)\
**Post date:** [August 30, 2008, 10:57pm UTC](https://www.ruby-forum.com/t/speed-curiosity/147316/1 "2008-08-30T22:57:04Z")

</div>

As a note–using the mongrel example from

[http://mongrel.rubyforge.org/web/mongrel/files/README.html](http://mongrel.rubyforge.org/web/mongrel/files/README.html) and Mongrel  
1.1.5

It yielded (for me) ~800 req/s [running ab -n 1000 -c 1  
[http://localhost:3000/test](http://localhost:3000/test)]

and if I changed  
out.write(“hello!\n”)

to  
out.write(“hello!\n”\*10\_000)

it yielded ~300 req/s.

I was unable to get evented mongrel to run so wasn’t able to compare the  
two.

Doing a little bit of investigating, kcachegrind+ruby-prof points the  
latency to http\_response.rb line 137

```
@socket.write(data)

```

Experimenting by changing this line haphazardly to

```
  while data and data.length > 0
    wrote = @socket.write_nonblock(data)
    data = data[wrote..-1]
  end

```

yielded ~938 req/s [AFAICT]

Thoughts?

-=R

---

<div class="post-metadata">

**Author:** ![rogerdpack](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/r/45deac/32.png) [@rogerdpack](https://www.ruby-forum.com/u/rogerdpack)\
**Post date:** [September 1, 2008, 1:31pm UTC](https://www.ruby-forum.com/t/speed-curiosity/147316/2 "2008-09-01T13:31:21Z")

</div>

On 30 Aug 2008, at 21:57, Roger P. wrote:

> to  
> out.write(“hello!\n”\*10\_000)

AFAIK that’s not the fastest of operations.

> latency to http\_response.rb line 137  
> yielded ~938 req/s [AFAICT]  
> Thin and ebb both write more like this.

---

<div class="post-metadata">

**Author:** ![rogerdpack](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/r/45deac/32.png) [@rogerdpack](https://www.ruby-forum.com/u/rogerdpack)\
**Post date:** [September 1, 2008, 7:08pm UTC](https://www.ruby-forum.com/t/speed-curiosity/147316/3 "2008-09-01T19:08:20Z")

</div>

> > ```
> > out.write("hello!\n"*10_000)
> > 
> > ```
> 
> AFAIK that’s not the fastest of operations.

String creation itself turns out to not take too long:

> > Benchmark.measure { “hello!\n”\*10\_000}  
> > =\> #\<Benchmark… @real=0.000353097915649414, @utime=0.0, @cstime=0.0\>

So it’s not a huge bottleneck. Replacing it with a static string yields  
approx. the same results, which I did thanks to your suggestion.

> > latency to http\_response.rb line 137  
> > yielded ~938 req/s [AFAICT]  
> > Thin and ebb both write more like this.

Here’s some results [ruby 1.8.6p287 OS X] running ab -n 300

7B response: old: 1595 req/s new: 1690 req/s (thin: 1901)  
7K response: old: 1168 req/s new: 1559 req/s (thin: 1849)  
70K response: old: 366 req/s new: 1140 req/s (thin: 1160)  
700K response: old: 46 req/s new: 286 [or 48] req/s (thin: 295)\*

So overall better results, but most noticeable at the 70K level. It  
seems roughly on par with thin.  
IO#write is [I think] ruby thread friendly, so I’m not sure why the  
difference.

Thanks!  
-=R

- or 48: With some mongrel tests it would have a single long, out of  
300, that would take 4s while the others all took 20ms. Not sure why.  
Excluding that, it ran at the 286

# patch: Index: lib/mongrel/http\_response.rb

— lib/mongrel/http\_response.rb (revision 1036)  
+++ lib/mongrel/http\_response.rb (working copy)  
@@ -137,7 +137,15 @@  
end

```
 def write(data)

```

- 

```
 @socket.write(data)

```

- 

```
 while data and data.length > 0

```

- begin
- amount\_wrote = @socket.write\_nonblock(data)
- 

```
     data = data[amount_wrote..-1]

```

- rescue Errno::EAGAIN
- 
# wait for it to become writable again
- select nil, [@socket], nil, nil
- end
- 

```
 end

```

rescue =\> details  
socket\_error(details)  
end

---

<div class="post-metadata">

**Author:** ![rogerdpack](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/r/45deac/32.png) [@rogerdpack](https://www.ruby-forum.com/u/rogerdpack)\
**Post date:** [September 6, 2008, 7:04pm UTC](https://www.ruby-forum.com/t/speed-curiosity/147316/4 "2008-09-06T19:04:56Z")

</div>

Wayne Seguin wrote:

> Using write\_nonblock is an absolutely great suggestion; the only real  
> issue with write\_nonblock is that it doesn’t work in all environments.  
> While Ruby is supposed to fall back on blocking IO when async is  
> unavailable in the underlying system the reality is sketchy at best.

Yeah I guess the best thing’d be to fix IO#write in the core, instead of  
a hack to work around it 🙂 I have no idea what #write does but it  
appears that it is suboptimal, at least for this one distro on this one  
machine.

Maybe I should just file a ruby bug report that says “IO#write seems  
slow!” 🙂

Unfortunately it seems that on 1.9 it has the same speed pattern, so no  
help there.

Thanks for your help.

-=R

> > approx. the same results, which I did thanks to your suggestion.  
> > 70K response: old: 366 req/s new: 1140 req/s (thin: 1160)  
> > -=R

---

<div class="post-metadata">

**Author:** ![rogerdpack](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/r/45deac/32.png) [@rogerdpack](https://www.ruby-forum.com/u/rogerdpack)\
**Post date:** [September 1, 2008, 10:14pm UTC](https://www.ruby-forum.com/t/speed-curiosity/147316/5 "2008-09-01T22:14:38Z")

</div>

Roger,

Using write\_nonblock is an absolutely great suggestion; the only real  
issue with write\_nonblock is that it doesn’t work in all environments.  
While Ruby is supposed to fall back on blocking IO when async is  
unavailable in the underlying system the reality is sketchy at best.  
Through in Ruby implementations like Ruby and you end up with code  
that seemingly arbitrarily breaks. I’m all for write\_nonblock however  
if this route is pursued then there has to be a whole chain of  
capability and environment detection around this chunk not a simple  
rescue (yes I have done this before).

So, +1 from me as long as we modify as I suggested.

~Wayne

On Sep 1, 2008, at 13:08 , Roger P. wrote:

> approx. the same results, which I did thanks to your suggestion.  
> 70K response: old: 366 req/s new: 1140 req/s (thin: 1160)  
> -=R

---

<div class="post-metadata">

**Author:** ![rogerdpack](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/r/45deac/32.png) [@rogerdpack](https://www.ruby-forum.com/u/rogerdpack)\
**Post date:** [September 7, 2008, 12:54am UTC](https://www.ruby-forum.com/t/speed-curiosity/147316/6 "2008-09-07T00:54:34Z")

</div>

> Yeah I guess the best thing’d be to fix IO#write in the core, instead  
> of a hack to work around it 🙂 I have no idea what #write does but  
> it appears that it is suboptimal, at least for this one distro on  
> this one machine.

I do not think so, IO#write writes “exactly” n bytes even if the  
operating systems socket buffer infrastructure (and ruby’s one) cannot  
handle “exactly” n bytes with optimal efficiency. It maybe force  
sending fragmented packages, do costly buffer expansion or other time  
consuming operations.

IO#writepartial let the os (and ruby?) write the “optimal” amount  
of data in the current buffer and socket environment.

Maybe the usage of the select (ruby) system call Kernel#select can be  
used to write to the socket “only if it would not block and or do  
other costly operations” - see above.

Ruby 1.8 (maybe 1.9 too?) internally useing the select system call, to  
manage threading.

Year’s ago i tried to improve the throughput using select,  
unfortunately ruby 1.8 did not merge “my select call” in it’s  
internal select calls.

strace showed various “internal selects” and “my io selects” in  
unpredictable order, interrupting each other (ruby’s thread scheduler  
timer?)…

It was not he perfect solution 😉  
Maybe situation has changed?

Mfg

Markus
