refactor(php): 优化对象属性获取逻辑并添加性能测试

- 替换直接访问 objectProps 数组为 getObjectPropInfoByVar 方法
- 添加 registerObjectPropVar 和 registerHoistedObjectPropVar 辅助方法
- 移除重复的数组键检查逻辑,简化代码流程
- 新增性能基准测试文件用于测量不同调用方式的性能差异
- 添加实例和静态属性获取目标类以改进解析器结构
pull/4/head
韩天峰 2 months ago
parent 8711545637
commit 7d601a3f8c
  1. 177
      perf/code.php
  2. 38
      src/Php/CompilerBase.php
  3. 5
      src/Php/Parser/BinaryOpTrait.php
  4. 18
      src/Php/Resolver/InstancePropertyFetchTarget.php
  5. 24
      src/Php/Resolver/StaticPropertyFetchTarget.php

@ -0,0 +1,177 @@
<?php
use native_types;
const N = 1000_0000;
const ROUNDS = 5;
final class NativeCallTarget
{
public function hit(int $i): int
{
return $i + 1;
}
}
final class DynamicCallTarget
{
public function hit(int $i): int
{
return $i + 1;
}
}
final class EmptyObject
{
}
final class TemporaryCallTarget
{
public function hit(int $i): int
{
return $i + 1;
}
}
function baseline_loop(int $n): int
{
$sum = 0;
for ($i = 0; $i < $n; ++$i) {
$sum += $i + 1;
}
return $sum;
}
function native_method_call(int $n): int
{
$obj = new NativeCallTarget();
$sum = 0;
for ($i = 0; $i < $n; ++$i) {
$sum += $obj->hit($i);
}
return $sum;
}
function dynamic_method_call(int $n): int
{
global $dynamicObject;
$sum = 0;
for ($i = 0; $i < $n; ++$i) {
$sum += $dynamicObject->hit($i);
}
return $sum;
}
function new_object_only(int $n): int
{
$sum = 0;
for ($i = 0; $i < $n; ++$i) {
$obj = new EmptyObject();
$sum += $obj instanceof EmptyObject ? 1 : 0;
}
return $sum;
}
function temporary_new_and_call(int $n): int
{
$sum = 0;
for ($i = 0; $i < $n; ++$i) {
$sum += (new TemporaryCallTarget())->hit($i);
}
return $sum;
}
function empty_new_baseline(int $n): int
{
$sum = 0;
for ($i = 0; $i < $n; ++$i) {
$sum += 1;
}
return $sum;
}
function elapsed_ns(string $case, int $n): array
{
$start = hrtime(true);
if ($case === 'baseline') {
$result = baseline_loop($n);
} elseif ($case === 'native') {
$result = native_method_call($n);
} elseif ($case === 'dynamic') {
$result = dynamic_method_call($n);
} elseif ($case === 'empty-new') {
$result = empty_new_baseline($n);
} elseif ($case === 'new') {
$result = new_object_only($n);
} elseif ($case === 'temporary') {
$result = temporary_new_and_call($n);
} else {
throw new RuntimeException("unknown benchmark case: " . $case);
}
return [hrtime(true) - $start, $result];
}
function measure_min(string $case, int $n, int $rounds): array
{
$bestNs = 0;
$bestResult = 0;
for ($i = 0; $i < $rounds; ++$i) {
[$ns, $result] = elapsed_ns($case, $n);
if ($i === 0 || $ns < $bestNs) {
$bestNs = $ns;
$bestResult = $result;
}
}
return [$bestNs, $bestResult];
}
function report(string $name, int $ns, int $n, int $baselineNs = 0): void
{
$adjusted = max(0, $ns - $baselineNs);
printf(
"%-20s raw=%0.6fs adjusted=%0.6fs ns/op=%0.2f\n",
$name,
$ns / 1000000000.0,
$adjusted / 1000000000.0,
$adjusted / (float) $n,
);
}
function main(): void
{
global $dynamicObject;
$n = N;
$dynamicObject = new DynamicCallTarget();
baseline_loop(1000);
native_method_call(1000);
dynamic_method_call(1000);
new_object_only(1000);
temporary_new_and_call(1000);
[$baseline, $baselineResult] = measure_min('baseline', $n, ROUNDS);
[$native, $nativeResult] = measure_min('native', $n, ROUNDS);
[$dynamic, $dynamicResult] = measure_min('dynamic', $n, ROUNDS);
[$emptyNew, $emptyNewResult] = measure_min('empty-new', $n, ROUNDS);
[$newObject, $newResult] = measure_min('new', $n, ROUNDS);
[$temporary, $temporaryResult] = measure_min('temporary', $n, ROUNDS);
printf("n=%d rounds=%d\n", $n, ROUNDS);
report('baseline loop', $baseline, $n);
report('native call', $native, $n, $baseline);
report('dynamic call', $dynamic, $n, $baseline);
report('newObject', $newObject, $n, $emptyNew);
report('newObject + call', $temporary, $n, $baseline);
printf(
"checksums: baseline=%d native=%d dynamic=%d new=%d temporary=%d\n",
$baselineResult,
$nativeResult,
$dynamicResult,
$newResult + $emptyNewResult,
$temporaryResult,
);
}

