Fastcgi performance problems and ruby

Hello

I have recently embarked upon the process of getting FastCGI to work
with Apache in order to run Ruby via FastCGI.

It is all working a running but the performance is not what I had
expected and certainly not what is needed to run a production web
application.

Strange because I am using fairly powerful computers. Stranger because
this discussion thread

http://groups.google.com/groups?hl=en&lr=lang_en&safe=off&threadm=20021005174928.A71759%40freeze.org&rnum=9&p
rev=/groups%3Fnum%3D100%26hl%3Den%26lr%3Dlang_en%26safe%3Doff%26q%3Dfastcgi%2Bruby%26btnG%3DSearch

-->extract from above thread
a) 400 request/sec with mod_ruby and cached Ruby script
b) 200 request/sec with fastcgi.so (the mixed C/Ruby version)
c) 100 request/sec with FCGI (clean Ruby version)
d) 70 request/sec with mod_ruby naive.

The thread above seems to suggest that it would be reasonable to expect
at least 200 requests/second using

fastcgi. I actually would have expected better performance than this
because I am using a 2.4ghz p4

I have included results of my tests below.

Can anyone throw any light for me on what sort performance I should
expect from ruby under mod_fastcgi?

Would anyone mind taking the trouble to benchmark their own mod_fastcgi
performance with Ruby?

I'm concerned that unless I get the performance up then ruby with
fastcgi does not represent a production

level solution from a performance perspective.

Thanks in advance for any help.

Andrew Stuart
andrew dot stuart at xse dot com dot au

···

***********************************************************************
***********************************************************************
****************load testing application
http://www.acme.com/software/http_load/
http_load - multiprocessing http test client

load testing executed on the web server itself. not from a client over
the network

***********************************************************************
***********************************************************************
****************server config
Intel P4 Celeron 2.4Ghz
512meg RAM
Fedora core 2
Apache
FastGCI
Ruby
Postgres

***********************************************************************
***********************************************************************
****************output of "free" shows plenty of memory available on
server

[root@xsecore docs]# free
total used free shared buffers
cached
Mem: 483724 138532 345192 0 21532
47944
-/+ buffers/cache: 69056 414668
Swap: 979956 0 979956
[root@xsecore docs]#

***********************************************************************
***********************************************************************
****************performance test plain HTML file -- 897.629 fetches/sec

[root@xsecore http_load-04jan2002]# ./http_load -verbose -parallel 100
-seconds 20 url82testhtml.txt
17960 fetches, 20 max parallel, 1.42082e+08 bytes, in 20.0083 seconds
7911 mean bytes/connection
897.629 fetches/sec, 7.10114e+06 bytes/sec
msecs/connect: 2.19892 mean, 20.245 max, 0.133 min
msecs/first-response: 11.1338 mean, 20.245 max, 9.666 min
HTTP response codes:
code 200 -- 17960
[root@xsecore http_load-04jan2002]#

***********************************************************************
***********************************************************************
****************performance test FastCGI simple ruby script dump env --
155.699 fetches/sec

[root@xsecore http_load-04jan2002]# ./http_load -verbose -parallel 100
-seconds 20 urlfcgienvrb.txt
3114 fetches, 100 max parallel, 2.21717e+06 bytes, in 20.0001 seconds
712 mean bytes/connection
155.699 fetches/sec, 110858 bytes/sec
msecs/connect: 0.445062 mean, 30.947 max, 0.152 min
msecs/first-response: 630.785 mean, 883.717 max, 13.366 min
HTTP response codes:
code 200 -- 3114
[root@xsecore http_load-04jan2002]#

Ruby script source for this test:

[root@xsecore http_load-04jan2002]# cat /var/www/fcgi-bin/env.rb
#!/usr/bin/ruby
require 'cgi'
require 'fcgi'

FCGI.each_cgi do |cgi|
content = ''
env = []
cgi.env_table.each do |k,v|
env << [k,v]
end
env.sort!
env.each do |k,v|
content << %Q(#{k} => #{v}<br>\n)
end
content << "hello from dude"
cgi.out{content}
end

[root@xsecore http_load-04jan2002]#

***********************************************************************
***********************************************************************
****************performance test FastCGI database access ruby script --
35.9997 fetches/sec

[root@xsecore http_load-04jan2002]# ./http_load -verbose -parallel 100
-seconds 20 url82dbtestrb.txt
1639 fetches, 100 max parallel, 908006 bytes, in 20.0001 seconds
554 mean bytes/connection
81.9498 fetches/sec, 45400.2 bytes/sec
msecs/connect: 0.429315 mean, 20.021 max, 0.146 min
msecs/first-response: 1182.09 mean, 1489.77 max, 208.131 min
HTTP response codes:
code 200 -- 1639
[root@xsecore http_load-04jan2002]#

Ruby script source for this test:

[root@xsecore http_load-04jan2002]# cat /var/www/fcgi-bin/dbtest.rb
#!/usr/bin/ruby
require 'cgi'
require 'fcgi'
require 'dbi'

dbh = DBI.connect('DBI:Pg:catchmail', 'dbusername', 'password')

FCGI.each_cgi do |cgi|

content = ''
dbh.select_all('select msgno, msgtype, msgsubject, msgbody from
message limit 10 offset 50') do |row|
content << "subject line: "
content << row["msgsubject"]
content << "<br>"
end

cgi.out{content}
end

dbh.disconnect
[root@xsecore http_load-04jan2002]#

***********************************************************************
***********************************************************************
****************performance test FastCGI echo example -- 664.188
fetches/sec

[root@xsecore http_load-04jan2002]# ./http_load -verbose -parallel 100
-seconds 20 url82fcgiecho.txt
13290 fetches, 31 max parallel, 1.21736e+07 bytes, in 20.0094 seconds
916 mean bytes/connection
664.188 fetches/sec, 608396 bytes/sec
msecs/connect: 2.56759 mean, 30.368 max, 0.153 min
msecs/first-response: 15.0369 mean, 86.158 max, 12.604 min
HTTP response codes:
code 200 -- 13290
[root@xsecore http_load-04jan2002]#

Source code for echo is written in C and is included in the examples in
the fastcgi software

***********************************************************************
***********************************************************************
****************performance test PHP example (not using FastCGI) --
234.798 fetches/sec

[root@xsecore http_load-04jan2002]# ./http_load -verbose -parallel 100
-seconds 20 url82testphp.txt
4699 fetches, 100 max parallel, 1.88669e+08 bytes, in 20.013 seconds
40150.9 mean bytes/connection
234.798 fetches/sec, 9.42734e+06 bytes/sec
msecs/connect: 5.28116 mean, 113.121 max, 0.15 min
msecs/first-response: 161.609 mean, 2363 max, 22.176 min
HTTP response codes:
code 200 -- 4699
[root@xsecore http_load-04jan2002]#

Source code for test.php

[root@xsecore html]# cat test.php
<html>
<head>
<title>PHP Test</title>
</head>
<body>
<?php echo '<p>Hello World</p>'; ?>
</body>
</html>

<?php
phpinfo();
?>

***********************************************************************
***********************************************************************
****************performance test FastCGI simple ruby script dump
username/IP -- 159.399 fetches/sec

[root@xsecore html]#
[root@xsecore http_load-04jan2002]# ./http_load -verbose -parallel 100
-seconds 20 urlfcgiusernamerb.txt
3188 fetches, 100 max parallel, 153024 bytes, in 20.0001 seconds
48 mean bytes/connection
159.399 fetches/sec, 7651.15 bytes/sec
msecs/connect: 0.462713 mean, 41.912 max, 0.168 min
msecs/first-response: 615.345 mean, 949.505 max, 160.687 min
HTTP response codes:
code 200 -- 3188
[root@xsecore http_load-04jan2002]#

Source code for username.rb

[root@xsecore http_load-04jan2002]# cat /var/www/fcgi-bin/username.rb
#!/usr/bin/ruby

require "fcgi"

FCGI.each_cgi {|cgi|
puts cgi.header
puts "You are #{cgi.remote_user} <br>"
puts "Connecting from #{cgi.remote_addr} <br>"
}

[root@xsecore http_load-04jan2002]#

***********************************************************************
***********************************************************************
***************************extract from httpd.conf relating to FastCGI

LoadModule fastcgi_module modules/mod_fastcgi.so

<IfModule mod_fastcgi.c>

# URIs that begin with /fcgi-bin/, are found in /var/www/fcgi-bin/
Alias /fcgi-bin/ /var/www/fcgi-bin/

FastCgiIpcDir /var/fcgi/
#FastCgiConfig -autoUpdate

# Anything in here is handled as a "dynamic" server if not defined
as "static" or "external"
<Directory /var/www/fcgi-bin/>
Order allow,deny
Allow from all
SetHandler fastcgi-script
Options +ExecCGI
#Require user andrewstuart
</Directory>

# Anything with one of these extensions is handled as a "dynamic"
server if not defined as
# "static" or "external". Note: "dynamic" servers require ExecCGI
to be on in their directory.
AddHandler fastcgi-script .fcgi .fpl

# Start a "static" server at httpd initialization inside the scope
of the SetHandler
FastCgiServer /var/www/fcgi-bin/examples/echo -processes 5

# Start a "static" server at httpd initialization inside the scope
of the AddHandler
#FastCgiServer /var/www/fcgi-bin/examples/echo

# Start a "static" server at httpd initialization outside the scope
of the Set/AddHandler
#FastCgiServer /var/www/htdocs/some/path/coolapp
<Directory /var/www/html/ruby>
SetHandler fastcgi-script
    </Directory>

</IfModule>

i can't run all of them - but here's the env test:

[ahoward@www htdocs]$ cat env.fcgi
#!/usr/local/ruby-1.8.0/bin/ruby
require 'cgi'
require 'fcgi'
FCGI.each_cgi do |cgi|
   content = ''
   env =
   cgi.env_table.each{|k,v| env << [k,v]}
   env.sort!
   env.each{|k,v| content << %Q(#{k} => #{v}<br>\n)}
   cgi.out{content}
end

[ahoward@www htdocs]$ echo http://127.0.0.1/env.fcgi | http_load -verbose -parallel 100 -seconds 20 /dev/stdin
4420 fetches, 100 max parallel, 3.54926e+06 bytes, in 20.0237 seconds
803 mean bytes/connection
220.738 fetches/sec, 177253 bytes/sec
msecs/connect: 5.67523 mean, 127.545 max, 0.203 min
msecs/first-response: 86.2776 mean, 1077.36 max, 29.886 min
HTTP response codes:
   code 200 -- 4420

[ahoward@www htdocs]$ ab -n 100 -c 100 -t 20 http://127.0.0.1/env.fcgi
This is ApacheBench, Version 2.0.40-dev <$Revision: 1.116 $> apache-2.0
Copyright (c) 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Copyright (c) 1998-2002 The Apache Software Foundation, http://www.apache.org/

Benchmarking 127.0.0.1 (be patient)
Finished 4129 requests

Server Software: Apache/2.0.47
Server Hostname: 127.0.0.1
Server Port: 80

Document Path: /env.fcgi
Document Length: 829 bytes

Concurrency Level: 100
Time taken for tests: 20.365556 seconds
Complete requests: 4129
Failed requests: 0
Write errors: 0
Total transferred: 4244612 bytes
HTML transferred: 3422941 bytes
Requests per second: 202.74 [#/sec] (mean)
Time per request: 493.232 [ms] (mean)
Time per request: 4.932 [ms] (mean, across all concurrent requests)
Transfer rate: 203.53 [Kbytes/sec] received

Connection Times (ms)
               min mean[+/-sd] median max
Connect: 0 0 1.6 0 92
Processing: 71 483 105.3 468 1689
Waiting: 2 312 158.4 344 1299
Total: 71 483 105.3 468 1689

Percentage of the requests served within a certain time (ms)
   50% 468
   66% 490
   75% 493
   80% 495
   90% 544
   95% 657
   98% 802
   99% 879
  100% 1689 (longest request)

my machine info (it's a dog)

[ahoward@www htdocs]$ uname -srm
Linux 2.4.20-8 i686

[ahoward@www htdocs]$ cat /etc/redhat-release Red Hat Linux release 9 (Shrike)
[ahoward@www htdocs]$ cat /proc/cpuinfo processor : 0
vendor_id : GenuineIntel
cpu family : 6
model : 5
model name : Pentium II (Deschutes)
stepping : 2
cpu MHz : 398.273
cache size : 512 KB
fdiv_bug : no
hlt_bug : no
f00f_bug : no
coma_bug : no
fpu : yes
fpu_exception : yes
cpuid level : 2
wp : yes
flags : fpu vme de pse tsc msr pae mce cx8 sep mtrr pge mca cmov pat pse36 mmx fxsr
bogomips : 794.62

[ahoward@www htdocs]$ cat /proc/meminfo
         total: used: free: shared: buffers: cached:
Mem: 393760768 364519424 29241344 0 80605184 192499712
Swap: 263200768 11534336 251666432
MemTotal: 384532 kB
MemFree: 28556 kB
MemShared: 0 kB
Buffers: 78716 kB
Cached: 176740 kB
SwapCached: 11248 kB
Active: 241496 kB
ActiveAnon: 40960 kB
ActiveCache: 200536 kB
Inact_dirty: 100 kB
Inact_laundry: 57860 kB
Inact_clean: 5604 kB
Inact_target: 61012 kB
HighTotal: 0 kB
HighFree: 0 kB
LowTotal: 384532 kB
LowFree: 28556 kB
SwapTotal: 257032 kB
SwapFree: 245768 kB
[ahoward@www htdocs]$ ruby -v
ruby 1.8.0 (2003-09-22) [i686-linux]
[ahoward@www htdocs]$ httpd -v
Server version: Apache/2.0.40
Server built: Feb 25 2003 05:01:56

hope that's helpful.

kind regards.

-a

···

On Mon, 1 Nov 2004 andrew.stuart@xse.com.au wrote:

Can anyone throw any light for me on what sort performance I should
expect from ruby under mod_fastcgi?

--

EMAIL :: Ara [dot] T [dot] Howard [at] noaa [dot] gov
PHONE :: 303.497.6469
When you do something, you should burn yourself completely, like a good
bonfire, leaving no trace of yourself. --Shunryu Suzuki

===============================================================================

Sometimes, limiting the number of fastcgi processes that get created can help boost performance. See:

http://www.renegadeinternet.com/kb/faq.php?num=4&f_id=11&s_id=34&q_id=125

···

andrew.stuart@xse.com.au wrote:

Hello

I have recently embarked upon the process of getting FastCGI to work
with Apache in order to run Ruby via FastCGI.

It is all working a running but the performance is not what I had
expected and certainly not what is needed to run a production web
application.

Strange because I am using fairly powerful computers. Stranger because
this discussion thread

http://groups.google.com/groups?hl=en&lr=lang_en&safe=off&threadm=20021005174928.A71759%40freeze.org&rnum=9&p
rev=/groups%3Fnum%3D100%26hl%3Den%26lr%3Dlang_en%26safe%3Doff%26q%3Dfastcgi%2Bruby%26btnG%3DSearch

-->extract from above thread
a) 400 request/sec with mod_ruby and cached Ruby script
b) 200 request/sec with fastcgi.so (the mixed C/Ruby version)
c) 100 request/sec with FCGI (clean Ruby version)
d) 70 request/sec with mod_ruby naive.

The thread above seems to suggest that it would be reasonable to expect
at least 200 requests/second using

fastcgi. I actually would have expected better performance than this
because I am using a 2.4ghz p4

I have included results of my tests below.

Can anyone throw any light for me on what sort performance I should
expect from ruby under mod_fastcgi?

Would anyone mind taking the trouble to benchmark their own mod_fastcgi
performance with Ruby?

I'm concerned that unless I get the performance up then ruby with
fastcgi does not represent a production

level solution from a performance perspective.

Thanks in advance for any help.

Andrew Stuart
andrew dot stuart at xse dot com dot au

***********************************************************************
****************load testing application
http_load
http_load - multiprocessing http test client

load testing executed on the web server itself. not from a client over
the network

***********************************************************************
****************server config
Intel P4 Celeron 2.4Ghz
512meg RAM
Fedora core 2
Apache
FastGCI
Ruby
Postgres

***********************************************************************
****************output of "free" shows plenty of memory available on
server

[root@xsecore docs]# free
total used free shared buffers
cached
Mem: 483724 138532 345192 0 21532
47944
-/+ buffers/cache: 69056 414668
Swap: 979956 0 979956
[root@xsecore docs]#

***********************************************************************
****************performance test plain HTML file -- 897.629 fetches/sec

[root@xsecore http_load-04jan2002]# ./http_load -verbose -parallel 100
-seconds 20 url82testhtml.txt
17960 fetches, 20 max parallel, 1.42082e+08 bytes, in 20.0083 seconds
7911 mean bytes/connection
897.629 fetches/sec, 7.10114e+06 bytes/sec
msecs/connect: 2.19892 mean, 20.245 max, 0.133 min
msecs/first-response: 11.1338 mean, 20.245 max, 9.666 min
HTTP response codes:
code 200 -- 17960
[root@xsecore http_load-04jan2002]#

***********************************************************************
****************performance test FastCGI simple ruby script dump env --
155.699 fetches/sec

[root@xsecore http_load-04jan2002]# ./http_load -verbose -parallel 100
-seconds 20 urlfcgienvrb.txt
3114 fetches, 100 max parallel, 2.21717e+06 bytes, in 20.0001 seconds
712 mean bytes/connection
155.699 fetches/sec, 110858 bytes/sec
msecs/connect: 0.445062 mean, 30.947 max, 0.152 min
msecs/first-response: 630.785 mean, 883.717 max, 13.366 min
HTTP response codes:
code 200 -- 3114
[root@xsecore http_load-04jan2002]#

Ruby script source for this test:

[root@xsecore http_load-04jan2002]# cat /var/www/fcgi-bin/env.rb
#!/usr/bin/ruby
require 'cgi'
require 'fcgi'

FCGI.each_cgi do |cgi|
content = ''
env =
cgi.env_table.each do |k,v|
env << [k,v]
end
env.sort!
env.each do |k,v|
content << %Q(#{k} => #{v}<br>\n)
end
content << "hello from dude"
cgi.out{content}
end

[root@xsecore http_load-04jan2002]#

***********************************************************************
****************performance test FastCGI database access ruby script --
35.9997 fetches/sec

[root@xsecore http_load-04jan2002]# ./http_load -verbose -parallel 100
-seconds 20 url82dbtestrb.txt
1639 fetches, 100 max parallel, 908006 bytes, in 20.0001 seconds
554 mean bytes/connection
81.9498 fetches/sec, 45400.2 bytes/sec
msecs/connect: 0.429315 mean, 20.021 max, 0.146 min
msecs/first-response: 1182.09 mean, 1489.77 max, 208.131 min
HTTP response codes:
code 200 -- 1639
[root@xsecore http_load-04jan2002]#

Ruby script source for this test:

[root@xsecore http_load-04jan2002]# cat /var/www/fcgi-bin/dbtest.rb
#!/usr/bin/ruby
require 'cgi'
require 'fcgi'
require 'dbi'

dbh = DBI.connect('DBI:Pg:catchmail', 'dbusername', 'password')

FCGI.each_cgi do |cgi|

content = ''
dbh.select_all('select msgno, msgtype, msgsubject, msgbody from
message limit 10 offset 50') do |row|
content << "subject line: "
content << row["msgsubject"]
content << "<br>"
end

cgi.out{content}
end

dbh.disconnect
[root@xsecore http_load-04jan2002]#

***********************************************************************
****************performance test FastCGI echo example -- 664.188
fetches/sec

[root@xsecore http_load-04jan2002]# ./http_load -verbose -parallel 100
-seconds 20 url82fcgiecho.txt
13290 fetches, 31 max parallel, 1.21736e+07 bytes, in 20.0094 seconds
916 mean bytes/connection
664.188 fetches/sec, 608396 bytes/sec
msecs/connect: 2.56759 mean, 30.368 max, 0.153 min
msecs/first-response: 15.0369 mean, 86.158 max, 12.604 min
HTTP response codes:
code 200 -- 13290
[root@xsecore http_load-04jan2002]#

Source code for echo is written in C and is included in the examples in
the fastcgi software

***********************************************************************
****************performance test PHP example (not using FastCGI) --
234.798 fetches/sec

[root@xsecore http_load-04jan2002]# ./http_load -verbose -parallel 100
-seconds 20 url82testphp.txt
4699 fetches, 100 max parallel, 1.88669e+08 bytes, in 20.013 seconds
40150.9 mean bytes/connection
234.798 fetches/sec, 9.42734e+06 bytes/sec
msecs/connect: 5.28116 mean, 113.121 max, 0.15 min
msecs/first-response: 161.609 mean, 2363 max, 22.176 min
HTTP response codes:
code 200 -- 4699
[root@xsecore http_load-04jan2002]#

Source code for test.php

[root@xsecore html]# cat test.php
<html>
<head>
<title>PHP Test</title>
</head>
<body>
<?php echo '<p>Hello World</p>'; ?>
</body>
</html>

<?php
phpinfo();
?>

***********************************************************************
****************performance test FastCGI simple ruby script dump
username/IP -- 159.399 fetches/sec

[root@xsecore html]#
[root@xsecore http_load-04jan2002]# ./http_load -verbose -parallel 100
-seconds 20 urlfcgiusernamerb.txt
3188 fetches, 100 max parallel, 153024 bytes, in 20.0001 seconds
48 mean bytes/connection
159.399 fetches/sec, 7651.15 bytes/sec
msecs/connect: 0.462713 mean, 41.912 max, 0.168 min
msecs/first-response: 615.345 mean, 949.505 max, 160.687 min
HTTP response codes:
code 200 -- 3188
[root@xsecore http_load-04jan2002]#

Source code for username.rb

[root@xsecore http_load-04jan2002]# cat /var/www/fcgi-bin/username.rb
#!/usr/bin/ruby

require "fcgi"

FCGI.each_cgi {|cgi|
puts cgi.header
puts "You are #{cgi.remote_user} <br>"
puts "Connecting from #{cgi.remote_addr} <br>"
}

[root@xsecore http_load-04jan2002]#

***********************************************************************
***************************extract from httpd.conf relating to FastCGI

LoadModule fastcgi_module modules/mod_fastcgi.so

<IfModule mod_fastcgi.c>

# URIs that begin with /fcgi-bin/, are found in /var/www/fcgi-bin/
Alias /fcgi-bin/ /var/www/fcgi-bin/

FastCgiIpcDir /var/fcgi/
#FastCgiConfig -autoUpdate

# Anything in here is handled as a "dynamic" server if not defined
as "static" or "external"
<Directory /var/www/fcgi-bin/>
Order allow,deny
Allow from all
SetHandler fastcgi-script
Options +ExecCGI
#Require user andrewstuart
</Directory>

# Anything with one of these extensions is handled as a "dynamic"
server if not defined as
# "static" or "external". Note: "dynamic" servers require ExecCGI
to be on in their directory.
AddHandler fastcgi-script .fcgi .fpl

# Start a "static" server at httpd initialization inside the scope
of the SetHandler
FastCgiServer /var/www/fcgi-bin/examples/echo -processes 5

# Start a "static" server at httpd initialization inside the scope
of the AddHandler
#FastCgiServer /var/www/fcgi-bin/examples/echo

# Start a "static" server at httpd initialization outside the scope
of the Set/AddHandler
#FastCgiServer /var/www/htdocs/some/path/coolapp
<Directory /var/www/html/ruby>
SetHandler fastcgi-script
    </Directory>

</IfModule>

Hello,

Ruby script source for this test:

[root@xsecore http_load-04jan2002]# cat /var/www/fcgi-bin/env.rb
#!/usr/bin/ruby
require 'cgi'
require 'fcgi'

FCGI.each_cgi do |cgi|
content = ''
env =
cgi.env_table.each do |k,v|
env << [k,v]
end
env.sort!
env.each do |k,v|
content << %Q(#{k} => #{v}<br>\n)
end
content << "hello from dude"
cgi.out{content}
end

Note, that the FCGI.each_cgi method is slower than the FCGI.each method.
Depending upon how you want to use FCGI with a CGI object this may or may
not be necessary.
You may want to try the following code snippet to see if there is
any significant difference between FCGI.each_cgi and FCGI.each.

#!/usr/bin/ruby
require "fcgi"
FCGI.each do |request|
   out = request.out
   content = ""
   env = Array.new
   request.env.each do |k,v|
     env << [k,v]
   end
   env.sort!
   env.each do |k,v|
     content << %Q(#{k} => #{v}<br>\n)
   end

   content << "hello from dude"
   out.print "Content-Type: text/plain\r\n"
   out.print "Content-Length: #{content.size}\r\n"
   out.print "\r\n"
   out.print content
   request.finish
end

Best,
Zev

···

On Tue, 2 Nov 2004 12:18:51 +0900, <andrew.stuart@xse.com.au> wrote:

cgi.out{content}

I think that may be your problem. The 'out' methods of cgi are inefficient,
I believe due to the large number of singleton methods which are added on
each object - although I seem to remember it's much worse if you do
each_cgi("html3") for example.

As suggested elsewhere, try rewriting your app to use the native fastcgi API
(each) rather than each_cgi.

Also, if your benchmarks are forcing a lot of concurrent connections, then
you could try configuring mod_fastcgi to launch a static pool of processes
for your application (say 5 or 10 processes) to see if that zips things
along, because in that case they will be pre-spawned.

Regards,

Brian.

Hello,

···

On Tuesday, November 2, 2004, at 12:37 AM, Zev Blut wrote:

Note, that the FCGI.each_cgi method is slower than the FCGI.each method.
Depending upon how you want to use FCGI with a CGI object this may or may
not be necessary.
You may want to try the following code snippet to see if there is
any significant difference between FCGI.each_cgi and FCGI.each.

It would make sense for each_cgi to be slower. I think CGI adds alot of methods to itself dynamically on initialization.

~ pat

J. D. ha scritto:

a little suggestion: try to quote a little less if the quote is not necessary :slight_smile:

(sorry if I sound rude, not being a native speaker, I sometime do)

I'm also curious about Singleton Class, i.e. I recall (it was a while ago)
somthing about modifying CGI to be a Singleton class rather then a regular
class --that seemed helpful. I wonder if that would help and/or if the same
can be applied to FCGI. But I'm not sure how that all fits together so I was
wondering if someone could make sense it for us.

Thanks,
T.

···

On Tuesday 02 November 2004 06:20 am, Brian Candler wrote:

> cgi.out{content}

I think that may be your problem. The 'out' methods of cgi are inefficient,
I believe due to the large number of singleton methods which are added on
each object - although I seem to remember it's much worse if you do
each_cgi("html3") for example.

As suggested elsewhere, try rewriting your app to use the native fastcgi
API (each) rather than each_cgi.

Also, if your benchmarks are forcing a lot of concurrent connections, then
you could try configuring mod_fastcgi to launch a static pool of processes
for your application (say 5 or 10 processes) to see if that zips things
along, because in that case they will be pre-spawned.

gabriele renzi wrote:

J. D. ha scritto:

a little suggestion: try to quote a little less if the quote is not necessary :slight_smile:

(sorry if I sound rude, not being a native speaker, I sometime do)

Oops. I will use your suggestion.

And, you do do not sound rude :slight_smile:

That sentence doesn't make much sense to me - can you define what you mean
by "modifying .. to be a Singleton class rather than a regular class" ??

Remember that:

1. CGI is_a Class (i.e. CGI is an object, an instance of class Class)

2. This object has a singleton class; that is, methods which apply to
   CGI itself, and not to any instance of class CGI, nor any other
   instance of class Class.

   For example, CGI.escapeHTML is a method within CGI's singleton class

3. Singleton classes are anonymous; there is no need to name them, as they
   are always only associated with one object.

So I can't really make sense of what you're suggesting. If you've not seen
it, have a look at Captcha and see
if that makes the terminology any clearer.

The issue with performance is: CGI.new creates a new object (an instance of
class CGI), and at object creation time, a whole bunch of methods are added
at run-time to that instance's singleton class.

e.g.

  a = CGI.new("html3")
  b = CGI.new("html4")

creates two objects, each of which is an instance of CGI, but if you look in
the source for cgi.rb you can see a lot of work is done in the initialize()
method:

    case type
    when "html3"
      extend Html3
      element_init()
      extend HtmlExtension
    when "html4"
      extend Html4
      element_init()
      extend HtmlExtension

In turn, method element_init does a whole load of work defining methods the
slow way, with code such as

      for element in %w[ A TT I B U STRIKE BIG SMALL SUB SUP EM STRONG
          DFN CODE SAMP KBD VAR CITE FONT ADDRESS DIV center MAP
          APPLET PRE XMP LISTING DL OL UL DIR MENU SELECT table TITLE
          STYLE SCRIPT H1 H2 H3 H4 H5 H6 TEXTAREA FORM BLOCKQUOTE
          CAPTION ]
        methods += <<-BEGIN + nn_element_def(element) + <<-END
          def #{element.downcase}(attributes = {})
        BEGIN
          end
        END
      end
      ...
      eval(methods)

I'm sure this was to save the programmer having to enumerate all these
methods in the mixin, but I'd have thought it could be done more
efficiently than that (by defining the methods once in the mixin, not at
run-time in every object instance)

Perhaps the assumption in the CGI library is that normally a CGI is a
one-shot process: there is no point carefully preparing mixins for
Html3/Html4/Html4Tr/Html4Fr if at runtime only one will be used and then the
CGI will terminate. But that assumption is not true for a FastCGI
environment.

But actually, if you just use CGI.new without passing a html type, this
heavyweight construction work isn't done anyway.

Regards,

Brian.

···

On Tue, Nov 02, 2004 at 11:29:09PM +0900, trans. (T. Onoma) wrote:

I'm also curious about Singleton Class, i.e. I recall (it was a while ago)
somthing about modifying CGI to be a Singleton class rather then a regular
class --that seemed helpful.

> I'm also curious about Singleton Class, i.e. I recall (it was a while
> ago) somthing about modifying CGI to be a Singleton class rather then a
> regular class --that seemed helpful.

That sentence doesn't make much sense to me - can you define what you mean
by "modifying .. to be a Singleton class rather than a regular class" ??

Ah yes, the old name confusion. Sorry. I'm _not_ referring to Virtual class
(or Instance class), which is also called singleton. That's different. (BTW
we really need to get onto this new naming convention to prevent this
confusion! Yes?) I'm referring to the other kind where by only one instance
of a class can exist. See Singleton module for more info on that.

So if I make CGI singleton, then only one instance of it ever exists
--presumably even between http requests. Thus an increase in performance. But
like I said I'm not perfectly clear about how it all works, and how FCGI
might fit in.

HTH,
T.

···

On Tuesday 02 November 2004 09:49 am, Brian Candler wrote:

On Tue, Nov 02, 2004 at 11:29:09PM +0900, trans. (T. Onoma) wrote:

Remember that:

1. CGI is_a Class (i.e. CGI is an object, an instance of class Class)

2. This object has a singleton class; that is, methods which apply to
   CGI itself, and not to any instance of class CGI, nor any other
   instance of class Class.

   For example, CGI.escapeHTML is a method within CGI's singleton class

3. Singleton classes are anonymous; there is no need to name them, as they
   are always only associated with one object.

So I can't really make sense of what you're suggesting. If you've not seen
it, have a look at http://www.rubygarden.org/ruby?SingletonTutorial and see
if that makes the terminology any clearer.

The issue with performance is: CGI.new creates a new object (an instance of
class CGI), and at object creation time, a whole bunch of methods are added
at run-time to that instance's singleton class.

e.g.

  a = CGI.new("html3")
  b = CGI.new("html4")

creates two objects, each of which is an instance of CGI, but if you look
in the source for cgi.rb you can see a lot of work is done in the
initialize() method:

    case type
    when "html3"
      extend Html3
      element_init()
      extend HtmlExtension
    when "html4"
      extend Html4
      element_init()
      extend HtmlExtension

In turn, method element_init does a whole load of work defining methods the
slow way, with code such as

      for element in %w[ A TT I B U STRIKE BIG SMALL SUB SUP EM STRONG
          DFN CODE SAMP KBD VAR CITE FONT ADDRESS DIV center MAP
          APPLET PRE XMP LISTING DL OL UL DIR MENU SELECT table TITLE
          STYLE SCRIPT H1 H2 H3 H4 H5 H6 TEXTAREA FORM BLOCKQUOTE
          CAPTION ]
        methods += <<-BEGIN + nn_element_def(element) + <<-END
          def #{element.downcase}(attributes = {})
        BEGIN
          end
        END
      end
      ...
      eval(methods)

I'm sure this was to save the programmer having to enumerate all these
methods in the mixin, but I'd have thought it could be done more
efficiently than that (by defining the methods once in the mixin, not at
run-time in every object instance)

Perhaps the assumption in the CGI library is that normally a CGI is a
one-shot process: there is no point carefully preparing mixins for
Html3/Html4/Html4Tr/Html4Fr if at runtime only one will be used and then
the CGI will terminate. But that assumption is not true for a FastCGI
environment.

But actually, if you just use CGI.new without passing a html type, this
heavyweight construction work isn't done anyway.

Regards,

Brian.

> That sentence doesn't make much sense to me - can you define what you mean
> by "modifying .. to be a Singleton class rather than a regular class" ??

Ah yes, the old name confusion. Sorry. I'm _not_ referring to Virtual class
(or Instance class), which is also called singleton. That's different. (BTW
we really need to get onto this new naming convention to prevent this
confusion! Yes?) I'm referring to the other kind where by only one instance
of a class can exist. See Singleton module for more info on that.

OK, the 'Singleton pattern'. Always seemed rather pointless to me; if
there's only ever going to be one CGI instance, then you might as well make
all the methods class methods of CGI itself, and use class instance
variables.

So if I make CGI singleton, then only one instance of it ever exists
--presumably even between http requests. Thus an increase in performance. But
like I said I'm not perfectly clear about how it all works, and how FCGI
might fit in.

Well, it seems to me that each request ought to create a different CGI
object, for the simple reason that each HTTP request has a different set of
parameters - and it's the CGI object that represents them.

I suppose you could 're-use' the previous CGI object, by resetting its
instance variables to the new request, but that's not a very Rubyesque way
of doing things (IMO).

It would be better, I think, to make the object creation faster by not doing
all those slow eval def's each time. For example, if you want to include the
CGI::Html3 module you could first check if it exists, and if it doesn't then
define it (in the slow eval way if that's tidier). Subsequent requests can
just do 'extend CGI::Html3' which is fast. e.g.

  unless defined? Html3
    module Html3
      .. define static methods
    end
    .. define methods dynamically using eval
  end
  extend Html3

Regards,

Brian.

···

On Wed, Nov 03, 2004 at 12:08:08AM +0900, trans. (T. Onoma) wrote:

Hello,

···

On Tuesday, November 2, 2004, at 10:08 AM, trans. (T. Onoma) wrote:

So if I make CGI singleton, then only one instance of it ever exists
--presumably even between http requests. Thus an increase in performance. But
like I said I'm not perfectly clear about how it all works, and how FCGI
might fit in.

This is leads to a nice api, ie CGI::params. I think it's more of a plus for api reasons, than performance. You still end up having some kind of Connection/Request object per http request.

Cheers,

Patrick

OK, the 'Singleton pattern'. Always seemed rather pointless to me; if
there's only ever going to be one CGI instance, then you might as well make
all the methods class methods of CGI itself, and use class instance
variables.

In fact I asked about that (albeit not in relation to CGI) on the list a
couple weeks ago and three options were brought up: Singleton _pattern_,
Module functions, as you suggest, and an Object with virtual/singleton class.
All are very similar but there does turn out to be a few important
differences.

Well, it seems to me that each request ought to create a different CGI
object, for the simple reason that each HTTP request has a different set of
parameters - and it's the CGI object that represents them.

I suppose you could 're-use' the previous CGI object, by resetting its
instance variables to the new request, but that's not a very Rubyesque way
of doing things (IMO).

I see what your saying, but a Singleton can have open parameters that can be
changed in place, too. I have used Singleton CGI myself and it does work. As
for the Ruby Way to do it, you certainly have a point, and probably the most
OOP tack would be to separate out the part that changes from the part that
doesn't. The part that doesn't, i.e. CGI, would then delegate for the part
that does, i.e. the Request. Thus only the changing part need to be
re-instantiated between requests. Seem reasonable? Nonetheless I'm fairly
sure that still won't be as fast as in place changes.

It would be better, I think, to make the object creation faster by not
doing all those slow eval def's each time. For example, if you want to
include the CGI::Html3 module you could first check if it exists, and if it
doesn't then define it (in the slow eval way if that's tidier). Subsequent
requests can just do 'extend CGI::Html3' which is fast. e.g.

  unless defined? Html3
    module Html3
      .. define static methods
    end
    .. define methods dynamically using eval
  end
  extend Html3

Certainly would help. But again, if you're after speed....

BTW, look what just popped up on RAA:

  http://raa.ruby-lang.org/project/apachecgi/

I haven't looked at it yet thought So I'm not sure how it works, but I
suspect...

T.

···

On Tuesday 02 November 2004 11:29 am, Brian Candler wrote:

No. all apachecgi extension does it removes parsing of request parameters from the CGI class and delegates it to mod_ruby.

Cheers,
Kent.

···

On Nov 2, 2004, at 11:53 PM, trans. (T. Onoma) wrote:

Certainly would help. But again, if you're after speed....

BTW, look what just popped up on RAA:

  http://raa.ruby-lang.org/project/apachecgi/

I haven't looked at it yet thought So I'm not sure how it works, but I
suspect...

T.

Ah, okay. Thanks, now I don't have to figure it out myself :slight_smile:

T.

···

On Wednesday 03 November 2004 10:02 pm, Kent Sibilev wrote:

On Nov 2, 2004, at 11:53 PM, trans. (T. Onoma) wrote:
> Certainly would help. But again, if you're after speed....
>
> BTW, look what just popped up on RAA:
>
> http://raa.ruby-lang.org/project/apachecgi/
>
> I haven't looked at it yet thought So I'm not sure how it works, but I
> suspect...
>
> T.

No. all apachecgi extension does it removes parsing of request
parameters from the CGI class and delegates it to mod_ruby.