Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:42670 Return-Path: Mailing-List: contact internals-help@lists.php.net; run by ezmlm Delivered-To: mailing list internals@lists.php.net Received: (qmail 13658 invoked from network); 17 Jan 2009 01:59:30 -0000 Received: from unknown (HELO lists.php.net) (127.0.0.1) by localhost with SMTP; 17 Jan 2009 01:59:30 -0000 Authentication-Results: pb1.pair.com header.from=arvids.godjuks@gmail.com; sender-id=pass; domainkeys=bad Authentication-Results: pb1.pair.com smtp.mail=arvids.godjuks@gmail.com; spf=pass; sender-id=pass Received-SPF: pass (pb1.pair.com: domain gmail.com designates 72.14.220.152 as permitted sender) DomainKey-Status: bad X-DomainKeys: Ecelerity dk_validate implementing draft-delany-domainkeys-base-01 X-PHP-List-Original-Sender: arvids.godjuks@gmail.com X-Host-Fingerprint: 72.14.220.152 fg-out-1718.google.com Received: from [72.14.220.152] ([72.14.220.152:36235] helo=fg-out-1718.google.com) by pb1.pair.com (ecelerity 2.1.1.9-wez r(12769M)) with ESMTP id B6/FC-41390-18B31794 for ; Fri, 16 Jan 2009 20:59:29 -0500 Received: by fg-out-1718.google.com with SMTP id 16so831246fgg.23 for ; Fri, 16 Jan 2009 17:59:26 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=domainkey-signature:mime-version:received:in-reply-to:references :date:message-id:subject:from:to:cc:content-type; bh=UJfdOQ6aWdWa04hKLEJ//CpGgWus8MknRxicltcbc0Y=; b=c+oMXtE3iiOlFG0zYPnGo6ORFjaEtjVyN5p/humrqxc1JZEMi/P8XnmKAK3Ib9YZUS cWN99XhNn6J9QMY0/yqpe3m4lxrI8iMK+7RPQik5s8Yi2gb2n4/CTpE0aYo7Nj2o3GgB W2mDAZqBhU4h1EJsZ85nuWnU99ZQ5OsU67kXk= DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=mime-version:in-reply-to:references:date:message-id:subject:from:to :cc:content-type; b=k+IJ7df0LyRijh6R3YqoAeUyFdyMhAYA9EyXb+CWlEZyNU09oNe83msd7J56uVsbZs bo8/FAJcx5JgsiOn5bS7fwWpTEiiVrh/PIQVt7P0OriXyHUMAx5vONPxkd1lpwaRG432 wyZLp1oLgfn1ohKAF4qfdJF9286ZmAVMdjBzk= MIME-Version: 1.0 Received: by 10.86.84.18 with SMTP id h18mr2324312fgb.69.1232157566261; Fri, 16 Jan 2009 17:59:26 -0800 (PST) In-Reply-To: References: Date: Sat, 17 Jan 2009 03:59:26 +0200 Message-ID: <9b3df6a50901161759q528f2f93u6d1821e73b10b720@mail.gmail.com> To: Timm Friebe Cc: internals@lists.php.net Content-Type: multipart/alternative; boundary=000e0cd250b80b5b7d0460a40a97 Subject: Re: [PHP-DEV] Method call improvements From: arvids.godjuks@gmail.com (Arvids Godjuks) --000e0cd250b80b5b7d0460a40a97 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 7bit Hi Timm! Worth it? I think that's absolute necessity to add this patch, because if it gives almost 2 times speed boost for typical frameworks, it's just fantastic (well, the less calls, the less performance boost, but really some monsters like Zend or CakePHP and others will definitely benefit from it) . I'm definitely going to do some tests with your patch and give feedback here. Thanks for your work! 2009/1/16 Timm Friebe > Hi, > > in every programming language, method calls are expensive. Especially in > PHP, which does not spend any effort during compile time to resolve method > calls their target (and cannot due to the possibility of lazily loading > classes using include w/ variables). I recently did some performance > profiling on exactly how slow method calls are compared to other operation > such as, for example, incrementing an int (the factor is around seven) and > how they compare to compiled languages (the factor lies between 400 and > 1400). > > Here goes the test: > > $instance->method(); > > ...in different variants, using public, private and protected (the latter > are the slowest). On my machine I get about somewhere around 700'000 method > calls per second, while C# scores 250'000'000, for example. Your mileage is > going to vary. > > The difference in these numbers being quite discouraging, I started digging > a bit deeper into how method calls are handled by the Zend Engine. Again, > let's take the example from above, here's what happens (in zend_vm_def.h and > zend_object_handlers.c): > > 1) Finding the execution target > a. $instance is a variable, so we have a zval* > b. if Z_TYPE_P() of this zval is IS_OBJECT, OK. > c. Z_OBJCE_P() will render the zend_class_entry* ce > d. method is a zval*, its zval being a IS_STRING > e. Given ce's function_table, we can lookup the zend_function* > corresponding to the method entry by its (previously lower- > cased!) name > f. If we can't find it and the ce has a __call, go for that, > else zend_error() > > 2) Verifying it > a. If the modifiers are PUBLIC, OK. > b. If they're private, verify EG(scope) == ce. If they match, > OK, if not, try for ce->__call, if that doesn't exist, error. > c. If they're protected, verify instanceof_function(ce, EG(scope)) > If that returns FAILURE, try ce->__call, if that doesn't exist, > error. If it exists, OK. > > 3) Insurance > a. Finally test if the zend_function* found is neither abstract > nor deprecated. > b. Test non-static methods aren't called statically, else issue > a warning (or error, depending on the situation). > > 4) Execute > a. Take EX(function_state).function->op_array and zend_execute() > it. > > You can clearly see the checks in #1 and #2 (most of which happens in > zend_std_get_method())are quite extensive. Now the idea I developed was to > cache this information and I thus came up with the following: > > * At [1d], calculate a hash key for the following: > - method->name > - ce->name > - EG(scope) ? EG(scope)->name : "" > These are the only variables used for verifying scope and > modifiers, and the verification is always going to yield the > same result as long as the stay the same. > > * Look this up in a hashtable (in generic-speak: > HashTable). If found, return that, > continue with [1e] otherwise. > > * After [2c], store the found zend_function* to the hash. > > I was curious how this would affect overall performance, both in synthetic > and in real-world situations. The first tests I ran were something along the > lines of: > > for ($i= 0; $i < $times; $i++) { > $instance->method(); > } > > ...with and without the patch - this gave me a factor of 1.7 to 1.8 (times > the PHP I built with the patch was faster)! The real-world situation was > running the test suite of an object-oriented PHP framework, taking 1.55 > seconds before and 0.91 after. I would call this good, almost doubling the > speed. Of course this is nowhere near the factors I mentioned before but I > think this has potential. Of course, caching comes at a cost, but by using a > numeric key instead of a string I could reduce the overhead to a minimum, > the real-world application consuming about 20 KB more memory, which I'd call > negligible. > > Last but not least I verified I hadn't utterly broken the way PHP works by > running the tests from Zend/tests and found no test where failing with the > patch that weren't already failing without it (some of them expected, some > not). > > The simple idea is a ~50 line patch intended for the PHP_5_3 branch and > available at the following location: > > http://sitten-polizei.de/php/method-call-cache.diff > > It serves its purpose quite well in CLI sapi and would definitive fixing up > for it to go into production (parts of it belong to zend_hash.c, and the > cache variable needs to be an EG() instead of static). > > I'm interested in your opinions and if you think its addition would be > worth a try. > > - Timm > > > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: http://www.php.net/unsub.php > > --000e0cd250b80b5b7d0460a40a97--