@ -1063,6 +1063,29 @@ class CompilerBase extends \PhpAot\Core\Translator implements PropertyAccessCont
return $this->context->objectProps[$this->getObjectPropVarName($object, $prop)];
}
protected function getObjectPropInfoByVar(string $var): ?array
{
return $this->context->objectProps[$var] ?? null;
}
protected function registerObjectPropVar(string $var, array $info): void
{
if (isset($this->context->objectProps[$var])) {
return;
}
$this->context->objectProps[$var] = $info;
}
protected function registerHoistedObjectPropVar(string $var, string $type, string $getter): void
{
$info = $this->getHoistedObjectPropInfo($type);
$this->registerObjectPropVar($var, [
'type' => $info['type'],
'getter' => $getter,
'kind' => $info['kind'],
]);
}
protected function getNativeName(string $fn, string $ns = '', string $class = ''): string
{
$names = [];
@ -2420,8 +2443,10 @@ class CompilerBase extends \PhpAot\Core\Translator implements PropertyAccessCont
$this->parsePropertyFetch($expr);
$propVar = $this->getNativePropertyVar($expr);
if ($propVar !== null) {
$info = $this->context->objectProps[$propVar];
return $info['type'];
$info = $this->getObjectPropInfoByVar($propVar);
if ($info !== null) {
return $info['type'];
}
}
}
}
@ -4942,14 +4967,7 @@ class CompilerBase extends \PhpAot\Core\Translator implements PropertyAccessCont
if (!$this->canHoistObjectProp($objectVar, $propName)) {
return null;
}
if (!$this->hasObjectPropVar($propVar)) {
$info = $this->getHoistedObjectPropInfo($def->type);
$this->context->objectProps[$propVar] = [
'type' => $info['type'],
'getter' => $getter,
'kind' => $info['kind'],
];
}
$this->registerHoistedObjectPropVar($propVar, $def->type, $getter);
$this->setNativePropertyVar($expr, $propVar);
$this->setNativePropertyValueSource($expr, self::NATIVE_PROPERTY_VALUE_VAR);
return $propVar;

@ -206,8 +206,9 @@ trait BinaryOpTrait
if ($expr instanceof Expr\PropertyFetch) {
$nativePropertyVar = $this->getNativePropertyVar($expr);
if ($nativePropertyVar !== null && $nativePropertyVar === $value) {
if (isset($this->context->objectProps[$nativePropertyVar])) {
return $this->context->objectProps[$nativePropertyVar]['type'];
$info = $this->getObjectPropInfoByVar($nativePropertyVar);
if ($info !== null) {
return $info['type'];
}
$def = $this->getNativePropertyDef($expr);
if ($def && $this->isNativePropertyTypedValue($expr)) {

@ -0,0 +1,18 @@
<?php
/**
* This file is part of Swoole-Compiler(AOT).
*
* @link https://www.swoole.com/
* @contact service@swoole.com
*/
namespace PhpAot\Php\Resolver;
final readonly class InstancePropertyFetchTarget
{
public function __construct(
public string $property,
public string $class,
) {
}
}

@ -0,0 +1,24 @@
<?php
/**
* This file is part of Swoole-Compiler(AOT).
*
* @link https://www.swoole.com/
* @contact service@swoole.com
*/
namespace PhpAot\Php\Resolver;
final readonly class StaticPropertyFetchTarget
{
public function __construct(
public string $property,
public ?string $class,
public ?string $dynamicExpression,
) {
}
public function isDynamic(): bool
{
return $this->dynamicExpression !== null;
}
}
Loading…
Cancel
Save