diff --git a/.github/workflows/benchmark-tag.yml b/.github/workflows/benchmark-tag.yml index 4a3df2a27..eaeb7644b 100644 --- a/.github/workflows/benchmark-tag.yml +++ b/.github/workflows/benchmark-tag.yml @@ -1,6 +1,12 @@ name: Benchmark History Tag on: + workflow_dispatch: + inputs: + tag_name: + description: 'Git Tag Name' + required: true + default: '1.x' push: branches: [ 1.x ] @@ -23,6 +29,8 @@ jobs: steps: - name: "Checkout" uses: "actions/checkout@v4" + with: + ref: refs/tags/${{ github.event.inputs.tag_name }} - name: "Install PHP" uses: "shivammathur/setup-php@v2" @@ -52,9 +60,12 @@ jobs: uses: "actions/cache@v3" with: path: "var/phpbench" - key: "php-${{ matrix.php-version }}-phpbench" + key: "php-${{ matrix.php-version }}-${{ github.event.inputs.tag_name }}-phpbench" restore-keys: | - php-${{ matrix.php-version }}-phpbench- + php-${{ matrix.php-version }}-${{ github.event.inputs.tag_name }}-phpbench- + + - name: Adding main headline + run: echo '# Flow PHP - Benchmark - ${{ github.event.inputs.tag_name }}' >> $GITHUB_STEP_SUMMARY - - name: "Benchmark Tag" - run: "composer test:benchmark -- --tag=original --progress=plain" + - name: "Benchmark" + run: "composer test:benchmark -- --tag=${{ github.event.inputs.tag_name }} --progress=none >> $GITHUB_STEP_SUMMARY" diff --git a/.github/workflows/test-benchmark.yml b/.github/workflows/test-benchmark.yml index a3b72624d..894cc94b1 100644 --- a/.github/workflows/test-benchmark.yml +++ b/.github/workflows/test-benchmark.yml @@ -58,12 +58,27 @@ jobs: uses: "actions/cache@v3" with: path: "var/phpbench" - key: "php-${{ matrix.php-version }}-phpbench" + key: "php-${{ matrix.php-version }}-1.x-phpbench" restore-keys: | - php-${{ matrix.php-version }}-phpbench- + php-${{ matrix.php-version }}-1.x-phpbench- - - name: Adding markdown + - name: Adding main headline run: echo '# Flow PHP - Benchmark' >> $GITHUB_STEP_SUMMARY + - name: Adding extractors headline + run: echo '## Extractors' >> $GITHUB_STEP_SUMMARY + + - name: "Benchmark" + run: "composer test:benchmark -- --ref=1.x --progress=none --group=extractor >> $GITHUB_STEP_SUMMARY" + + - name: Adding transformers headline + run: echo '## Transformers' >> $GITHUB_STEP_SUMMARY + + - name: "Benchmark" + run: "composer test:benchmark -- --ref=1.x --progress=none --group=transformer >> $GITHUB_STEP_SUMMARY" + + - name: Adding entry factory headline + run: echo '## Entry Factory' >> $GITHUB_STEP_SUMMARY + - name: "Benchmark" - run: "composer test:benchmark -- --ref=original --progress=none >> $GITHUB_STEP_SUMMARY" + run: "composer test:benchmark -- --ref=1.x --progress=none --group=entry_factory >> $GITHUB_STEP_SUMMARY" \ No newline at end of file diff --git a/src/adapter/etl-adapter-csv/tests/Flow/ETL/Adapter/CSV/Tests/Benchmark/CSVExtractorBench.php b/src/adapter/etl-adapter-csv/tests/Flow/ETL/Adapter/CSV/Tests/Benchmark/CSVExtractorBench.php index 102954e65..8678591fc 100644 --- a/src/adapter/etl-adapter-csv/tests/Flow/ETL/Adapter/CSV/Tests/Benchmark/CSVExtractorBench.php +++ b/src/adapter/etl-adapter-csv/tests/Flow/ETL/Adapter/CSV/Tests/Benchmark/CSVExtractorBench.php @@ -3,10 +3,12 @@ namespace Flow\ETL\Adapter\CSV\Tests\Benchmark; use Flow\ETL\DSL\CSV; +use PhpBench\Attributes\Groups; use PhpBench\Attributes\Iterations; use PhpBench\Attributes\Revs; #[Iterations(5)] +#[Groups(['extractor'])] final class CSVExtractorBench { #[Revs(1000)] diff --git a/src/adapter/etl-adapter-json/tests/Flow/ETL/Adapter/JSON/Tests/Benchmark/JsonExtractorBench.php b/src/adapter/etl-adapter-json/tests/Flow/ETL/Adapter/JSON/Tests/Benchmark/JsonExtractorBench.php index b25c34017..dad039cf0 100644 --- a/src/adapter/etl-adapter-json/tests/Flow/ETL/Adapter/JSON/Tests/Benchmark/JsonExtractorBench.php +++ b/src/adapter/etl-adapter-json/tests/Flow/ETL/Adapter/JSON/Tests/Benchmark/JsonExtractorBench.php @@ -3,10 +3,12 @@ namespace Flow\ETL\Adapter\JSON\Tests\Benchmark; use Flow\ETL\DSL\Json; +use PhpBench\Attributes\Groups; use PhpBench\Attributes\Iterations; use PhpBench\Attributes\Revs; #[Iterations(5)] +#[Groups(['extractor'])] final class JsonExtractorBench { #[Revs(1000)] diff --git a/src/adapter/etl-adapter-parquet/tests/Flow/ETL/Adapter/Parquet/Tests/Benchmark/ParquetExtractorBench.php b/src/adapter/etl-adapter-parquet/tests/Flow/ETL/Adapter/Parquet/Tests/Benchmark/ParquetExtractorBench.php index ae23fc481..e6e6f109c 100644 --- a/src/adapter/etl-adapter-parquet/tests/Flow/ETL/Adapter/Parquet/Tests/Benchmark/ParquetExtractorBench.php +++ b/src/adapter/etl-adapter-parquet/tests/Flow/ETL/Adapter/Parquet/Tests/Benchmark/ParquetExtractorBench.php @@ -3,10 +3,12 @@ namespace Flow\ETL\Adapter\Parquet\Tests\Benchmark; use Flow\ETL\DSL\Parquet; +use PhpBench\Attributes\Groups; use PhpBench\Attributes\Iterations; use PhpBench\Attributes\Revs; #[Iterations(5)] +#[Groups(['extractor'])] final class ParquetExtractorBench { #[Revs(1000)] diff --git a/src/adapter/etl-adapter-text/tests/Flow/ETL/Adapter/Text/Tests/Benchmark/TextExtractorBench.php b/src/adapter/etl-adapter-text/tests/Flow/ETL/Adapter/Text/Tests/Benchmark/TextExtractorBench.php index 3f1be3784..3c732405b 100644 --- a/src/adapter/etl-adapter-text/tests/Flow/ETL/Adapter/Text/Tests/Benchmark/TextExtractorBench.php +++ b/src/adapter/etl-adapter-text/tests/Flow/ETL/Adapter/Text/Tests/Benchmark/TextExtractorBench.php @@ -3,10 +3,12 @@ namespace Flow\ETL\Adapter\Text\Tests\Benchmark; use Flow\ETL\DSL\Text; +use PhpBench\Attributes\Groups; use PhpBench\Attributes\Iterations; use PhpBench\Attributes\Revs; #[Iterations(5)] +#[Groups(['extractor'])] final class TextExtractorBench { #[Revs(1000)] diff --git a/src/adapter/etl-adapter-xml/tests/Flow/ETL/Adapter/XML/Tests/Benchmark/XmlExtractorBench.php b/src/adapter/etl-adapter-xml/tests/Flow/ETL/Adapter/XML/Tests/Benchmark/XmlExtractorBench.php index 38e8a418d..9278a13b5 100644 --- a/src/adapter/etl-adapter-xml/tests/Flow/ETL/Adapter/XML/Tests/Benchmark/XmlExtractorBench.php +++ b/src/adapter/etl-adapter-xml/tests/Flow/ETL/Adapter/XML/Tests/Benchmark/XmlExtractorBench.php @@ -3,10 +3,12 @@ namespace Flow\ETL\Adapter\XML\Tests\Benchmark; use Flow\ETL\DSL\XML; +use PhpBench\Attributes\Groups; use PhpBench\Attributes\Iterations; use PhpBench\Attributes\Revs; #[Iterations(5)] +#[Groups(['extractor'])] final class XmlExtractorBench { #[Revs(1000)] diff --git a/src/core/etl/src/Flow/ETL/Rows.php b/src/core/etl/src/Flow/ETL/Rows.php index 5126aab1f..539185392 100644 --- a/src/core/etl/src/Flow/ETL/Rows.php +++ b/src/core/etl/src/Flow/ETL/Rows.php @@ -4,6 +4,7 @@ namespace Flow\ETL; +use function Flow\ETL\DSL\array_to_rows; use Flow\ETL\DSL\Entry; use Flow\ETL\Exception\InvalidArgumentException; use Flow\ETL\Exception\RuntimeException; @@ -40,28 +41,7 @@ public function __construct(Row ...$rows) public static function fromArray(array $data, EntryFactory $entryFactory = new NativeEntryFactory()) : self { - /** @var array $rows */ - $rows = []; - - foreach ($data as $entries) { - if (!\is_array($entries)) { - throw new InvalidArgumentException('Rows expects nested array data structure: array>'); - } - - $row = []; - - /** - * @var string $entryName - * @var mixed $entryValue - */ - foreach ($entries as $entryName => $entryValue) { - $row[] = $entryFactory->create($entryName, $entryValue); - } - - $rows[] = Row::create(...$row); - } - - return new self(...$rows); + return array_to_rows($data, $entryFactory); } public function __serialize() : array diff --git a/src/core/etl/tests/Flow/ETL/Tests/Benchmark/EntryFactory/NativeEntryFactoryBench.php b/src/core/etl/tests/Flow/ETL/Tests/Benchmark/EntryFactory/NativeEntryFactoryBench.php new file mode 100644 index 000000000..6261294b0 --- /dev/null +++ b/src/core/etl/tests/Flow/ETL/Tests/Benchmark/EntryFactory/NativeEntryFactoryBench.php @@ -0,0 +1,76 @@ +rowsArray = \array_map( + static fn (int $i) : array => [ + 'order_id' => $faker->uuid, + 'created_at' => $faker->dateTimeThisYear->format(\DateTimeInterface::RFC3339), + 'updated_at' => $faker->dateTimeThisMonth->format(\DateTimeInterface::RFC3339), + 'cancelled_at' => ($i % 10) === 0 ? $faker->dateTimeThisMonth->format(\DateTimeInterface::RFC3339) : null, + 'active' => !(($i % 20) === 0), + 'total_price' => $faker->randomFloat(2, 0, 500), + 'discount' => $faker->randomFloat(2, 0, 50), + 'customer' => [ + 'name' => $faker->firstName, + 'last_name' => $faker->lastName, + 'email' => $faker->email, + ], + 'address' => [ + 'street' => $faker->streetAddress, + 'city' => $faker->city, + 'zip' => $faker->postcode, + 'country' => $faker->country, + 'location' => [ + 'lat' => $faker->latitude, + 'lng' => $faker->longitude, + ], + ], + 'notes' => \array_map( + static fn ($i) => $faker->sentence, + \range(1, $faker->numberBetween(1, 5)) + ), + ], + \range(1, 10_000) + ); + } + + #[BeforeMethods(['setUp'])] + #[Revs(5)] + public function bench_10k_rows() : void + { + array_to_rows($this->rowsArray, new NativeEntryFactory()); + } + + #[BeforeMethods(['setUp'])] + #[Revs(5)] + public function bench_1k_rows() : void + { + array_to_rows(\array_slice($this->rowsArray, 0, 1_000), new NativeEntryFactory()); + } + + #[BeforeMethods(['setUp'])] + #[Revs(5)] + public function bench_5k_rows() : void + { + array_to_rows(\array_slice($this->rowsArray, 0, 5_000), new NativeEntryFactory()); + } +} diff --git a/src/core/etl/tests/Flow/ETL/Tests/Benchmark/Transformer/EntryExpressionEvalTransformerBench.php b/src/core/etl/tests/Flow/ETL/Tests/Benchmark/Transformer/EntryExpressionEvalTransformerBench.php index 5c298e4e5..f2a2cd92c 100644 --- a/src/core/etl/tests/Flow/ETL/Tests/Benchmark/Transformer/EntryExpressionEvalTransformerBench.php +++ b/src/core/etl/tests/Flow/ETL/Tests/Benchmark/Transformer/EntryExpressionEvalTransformerBench.php @@ -9,10 +9,12 @@ use Flow\ETL\Row; use Flow\ETL\Rows; use Flow\ETL\Transformer\EntryExpressionEvalTransformer; +use PhpBench\Attributes\Groups; use PhpBench\Attributes\Iterations; use PhpBench\Attributes\Revs; #[Iterations(5)] +#[Groups(['transformer'])] final class EntryExpressionEvalTransformerBench { #[Revs(1000)] diff --git a/src/core/etl/tests/Flow/ETL/Tests/Benchmark/Transformer/RenameEntryTransformerBench.php b/src/core/etl/tests/Flow/ETL/Tests/Benchmark/Transformer/RenameEntryTransformerBench.php index d02ed93f2..964912666 100644 --- a/src/core/etl/tests/Flow/ETL/Tests/Benchmark/Transformer/RenameEntryTransformerBench.php +++ b/src/core/etl/tests/Flow/ETL/Tests/Benchmark/Transformer/RenameEntryTransformerBench.php @@ -6,27 +6,37 @@ use Flow\ETL\FlowContext; use Flow\ETL\Rows; use Flow\ETL\Transformer\RenameEntryTransformer; +use PhpBench\Attributes\BeforeMethods; +use PhpBench\Attributes\Groups; use PhpBench\Attributes\Iterations; use PhpBench\Attributes\Revs; #[Iterations(5)] +#[Groups(['transformer'])] final class RenameEntryTransformerBench { + private ?FlowContext $context = null; + + private ?Rows $rows = null; + + public function setUp() : void + { + $this->rows = Rows::fromArray( + [ + ['id' => 1, 'random' => false, 'text' => null, 'from' => 666], + ['id' => 2, 'random' => true, 'text' => null, 'from' => 666], + ['id' => 3, 'random' => false, 'text' => null, 'from' => 666], + ['id' => 4, 'random' => true, 'text' => null, 'from' => 666], + ['id' => 5, 'random' => false, 'text' => null, 'from' => 666], + ] + ); + $this->context = new FlowContext(Config::default()); + } + + #[BeforeMethods(['setUp'])] #[Revs(1000)] public function bench_transform() : void { - (new RenameEntryTransformer('from', 'to')) - ->transform( - Rows::fromArray( - [ - ['id' => 1, 'random' => false, 'text' => null, 'from' => 666], - ['id' => 2, 'random' => true, 'text' => null, 'from' => 666], - ['id' => 3, 'random' => false, 'text' => null, 'from' => 666], - ['id' => 4, 'random' => true, 'text' => null, 'from' => 666], - ['id' => 5, 'random' => false, 'text' => null, 'from' => 666], - ] - ), - new FlowContext(Config::default()) - ); + (new RenameEntryTransformer('from', 'to'))->transform($this->rows, $this->context); } }