perf(compiler): skip caches for variable method names

master
韩天峰 2 days ago
parent b69eb1dee6
commit 681c7d42e7
  1. 1
      phpunit/code/call-cache-sites.php
  2. 11
      phpunit/src/CallCacheCodegenTest.php
  3. 15
      phpunit/src/SymbolTest.php
  4. 15
      src/Generator/Symbol.php
  5. 19
      src/Parser/MethodCallTrait.php
  6. 9
      src/Parser/NullsafeAccessTrait.php
  7. 39
      tests/compiler/dynamic_call/call-cache-dispatch.phpt

@ -12,6 +12,7 @@ function call_cache_sites(mixed $callback, object $object, ?object $nullable, mi
$object->$method(2),
$object->fixedMethod(3),
$nullable?->nullableMethod(4),
$nullable?->$method(5),
];
}

@ -28,16 +28,17 @@ final class CallCacheCodegenTest extends BaseTest
self::assertSame(1, substr_count($code, 'typephp_call_cached('));
self::assertSame(3, substr_count($code, 'php::callStaticMethod('));
self::assertStringNotContainsString('php::concat({', $code);
self::assertSame(7, substr_count($code, 'php::VarList{'));
self::assertSame(8, substr_count($code, 'php::VarList{'));
self::assertStringNotContainsString('std::array<php::Variant', $code);
self::assertStringNotContainsString('php::ArgList{', $code);
self::assertSame(3, substr_count($code, 'typephp_call_method_cached('));
self::assertSame(1, substr_count($code, 'typephp_call_method_scoped_cached('));
self::assertStringNotContainsString('php::callScoped(', $code);
self::assertSame(2, substr_count($code, 'typephp_call_method_cached('));
self::assertSame(0, substr_count($code, 'typephp_call_method_scoped_cached('));
self::assertSame(1, substr_count($code, 'php::callScoped('));
self::assertSame(2, substr_count($code, '.call(method'));
self::assertStringContainsString('.call(get_persistent_method(', $code);
self::assertStringContainsString('php::FunctionCallCacheSlot function_call_cache_map[1]', $extension);
self::assertStringContainsString('php::MethodCallCacheSlot method_call_cache_map[4]', $extension);
self::assertStringContainsString('php::MethodCallCacheSlot method_call_cache_map[2]', $extension);
self::assertStringContainsString('typephp_get_function_call_cache(FunctionCallCacheId cache_id)', $extension);
self::assertStringContainsString('typephp_get_method_call_cache(MethodCallCacheId cache_id)', $extension);
}

