PHP built from source performs much better than the Ubuntu packaged version
Affects | Status | Importance | Assigned to | Milestone | |
---|---|---|---|---|---|
php7.4 (Ubuntu) |
Invalid
|
Medium
|
Unassigned | ||
Focal |
Fix Released
|
Undecided
|
Athos Ribeiro | ||
php8.1 (Ubuntu) |
Fix Released
|
Medium
|
Athos Ribeiro | ||
Jammy |
Fix Released
|
Undecided
|
Athos Ribeiro |
Bug Description
[Impact]
PHP has a compilation option to use x86_64 v2 and v3 capabilities for some of its operations, when these capabilities are available during runtime. This compilation option is enabled by default, but due to a bug in an old version of a GCC macro embedded in the PHP code, this option is disabled when "-Wall" is present in CFLAGS.
Since PHP is compiled with "-Wall" in Ubuntu, but the upstream shipped binaries (including the ones in docker images) are not, users of modern x86_64 machines (with v2 or v3 capabilities) will perceive huge performance differences in some operations (such as base64 encoding/decoding) when running Ubuntu's PHP vs PHP distributions not compiled with "-Wall" (e.g., upstream docker images).
https:/
[Test Plan]
1) Verify the fix:
On a x86_64 machine with v2/v3 capabilities, run the following script with a version of PHP without the fix (jammy or focal)
# BEGIN
<?php
$time_start = microtime(true);
$test_string = 'Amet in elit incididunt qui qui sint consectetur do eiusmod eiusmod voluptate voluptate adipisicing aute. Mollit consectetur adipisicing ad nostrud duis voluptate in non. Culpa reprehenderit et laboris deserunt aliqua ut. Adipisicing ad aute ullamco reprehenderit ex amet occaecat eiusmod sit sint eiusmod ad aute eiusmod. Commodo dolore exercitation culpa do ad consectetur est adipisicing aliquip. In fugiat adipisicing culpa elit ad culpa fugiat.';
$about_one_sec = 200000;
$about_5_sec = $about_one_sec * 5;
for ($i=0; $i < $about_5_sec; $i++) {
$_ = base64_
$_ = base64_decode($_);
}
$time_end = microtime(true);
$time = $time_end - $time_start;
echo $time;
die();
# END
Then, apply the fix and re-run the script.
The time it takes to run should have been greatly reduced (up to 10x from the perceived experimentation discussed in this bug).
2) Ensure no regressions are introduced in x86_64 v1 machines:
Run the same test above (from step 1) on a x86_64 v1 machine using an affected package, i.e., no fix applied (you can use a vm, e.g., virt-manager provides a menu with several CPUs to pick from, or actual v1 hardware). Make sure to register the time in the script output.
Apply the fix and re-run the test. Verify that it does not trigger an error (successful output) and that the script output (time) is in the same order of magnitude of the previous run (without the fix).
You can also run perf to verify that the test is calling php_base64_
[Where problems could occur]
This patch will have PHP use codepaths that have been available in PHP for years, but were not used in known distributions (we verified, Ubuntu, Debian, Fedora, and CentOS). This could have 2 different side effects:
1) bugs in those codepaths, which may not have been exhaustively tested in production environments before, may be discovered. In these cases, we will need to work with upstream to get them fixed.
2) while there are, as far as we could examine and test, fallbacks in place for whenever the x86_64 v2 or v3 capabilities are not available for a certain operation (i.e., user is running on a x86_64 v1 machine), if there are any codepaths where a fallback is not triggered or is not available, PHP will crash and we will need to either work with upstream to fix that specific codepath, or revert this patch until we find a solution, which may be non-trivial.
[Other Info]
The fix is already available in kinetic. It was forwarded to Debian and accepted, and it was also accepted upstream, but was later reverted because it introduced issues in the MacOS builds due to linux specific codepaths being triggered due to the GCC macro fix.
[ Original message ]
When you compare the performance of PHP installed using this package with PHP built from source (as it is in the official PHP Docker image) there is a pretty substantial performance difference. It is not specific to Docker but using Docker is the easiest way to test it.
When the attached script is served using php-fpm built from source it performs 10 times better than using php-fpm installed via this package.
Related branches
- git-ubuntu bot: Approve
- Bryce Harrington (community): Approve
- Canonical Server Reporter: Pending requested
-
Diff: 59 lines (+37/-0)3 files modifieddebian/changelog (+7/-0)
debian/patches/0049-Update-gcc-func-attr-macro.patch (+29/-0)
debian/patches/series (+1/-0)
- git-ubuntu bot: Approve
- Bryce Harrington (community): Approve
- Canonical Server Reporter: Pending requested
-
Diff: 62 lines (+37/-0)3 files modifieddebian/changelog (+7/-0)
debian/patches/0047-Update-gcc-func-attr-macro.patch (+29/-0)
debian/patches/series (+1/-0)
- Bryce Harrington (community): Approve
- Canonical Server: Pending requested
-
Diff: 101 lines (+51/-0)4 files modifieddebian/changelog (+8/-0)
debian/patches/0046-Update-gcc-func-attr-macro.patch (+29/-0)
debian/patches/series (+1/-0)
debian/rules (+13/-0)
Changed in php7.4 (Ubuntu): | |
assignee: | nobody → Athos Ribeiro (athos-ribeiro) |
Changed in php8.1 (Ubuntu): | |
assignee: | nobody → Athos Ribeiro (athos-ribeiro) |
tags: | added: server-todo |
Changed in php7.4 (Ubuntu): | |
status: | Triaged → Invalid |
assignee: | Athos Ribeiro (athos-ribeiro) → nobody |
Changed in php7.4 (Ubuntu Focal): | |
assignee: | nobody → Athos Ribeiro (athos-ribeiro) |
Changed in php8.1 (Ubuntu Jammy): | |
assignee: | nobody → Athos Ribeiro (athos-ribeiro) |
Changed in php7.4 (Ubuntu Focal): | |
status: | New → Triaged |
Changed in php8.1 (Ubuntu Jammy): | |
status: | New → Triaged |
description: | updated |
description: | updated |
Changed in php7.4 (Ubuntu Focal): | |
status: | Triaged → In Progress |
Changed in php8.1 (Ubuntu Jammy): | |
status: | Triaged → In Progress |
Hi Dustin and thanks for this bug report. Benchmarking is difficult and repeating one single operation several time is often not a good performance indicator, however I agree we shouldn't see such a huge difference. I can reproduce it too as follows:
1. Launch a Focal LXD container
2. apt install php
3. Run `php test.php` (your test script, unmodified). On my machine this it runs in about 1.5s.
Now in docker:
4. mkdir php-perf-test, copy test.php in this directory and create a Dockerfile containing the following:
FROM php:7.4-cli
COPY . /tmp/php
WORKDIR /tmp/php
CMD [ "php", "./test.php" ]
5. Run: `docker build -t my-php-app .`
6. Run: `docker run -it --rm --name my-running-app my-php-app`. The test script runs in about 0.25 in this case, on the same machine.
Version info for the Ubuntu packaged PHP:
$ apt policy php7.4
php7.4:
Installed: 7.4.3-4ubuntu2.2
$ php --version
PHP 7.4.3 (cli) (built: May 26 2020 12:24:22) ( NTS )
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies
with Zend OPcache v7.4.3, Copyright (c), by Zend Technologies
while the Docker version is:
$ docker run -it --rm --name my-running-app my-php-app php --version
PHP 7.4.6 (cli) (built: May 15 2020 12:47:30) ( NTS )
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies
This is worth some more investigation.