Skip to content

random: Optimize data flow for the generate function of native engines #13043

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 9, 2024

Conversation

TimWolla
Copy link
Member

Instead of returning the generated uint64_t and providing the size (i.e. the number of bytes of the generated value) out-of-band via the last_generated_size member of the php_random_status struct, the generate function is now expected to return a new php_random_result struct containing both the size and the result.

This has two benefits, one for the developer:

It's no longer possible to forget setting last_generated_size to the correct value, because it now happens at the time of returning from the function.

and the other benefit is for performance:

The php_random_result struct will be returned as a register pair, thus the size will be directly available without reloading it from main memory.

Checking a simplified version of php_random_range64() on Compiler Explorer (“Godbolt”) with clang 17 shows a single change in the resulting assembly showcasing the improvement (https://godbolt.org/z/G4WjdYxqx):

- add     rbp, qword ptr [r14]
+ add     rbp, rdx

Empirical testing confirms a measurable performance increase for the Randomizer::getBytes() method:

<?php
$e = new Random\Engine\Xoshiro256StarStar(0);
$r = new Random\Randomizer($e);

var_dump(strlen($r->getBytes(100000000)));

goes from 250ms (before the change) to 220ms (after the change). While generating 100 MB of random data certainly is not the most common use case, it confirms the theoretical improvement in practice.

Instead of returning the generated `uint64_t` and providing the size (i.e. the
number of bytes of the generated value) out-of-band via the
`last_generated_size` member of the `php_random_status` struct, the `generate`
function is now expected to return a new `php_random_result` struct containing
both the `size` and the `result`.

This has two benefits, one for the developer:

It's no longer possible to forget setting `last_generated_size` to the correct
value, because it now happens at the time of returning from the function.

and the other benefit is for performance:

The `php_random_result` struct will be returned as a register pair, thus the
`size` will be directly available without reloading it from main memory.

Checking a simplified version of `php_random_range64()` on Compiler Explorer
(“Godbolt”) with clang 17 shows a single change in the resulting assembly
showcasing the improvement (https://godbolt.org/z/G4WjdYxqx):

    - add     rbp, qword ptr [r14]
    + add     rbp, rdx

Empirical testing confirms a measurable performance increase for the
`Randomizer::getBytes()` method:

    <?php
    $e = new Random\Engine\Xoshiro256StarStar(0);
    $r = new Random\Randomizer($e);

    var_dump(strlen($r->getBytes(100000000)));

goes from 250ms (before the change) to 220ms (after the change). While
generating 100 MB of random data certainly is not the most common use case, it
confirms the theoretical improvement in practice.
@TimWolla TimWolla force-pushed the random-generate-size branch from cb883b6 to ba89f9b Compare December 28, 2023 23:02
Copy link
Member

@Girgias Girgias left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes sense to me, but will let other people have a look too

@zeriyoshi
Copy link
Contributor

Sorry for the delay in checking, but I think it is good!
I will check it in detail tomorrow.

Copy link
Contributor

@zeriyoshi zeriyoshi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great! Thank you!

Copy link
Member

@iluuu1994 iluuu1994 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM as well.

@@ -218,11 +217,15 @@ typedef struct _php_random_status_state_user {
zend_function *generate_method;
} php_random_status_state_user;

typedef struct _php_random_result {
const uint64_t result;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: The order of all initializers is inversed, so maybe inverse it here too?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've initially had it reversed, but this order minimizes the differences in the generated assembly, because .result stays in rax (with .size being in rdx, instead of vice versa).

@TimWolla TimWolla merged commit 162e1dc into php:master Jan 9, 2024
@TimWolla TimWolla deleted the random-generate-size branch January 9, 2024 18:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants