Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

PHP Performance Trivia

3,424 views

Published on

This talk discusses various issues of low-level PHP performance, such as: When is it more efficient to use arrays or objects? What causes catastrophic garbage collection? Does adding type annotations make PHP faster or slower?

I will answer these types of question with a (shallow) dive into PHP internals, touching on various topics like value representation, bytecode optimization and GC.

Published in: Technology

PHP Performance Trivia

  1. 1. PHP Performance Trivia Nikita Popov
  2. 2. Opcache
  3. 3. Opcache Method Opcodes Class Method Shared Memory
  4. 4. Opcache Method Opcodes Class Method Method Class Method Shared Memory Per-Request Arena copy
  5. 5. Opcache Method Opcodes Class Method Method Class Method Shared Memory Per-Request Arena copy Modified during inheritance
  6. 6. Opcache Method Opcodes Class Method Method Class Method Shared Memory Per-Request Arena copy Modified during inheritance Registered in class table
  7. 7. Preloading Method Opcodes Class Method Shared Memory
  8. 8. Preloading Method Opcodes Class Method Shared Memory MAP area Static properties Runtime cache
  9. 9. Preloading Method Opcodes Class Method Shared Memory MAP area Static properties Runtime cache Cleared on each request
  10. 10. Preloading Method Opcodes Class Method Shared Memory ● Classes must be fully inherited!
  11. 11. Preloading Method Opcodes Class Method Shared Memory ● Classes must be fully inherited! ● Parents/interfaces known ● All constant expressions known ● All-ish types known
  12. 12. Preloading Method Opcodes Class Method Shared Memory ● Classes must be fully inherited! ● Parents/interfaces known ● All constant expressions known ● All-ish types known ● Windows: lost cause (ASLR), internal classes not "known"
  13. 13. Preloading Method Opcodes Class Method Shared Memory ● No way to clear preload state ● Opcache reset not enough ● FPM reload not enough
  14. 14. Value Caching ● Only about completely static data here… – Say a composer class map
  15. 15. Value Caching PHP PHP PHP Opcache SHM APCU SHM
  16. 16. Value Caching PHP PHP PHP Opcache SHM APCU SHM Direct accessRequires copying!
  17. 17. Value Caching PHP PHP PHP Opcache SHM APCU SHM Direct accessRequires copying! Data never removed (*)Supports deletion
  18. 18. Value Caching ● Only about completely static data here… – Say a composer class map ● APCU very inefficient for large data – Requires unserialization and copying ● (Ab)use opcache as a data cache
  19. 19. Value Caching <?php return [ "foo" => "bar", "bar" => "baz", ]; Array stored as "immutable array" in shared memory
  20. 20. Opcache Reset ● Invalidated files remain in opcache ● Only cleared on full reset
  21. 21. Opcache Reset PHP PHP PHP Opcache SHM Wait for requests to finish
  22. 22. Opcache Reset PHP PHP PHP Opcache SHM Wait for requests to finish
  23. 23. Opcache Reset PHP PHP PHP Opcache SHM Wait for requests to finish
  24. 24. Opcache Reset PHP PHP PHP Opcache SHM Wait for requests to finish SIGKILL
  25. 25. Opcache Reset PHP PHP Opcache SHM Wait for requests to finish
  26. 26. Opcache Reset PHP PHP Opcache SHM Clear
  27. 27. Opcache Reset PHP PHP Opcache SHM
  28. 28. Opcache Reset ● Cache not used during reset ● Needs to be repopulated from scratch ● File cache can help mitigate
  29. 29. Arrays vs Objects ● Array: ["first" => $a, "second" => $b] ● Packed array: [$a, $b] ● Object with declared properties – class Test { public $first, $second; } ● Object with dynamic properties – (object)["first"=>$a, "second"=>$b]
  30. 30. Memory Usage 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 0 100 200 300 400 500 600 700 800 Array Array (packed) Object Number of properties/keys Memoryusage(bytes)
  31. 31. Memory Usage 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 0 100 200 300 400 500 600 700 800 Array Array (packed) Object Number of properties/keys Memoryusage(bytes) ["first" => $a, "second" => $b] [$a, $b] class Pair { public $first, $second; }
  32. 32. Memory Usage (Ratio) 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 0 1 2 3 4 5 6 7 Ratio Ratio (packed) Number of properties/keys Arraysize/objectsize
  33. 33. Caveat: Properties table ● Some operations materialize properties table – foreach ($object as $propName => $value) – (array) $object – var_dump($object) – …
  34. 34. Caveat: Properties table ● Some operations materialize properties table – foreach ($object as $propName => $value) – (array) $object – var_dump($object) – … ● You pay the price for both object and array ● No way to remove once created
  35. 35. Memory Usage 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 0 100 200 300 400 500 600 700 800 Array Array (packed) Object Object (dyn) Number of properties/keys Memoryusage(bytes)
  36. 36. Garbage Collection
  37. 37. Garbage Collection ● Reference Counting – Count how many times a value is used – Destroy when count is zero
  38. 38. Garbage Collection ● Reference Counting – Count how many times a value is used – Destroy when count is zero $x = "foobar"; // refcount=1 $y = $x; // refcount=2 unset($x); // refcount=1 unset($y); // refcount=0 ==> Destroy!
  39. 39. Garbage Collection ● Reference Counting – Count how many times a value is used – Destroy when count is zero $x = []; // refcount=1 $x[0] =& $x; // refcount=2 unset($x); // refcount=1 // Will never reach 0 due to cycle!
  40. 40. Garbage Collection ● Cycle Collector – Mark & sweep algorithm
  41. 41. Garbage Collection ● Cycle Collector – Mark & sweep algorithm ● Start from "roots" ● Simulate what would happen if they were released ● If simulation results in refcount=0, actually destroy
  42. 42. Garbage Collection ● Cycle Collector – Mark & sweep algorithm – PHP <= 7.2: Fixed root buffer with 10000 entries ● 10000 objects should be enough for everyone!
  43. 43. Garbage Collection ● Cycle Collector – Mark & sweep algorithm – PHP <= 7.2: Fixed root buffer with 10000 entries ● 10000 objects should be enough for everyone! ● Cycle collector runs every time root buffer full ● May walk graph with millions of objects each time
  44. 44. Composer + GC Too many memes
  45. 45. Composer + GC commit ac676f47f7bbc619678a29deae097b6b0710b799 Author: Jordi Boggiano <j.boggiano@seld.be> Date: Tue Dec 2 10:23:21 2014 +0000 Disable GC when computing deps, refs #3482 diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index b76155a5..1b2a6772 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -160,6 +160,8 @@ public function __construct(... */ public function run() { + gc_disable(); + if ($this->dryRun) { $this->verbose = true; $this->runScripts = false;
  46. 46. Composer + GC commit ac676f47f7bbc619678a29deae097b6b0710b799 Author: Jordi Boggiano <j.boggiano@seld.be> Date: Tue Dec 2 10:23:21 2014 +0000 Disable GC when computing deps, refs #3482 diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index b76155a5..1b2a6772 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -160,6 +160,8 @@ public function __construct(... */ public function run() { + gc_disable(); + if ($this->dryRun) { $this->verbose = true; $this->runScripts = false; 2x speedup
  47. 47. Garbage Collection ● Cycle Collector – Mark & sweep algorithm – PHP <= 7.2: Fixed root buffer with 10000 entries – PHP >= 7.3: Dynamic root buffer ● Root buffer automatically grows ● Dynamic GC threshold ● If GC collects little garbage, GC threshold grows
  48. 48. Type Declarations ● Do they make PHP slower or faster?
  49. 49. Type Declarations ● Do they make PHP slower or faster? ● Type declarations need to be checked ● Type declarations allow more optimizations
  50. 50. Type Optimization <?php function powi(float $base, int $power): float { if ($power == 0) return 1.0; $result = $base; for ($i = 1; $i < $power; $i++) { $result *= $base; } return $result; }
  51. 51. Type Optimization <?php function powi(float $base, int $power): float { if ($power == 0) return 1.0; $result = $base; for ($i = 1; $i < $power; $i++) { $result *= $base; } return $result; } Type inference: int Type inference: float
  52. 52. Type Optimization <?php function powi(float $base, int $power): float { if ($power == 0) return 1.0; $result = $base; for ($i = 1; $i < $power; $i++) { $result *= $base; } return $result; } Type inference: int Type inference: float → Eliminated return type check
  53. 53. Type Optimization <?php function powi(float $base, int $power): float { if ($power == 0) return 1.0; $result = $base; for ($i = 1; $i < $power; $i++) { $result *= $base; } return $result; } Use specialized ZEND_PRE_INC_LONG Use specialized ZEND_MUL_DOUBLE + eliminate compound operation Use specialized ZEND_IS_SMALLER_LONG
  54. 54. Type Optimization <?php function powi(float $base, int $power): float { if ($power == 0) return 1.0; $result = $base; for ($i = 1; $i < $power; $i++) { $result *= $base; } return $result; } Type inference: int Type inference: float
  55. 55. Type Optimization <?php function powi( $base, $power) { if ($power == 0) return 1.0; $result = $base; for ($i = 1; $i < $power; $i++) { $result *= $base; } return $result; } Type inference: int|float Type inference: mixed
  56. 56. Type Optimization ● For this example: – Without opcache: Performance ~same with and without types – With opcache: With types 2.5x faster – Type check cost: Once – Type optimization benefit: Multiple loop iterations
  57. 57. Type Optimization ● For this example: – Without opcache: Performance ~same with and without types – With opcache: With types 2.5x faster – Type check cost: Once – Type optimization benefit: Multiple loop iterations ● But: Does not happen often in practice.
  58. 58. Global Namespace Fallback <?php namespace Foo; var_dump(strlen("foobar")); // Might be strlen() // Might be Foostrlen()
  59. 59. Global Namespace Fallback <?php namespace Foo; var_dump(strlen("foobar")); // Might be strlen() // Might be Foostrlen() Actual function cached on first call → Not particularly expensive
  60. 60. Specialized Functions ● Some functions have optimized VM instruction – strlen() and count() – is_null() etc – intval() etc – defined() – call_user_func() and call_user_func_array() – in_array() and array_key_exists() – get_class(), get_called_class() and gettype() – func_num_args() and func_get_args()
  61. 61. Specialized Functions ● Some functions have optimized VM instruction ● Can only be used if function known ● Requires fully qualified name or "use function"
  62. 62. Compile-time evaluation <?php namespace Foo; function doSomething() { if (version_compare(PHP_VERSION, '7.3', '>=')) { // PHP 7.3 implementation } else { // Fallback implementation } } Can't evaluate due to namespace fallback
  63. 63. Compile-time evaluation <?php namespace Foo; function doSomething() { if (version_compare(PHP_VERSION, '7.3', '>=')) { // PHP 7.3 implementation } else { // Fallback implementation } } Evaluated to true/false by opcache
  64. 64. Compile-time evaluation <?php namespace Foo; use function version_compare; use const PHP_VERSION; function doSomething() { if (version_compare(PHP_VERSION, '7.3', '>=')) { // PHP 7.3 implementation } else { // Fallback implementation } } Evaluated to true/false by opcache
  65. 65. Thank You!

×