@ -12,11 +12,6 @@ class SymbolTest extends TestCase
$this->assertEquals('php::getStaticProperty', Symbol::getStaticProperty());
}
public function testGetResolvedStaticProperty(): void
{
$this->assertEquals('typephp_get_static_property', Symbol::getResolvedStaticProperty());
}
public function testSetStaticProperty(): void
{
$this->assertEquals('php::setStaticProperty', Symbol::setStaticProperty());
@ -52,16 +47,6 @@ class SymbolTest extends TestCase
$this->assertEquals('php::VarList', Symbol::varList());
}
public function testGetCalledCe(): void
{
$this->assertSame('typephp_get_called_ce(this_)', Symbol::getCalledCe());
}
public function testGetCalledClass(): void
{
$this->assertSame('typephp_get_called_class(this_)', Symbol::getCalledClass());
}
public function testSafeIndex(): void
{
$result = Symbol::safeIndex('0', '10');

@ -15,11 +15,6 @@ class Symbol
return 'php::getStaticProperty';
}
public static function getResolvedStaticProperty(): string
{
return 'typephp_get_static_property';
}
public static function getStaticPropertyRef(): string
{
return 'php::getStaticPropertyRef';
@ -40,16 +35,6 @@ class Symbol
return 'php::concat';
}
public static function getCalledCe(): string
{
return 'typephp_get_called_ce(this_)';
}
public static function getCalledClass(): string
{
return 'typephp_get_called_class(this_)';
}
public static function constant(): string
{
return 'php::constant';

@ -840,6 +840,11 @@ trait MethodCallTrait
} else {
$funcName = '';
}
// A variable method name explicitly requests runtime dispatch. Such a
// call site commonly receives unrelated route/callback names, so a
// monomorphic method cache adds guards and request state without a
// reliable hit rate. Cache only a source-level identifier.
$cacheMethod = $this->isNamedMethod($expr->name);
$requiresDynamicScope = $this->runtimeMethodRequiresDynamicScope(
$class,
@ -862,6 +867,10 @@ trait MethodCallTrait
if (empty($expr->args)) {
if ($requiresDynamicScope && $this->methodDef) {
if (!$resolvedMethodPtr) {
if (!$cacheMethod) {
return 'php::callScoped(' . $object . ', ' . $methodPtr . ', '
. $this->getCallableScopeExpr() . ')';
}
return 'typephp_call_method_scoped_cached(' . $object . ', ' . $methodPtr . ', '
. $this->getCallableScopeExpr() . ', ' . $this->getMethodCallCache() . ')';
}
@ -871,6 +880,9 @@ trait MethodCallTrait
return 'php::callScoped(' . $object . ', ' . $methodPtr . ', ' . $this->getCallableScopeExpr() . ')';
}
if (!$resolvedMethodPtr) {
if (!$cacheMethod) {
return $object . '.call(' . $methodPtr . ')';
}
return 'typephp_call_method_cached(' . $object . ', ' . $methodPtr . ', '
. $this->getMethodCallCache() . ')';
}
@ -881,10 +893,17 @@ trait MethodCallTrait
if (!$resolvedMethodPtr) {
$callArgs = $this->parseCallArgs($expr->args, $funcName, $class);
if ($requiresDynamicScope && $this->methodDef) {
if (!$cacheMethod) {
return 'php::callScoped(' . $object . ', ' . $methodPtr . ', '
. $this->getCallableScopeExpr() . ', ' . $callArgs . ')';
}
return 'typephp_call_method_scoped_cached(' . $object . ', ' . $methodPtr . ', '
. $this->getCallableScopeExpr() . ', ' . $this->getMethodCallCache() . ', '
. $callArgs . ')';
}
if (!$cacheMethod) {
return $object . '.call(' . $methodPtr . ', ' . $callArgs . ')';
}
return 'typephp_call_method_cached(' . $object . ', ' . $methodPtr . ', '
. $this->getMethodCallCache() . ', ' . $callArgs . ')';
}

@ -118,13 +118,22 @@ trait NullsafeAccessTrait
$code .= $this->formatCapturedStmtLines($argBeforeStmts);
}
if ($requiresDynamicScope && $this->methodDef) {
if ($this->isNamedMethod($item[4]->name)) {
$code .= $this->getIndent()
. "{$tmpVar} = typephp_call_method_scoped_cached({$object}, {$item[1]}, "
. $this->getCallableScopeExpr() . ', ' . $this->getMethodCallCache()
. ", {$args});" . PHP_EOL;
} else {
$code .= $this->getIndent() . "{$tmpVar} = php::callScoped({$object}, {$item[1]}, "
. $this->getCallableScopeExpr() . ", {$args});" . PHP_EOL;
}
} else {
if ($this->isNamedMethod($item[4]->name)) {
$code .= $this->getIndent() . "{$tmpVar} = typephp_call_method_cached({$object}, {$item[1]}, "
. $this->getMethodCallCache() . ", {$args});" . PHP_EOL;
} else {
$code .= $this->getIndent() . "{$tmpVar} = {$object}.call({$item[1]}, {$args});" . PHP_EOL;
}
}
if ($argAfterStmts) {
$code .= $this->formatCapturedStmtLines($argAfterStmts);

@ -1,5 +1,5 @@
--TEST--
Dynamic call caches preserve polymorphic, object, and magic dispatch
Dynamic calls preserve cached names, dynamic names, polymorphism, and magic dispatch
--FILE--
<?php
@ -99,6 +99,11 @@ function invoke_nullsafe_named_method(?object $object, int $value): ?string
return $object?->run($value);
}
function invoke_nullsafe_dynamic_method(?object $object, mixed $method, int $value): ?string
{
return $object?->$method($value);
}
function invoke_static_method(mixed $class, mixed $method, int $value): string
{
return $class::$method($value);
@ -139,16 +144,18 @@ function main(): void
var_dump(invoke_nullsafe_named_method($objects[0], 11));
var_dump(invoke_nullsafe_named_method(null, 12));
var_dump(invoke_nullsafe_named_method($objects[1], 13));
var_dump(invoke_nullsafe_dynamic_method($objects[0], 'run', 14));
var_dump(invoke_nullsafe_dynamic_method(null, 'run', 15));
$scoped = new CachedScopedMethod();
var_dump($scoped->invoke('hidden', 14));
var_dump($scoped->invoke('hidden', 15));
var_dump($scoped->invoke('hidden', 16));
var_dump($scoped->invoke('hidden', 17));
var_dump(invoke_static_method('CachedStaticMethod', 'run', 16));
var_dump(invoke_static_method('CachedStaticMethodSecond', 'run', 17));
var_dump(invoke_static_method('CachedStaticMagic', 'missing', 18));
var_dump(invoke_static_named_method('CachedStaticMethod', 19));
var_dump(invoke_named_class_dynamic_method('run', 20));
var_dump(invoke_static_method('CachedStaticMethod', 'run', 18));
var_dump(invoke_static_method('CachedStaticMethodSecond', 'run', 19));
var_dump(invoke_static_method('CachedStaticMagic', 'missing', 20));
var_dump(invoke_static_named_method('CachedStaticMethod', 21));
var_dump(invoke_named_class_dynamic_method('run', 22));
}
?>
--EXPECT--
@ -166,10 +173,12 @@ string(16) "magic-missing:10"
string(15) "method-first:11"
NULL
string(16) "method-second:13"
string(9) "scoped:14"
string(9) "scoped:15"
string(9) "static:16"
string(16) "static-second:17"
string(23) "static-magic-missing:18"
string(9) "static:19"
string(9) "static:20"
string(15) "method-first:14"
NULL
string(9) "scoped:16"
string(9) "scoped:17"
string(9) "static:18"
string(16) "static-second:19"
string(23) "static-magic-missing:20"
string(9) "static:21"
string(9) "static:22"

Loading…
Cancel
Save