Skip to content

[RFC] opcache.allow_cache=0: Opcode optimization without caching #5504

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

Closed
wants to merge 3 commits into from

Conversation

TysonAndre
Copy link
Contributor

@TysonAndre TysonAndre commented May 1, 2020

RFC: https://wiki.php.net/rfc/opcache.no_cache

Currently, it isn't possible to enable optimizations without enabling caching.
They should be orthogonal features - it's already possible to cache without
optimization passes (opcache.optimization_level=0)

(In use cases such as running a web server in apache,
it's very desirable to both cache and optimize opcodes.
For short-lived cli scripts, either file_cache or disabling opcache
(to reduce optimization overhead) is often useful.
In a few use cases, it's desirable to optimize without any caching)

Being forced to either enable shared memory caching or file_cache_only
causes these problems:

  • For file_cache_only, users would be forced to manage the file cache.

    • Users may not be confident in properly handling all potential edge cases.
    • End users of applications may not be familiar with opcache.

    (Concerns include running out of disk space, needing to clear stale entries,
    concerns about opcode corruption or opcodes for the wrong file contents
    not being fixed after restarting a process, etc)

  • The shared memory block uses up a lot of RAM when individual PHP processes
    are known to be long-lived and don't have a common memory address space.

    (e.g. multiple long-lived php scripts managed by something that is not
    a php script (e.g. supervisord, daemons, tools run in the background in
    IDEs, etc))

    The opcodes are duplicated in shmem, which wastes memory in use cases
    where opcodes are stored in the cache but never retrieved.

    The default for opcache.memory_consumption is 128M.
    Even when this isn't used, the virtual memory to track the shared
    memory(shmem) segment seems to add 2MB extra per independent php process in
    "shared memory" segments, reducing the free RAM available for other processes.

    (starting a large number of php CLI scripts that sleep() in a loop,
    free (linux program to report free memory) reports that shared
    increases by 2MB per process without opcache.no_cache, but barely increases
    with opcache.no_cache=1

opcache.no_cache takes precedence over opcache.file_cache* settings.
(If enabled, neither the shared memory cache nor the file cache will be used.
It is an error to use both opcache.allow_cache=0 and opcache.preload.)

On an unrelated PR #5097 (comment)
dstogov mentioned that

Also, it would be great to move optimizer and JIT into core, to make them
available even without opcode caching.

This PR is one potential approach for making the optimizer and JIT available
without opcode caching.

  • Even if everything except caching was moved into core,
    It would still be useful to have a configurable way to disable opcode caching
    via a system ini setting (php -d opcache.allow_cache=0 my_script.php)

Feel free to point out combinations of settings that would be errors

Potential areas for future work:

  • Normally, opcache optimizes a file based only on that one file's contents.
    When opcache.allow_cache=0 is used, it may be possible to use all of the
    class, function, constant, etc. definitions parsed from previously parsed
    files (to eliminate dead code, inline function calls, etc).

RFC Changelog:
0.2: Change from opcache.no_cache=1 to opcache.allow_cache=0 to avoid double negatives in ini settings

@nikic
Copy link
Member

nikic commented May 1, 2020

I like the idea of having optimization without caching, but the way to go about this is definitely moving the optimizer into Zend and making it available completely independently of the opcache extension. This has been "planned" for a long time, but never actually happened.

@TysonAndre
Copy link
Contributor Author

TysonAndre commented May 1, 2020

I like the idea of having optimization without caching, but the way to go about this is definitely moving the optimizer into Zend and making it available completely independently of the opcache extension. This has been "planned" for a long time, but never actually happened.

I'd agree that it would be more reasonable to have the optimizer bundled with php, especially since I'm not aware of competing optimizers for php 7/8.
(e.g. it would be bad for usability to have optimizers in other languages separate from the compiler and invoke them like gcc|clang -plugin=Optimizer or java -cp NotARealBytecodeOptimizer programToRun.jar)

I'm also concerned about it staying "planned", since

  • The developers frequently working on the optimizer may prioritize bug fixes and improvements to opcache over refactorings unless the refactoring was necessary for improvements to php optimizations to be possible, or showed performance improvements
  • Users and voters might not see a tangible benefit from an RFC to refactor it and be averse to changing it (unless it enables improvements that were previously possible)

Also, what is the plan for this?

  1. Always-on opcache extension? (possibly have a ./configure flag to expose APIs such as opcache_get_status() but prevent optimizing altogether if there is a strong use case, such as shared hosting providers not wanting the functionality, WebAssembly builds, etc)
  2. Keep zend_extension=opcache as a separate shared library (but limit it to only caching and not optimizing). Make the opcache_optimizer an always-enabled statically-linked zend_extension (or just move optimization passes from ext/opcache into Zend/opcache or something along those lines) (I don't think that's been done before for zend_extension, so I don't know if there are any blockers for this or the above approach)

What would happen to current ini flags?

It would be useful to continue supporting opcache.optimization_level and opcache.enable_cli and opcache.file_cache=/path/to/folder to make migration easier.

So if we kept that prefix for other optimization/caching settings when moving the optimizer into Zend, I'd assume that this might end up with the name opcache.no_cache anyways, so I don't know if refactoring opcache should block supporting opcache.no_cache

  • If a different name for this ini setting was chosen, the old name could be deprecated
  • The opcache.no_cache setting can easily be used before and after such a refactoring, providing the benefits I mentioned for the use cases in the PR description.

@TysonAndre
Copy link
Contributor Author

The shared memory block uses up a lot of RAM when individual PHP processes
are known to be long-lived and don't have a common memory address space.
(e.g. multiple long-lived php scripts managed by something that is not
a php script (e.g. supervisord, daemons, tools run in the background in
IDEs, etc))

Aside, unrelated to this proposal: If opcache.file_cache_only was available, I wonder if opcache.file_cache=/dev/shm/some_folder_owned_by_that_user and opcache.file_cache_only=1 would work on Unix/Linux to share that memory space. (That would have the same issues with needing to clean up entries in /dev/shm/some_folder_owned_by_that_user)

  • This would only help in situations where disk space was low but there was normally a lot of available memory
  • That is not cross-platform

@TysonAndre TysonAndre force-pushed the opcache.no_cache branch 2 times, most recently from d7ce79f to efeaf13 Compare May 16, 2020 14:07
@TysonAndre TysonAndre changed the title [Proposal] opcache.no_cache: Opcode optimization without caching [RFC] opcache.no_cache: Opcode optimization without caching May 16, 2020
Currently, it isn't possible to enable optimizations without enabling caching.
They should be orthogonal features - it's already possible to cache without
optimization passes (`opcache.optimization_level=0`)

(In use cases such as running a web server in apache,
it's very desirable to both cache and optimize opcodes.
For short-lived cli scripts, either file_cache or disabling opcache
(to reduce optimization overhead) is often useful.
In a few use cases, it's desirable to optimize without any caching)

Being forced to either enable shared memory caching or file_cache_only
causes these problems:

- **For file_cache_only, users would be forced to manage the file cache.**

  - Users may not be confident in properly handling all potential edge cases.
  - End users of applications may not be familiar with opcache.

  (Concerns include running out of disk space, needing to clear stale entries,
  concerns about opcode corruption or opcodes for the wrong file contents
  not being fixed after restarting a process, etc)
- **The shared memory block uses up a lot of RAM when individual PHP processes
  are known to be long-lived and don't have a common memory address space.**
  (e.g. multiple long-lived php scripts managed by something that is not
  a php script (e.g. `supervisord`, daemons, tools run in the background in
  IDEs, etc))

  The opcodes are duplicated in shmem, which wastes memory in use cases
  where opcodes are stored in the cache but never retrieved.

  The default for opcache.memory_consumption is 128M.
  Even when this isn't used, the virtual memory to track the shared
  memory(shmem) segment seems to add 2MB extra per **independent** php process in
  "shared memory" segments, reducing the free RAM available for other processes.

  (starting a large number of php CLI scripts that sleep() in a loop,
  `free` (linux program to report free memory) reports that `shared`
  increases by 2MB per process without opcache.no_cache, but barely increases
  with opcache.no_cache=1

`opcache.no_cache` takes precedence over `opcache.file_cache*` settings.
(If enabled, neither the shared memory cache nor the file cache will be used.
It is an error to use both opcache.no_cache and opcache.preload.)

On an unrelated PR php#5097 (comment)
dstogov mentioned that

> Also, it would be great to move optimizer and JIT into core, to make them
> available even without opcode caching.

This PR is one potential approach for making the optimizer and JIT available
without opcode caching.

- Even if everything except caching was moved into core,
  It would still be useful to have a configurable way to disable opcode caching
  via a system ini setting (`php -d opcache.no_cache=1 my_script.php`)

This is currently a proof of concept - feel free to point out combinations of
settings that should be rejected.

Potential areas for future work:

- Normally, opcache optimizes a file based only on that one file's contents.
  When `opcache.no_cache=1` is used, it may be possible to use all of the
  class, function, constant, etc. definitions parsed from previously parsed
  files (to eliminate dead code, inline function calls, etc).
@TysonAndre TysonAndre changed the title [RFC] opcache.no_cache: Opcode optimization without caching [RFC] opcache.allow_cache=0: Opcode optimization without caching May 20, 2020
@kocsismate kocsismate added the RFC label May 20, 2020
@TysonAndre
Copy link
Contributor Author

I forgot to close this after the RFC vote was declined.

https://wiki.php.net/rfc/opcache.no_cache#move_optimizer_and_jit_into_core_instead seems promising according to the poll, but would require a lot of refactoring and deep familiarity with how opcache is used/meant to be used in NTS, ZTS (threaded), etc, and some design work on making the APIs continue to work with tooling that conflicts with opcache or requires that optimizations be disabled.

@TysonAndre TysonAndre closed this Aug 9, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants