Dear all,

on June 30, i wrote:

However, there might be NaviServer applications with nsvs out there, for which the change of using rwlocks for nsv variables might lead to a reduced performance. So we have in general the following options for the forthcoming release:

a) hardwire nsvs to rwlocks
b) make it a compile-time decision to choose between rwlocks and mutex locks for nsvs c) provide a configuration variable in the config file to choose between rwlocks and mutex locks for nsvs at startup
d) provide a runtime API for creating nsv arrays with rwlock or mutex

Since there were some concerns that rwlocks might not be the best choice for all nsvs, i did some more extensive tests and metering on several servers. These tests convinced me that rwlocks are best default value for web server applications, since there is an overwhelming amount of read operations compared to write operations (see below, e.g. less the 5% writer operations, these are real-world figures from our production code at the university):

    Name                        Locks   Busy    Read    Write   Write %
    nsv:56:live                 192.91M 14      192.87M 47.79K  0.02%
    nsv:138:live                92.15M  1       92.13M  16.21K  0.02%
    nsv:169:live                44M     0       43.99M  14.46K  0.03%
    nsv:164:live                31.81M  21      31.52M  294.13K 0.92%
    nsv:157:live                26.96M  0       26.96M  1.5K    0.01%
    nsv:76:live                 23.53M  0       23.53M  1.26K   0.01%
    nsv:146:live                20.59M  0       20.55M  38.33K  0.19%
    nsv:27:live                 13.26M  0       13.26M  801     0.01%
    nsv:50:live                 8.98M   0       8.98M   1.15K   0.01%
    nsv:185:live                8.57M   0       8.28M   288.21K 3.36%
    nsv:184:live                8.02M   0       7.72M   290.49K 3.62%
    nsv:107:live                7.05M   0       7.05M   924     0.01%

One more interesting fact is we see very little number of busy operations, which was in the case of mutex locks on the same server as least by a factor of 1000 higher. So, we can achieve a much higher degree of parallelism using rwlocks. These numbers can be obtained from the updated versions of the nstats module.

One more interesting comparison is potting different kind of operations into relation. The numbers below are in the sense of "Numbers everyone should know" in [1].

As we can see one very simple DB query (0.068ms) costs as much as 500 Tcl variable lookups (130 ns), but is about 70 times faster then an "exec ls /". The operations in the table are either NaviServer primitives, NSF commands, or simple OpenACS commands, from the point of view of an application developer (the exec is over nsproxy).

The results show that the nsv read operation (based on rwlock) is faster than a proc invocation or an "info command", where in the case of the mutex, it is slower.

          86 ns   time {dict get {a 1 b 2 c 3} b} 100000
         130 ns   set x 1; time {info exists x} 100000
         131 ns   set x 1; time {set x} 100000
         140 ns   time {set x 1} 100000
         198 ns   time {ns_quotehtml "hello world"} 100000
         214 ns   set x 1; time {expr {$x + $x}} 100000
         216 ns   nsv_set foo x 1; time {nsv_get foo x} 100000
         248 ns   proc foo {x} {return $x}; time {foo 1} 100000
         273 ns   time {info commands ::db_string} 100000
         313 ns   time {ns_cache_eval ns:memoize 1 {set x 1}} 100000
         319 ns   time {nsv_set foo x 1} 100000
         322 ns   time {array set x {a 1 b 2 c 3}} 100000
         348 ns   time {ns_md5 foo} 100000
         362 ns   time {ns_sha1 foo} 100000
         373 ns   time {lang::util::localize "hello world"} 100000
         485 ns   nx::Class create Foo {:public method bar {} {return 
0};:create ::foo}; time {::foo bar} 100000
         776 ns   time {ad_conn subsite_id} 100000
        1820 ns   time {nx::Object create ::o} 100000
        4104 ns   time {nx::Object new} 100000
        6945 ns   time {parameter::get -package_id [ad_conn subsite_id] -parameter 
DefaultMaster -default "x"} 100000
       25611 ns   time {md5::md5 foo} 100000
       27423 ns   time {sha1::sha1 foo} 100000
       68492 ns   set id 252; time {xo::dc get_value -prepare int qn {select 
title from acs_objects where object_id=:id}} 100000
       90712 ns   time {xo::dc get_value dbqd..qn {select title from 
acs_objects where object_id=252}} 100000
      103241 ns   time {db_string dbqd..qn {select title from acs_objects where 
object_id=252}} 100000
      156529 ns   time {set F [open /tmp/nix w]; puts $F x; close $F} 10000
     4760448 ns   time {exec ls /} 1000

times with mutex locks

         293 ns   nsv_set foo x 1; time {nsv_get foo x} 100000
         354 ns   time {nsv_set foo x 1} 100000

This is a test with very little contention, where the previous tests i've posted were with very high contention. But certainly, applications might be different.

Since we want to have on the longer range binary distributions of NaviServer, i implemented the option (c) from above, such that the decision of using rwlocks or mutex operation can be done (1) at startup time and (2) per server. Also, the documentation of NaviServer is updated.

all the best

-g

[1] https://stackoverflow.com/questions/4087280/approximate-cost-to-access-various-caches-and-main-memory


_______________________________________________
naviserver-devel mailing list
naviserver-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/naviserver-devel

Reply via email to