Hi, On non x86_64 platforms current implementation of safe_address uses doubles for 64 bit php which is very expensive on some architectures e.g sparc.
safe_address function calculates nmemb * size + offset and it want to make sure that it output doesn't overflow. My colleague Richard Smith suggested a platform independent mechanism to avoid double logic for most common cases (>99%). In this proposed optimization we can check if the inputs are sufficiently large enough so that overflow is possible. If overflow is possible, regular double logic will follow. This new implementation performs very closely to the assembly version on Linux x86_64 on micro-benchmark (attached at bottom). On a ecommerce benchmark on Solaris sparc, new implementation reduced the time spent in _ecalloc to 50%. Patch against php 5.3 trunk is attached. Please provide your comments. Regards, Basant. Microbenchmark testing on Linux x86_64 : ======================================= Based on the above suggested implementation, I benchmarked 3 implementation of safe_address on Linux x86_64 (fedora 11) using a micro benchmark. Here are the results : -------------------------------------- For a regular nmemb * size + offset when no overflow possible : nmemb = 23456 size = 67890 offset = 12345 assembly version of safe_address time diff = 26 ticks doubles imlementation of safe_address time diff = 72 ticks proposed safe_address time diff = 30 ticks -------------------------------------- For bigger integer multiplication : nmemb = 2589934591 size = 4294967295 offset = 5 assembly version of safe_address time diff = 26 ticks doubles imlementation of safe_address time diff = 75 ticks proposed safe_address time diff = 76 ticks -------------------------------------- Here is the micro benchmark link : http://bitbucket.org/basantk/php53perfpatches/src/tip/safe_address_bench.c
Index: Zend/zend_alloc.c =================================================================== --- Zend/zend_alloc.c (revision 291181) +++ Zend/zend_alloc.c (working copy) @@ -2382,9 +2382,26 @@ static inline size_t safe_address(size_t nmemb, size_t size, size_t offset) { size_t res = nmemb * size + offset; - double _d = (double)nmemb * (double)size + (double)offset; - double _delta = (double)res - _d; + double _d, _delta; +#if SIZEOF_SIZE_T == 8 + const int nmemb_bits = 30; + const int size_bits = 33; + + /* Check that arguments are within defined ranges such that overflow can't + * occur. The basic principle is that if nmemb*size < 2^63 and offset < + * 2^63 then nmemb*size+offset < 2^64. For efficency, powers of 2 have been + * used for each range. nmemb_bits + size_bits should not exceed 63. The + * test below checks that each argument doesn't exceed its allotted range. + */ + if (EXPECTED(!(nmemb >> nmemb_bits | size >> size_bits + | offset >> (nmemb_bits + size_bits)))) + return res; +#endif + + _d = (double)nmemb * (double)size + (double)offset; + _delta = (double)res - _d; + if (UNEXPECTED((_d + _delta ) != _d)) { zend_error_noreturn(E_ERROR, "Possible integer overflow in memory allocation (%zu * %zu + %zu)", nmemb, size, offset); return 0;
-- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php