LINQ PHP比较:YaLinqo,Ginq,Pinq

分享于 

40分钟阅读

Web开发

  繁體 雙語

介绍

本文讨论如何将LINQ端口的( 最明智的表现) 与. NET 进行比较。 在. NET 中,LINQ用于在各种集合( 包括数据库) 上编写sql查询。 在PHP中,它通常用于转换数组,比如在函数 array_filterarray_map 中构建的数组,以及更多的特性。 由于PHP和库的当前状态,LINQ端口最适合于在从 Web服务 返回的相对较小的数据集上执行转换,例如。

这不是一个介绍性的文章,我将不包括关于使用LINQ的教程。 我可能会写另一篇文章,详细介绍。 但是,一些示例应该是自我解释的,因这里即使以前没有使用 LINQ,也可以比较代码。

你应该知道如何使用闭包。 知道LINQ是一个巨大的奖金。

背景

在开发了从. NET 到PHP的LINQ的更多端口之前,我深入研究了所有可用的库。 其中有很多: LINQ,Phinq,PHPLinq和 Plinq。 不幸的是,它们都不支持惰性评估;其中大部分不包括足够的测试;文档要么缺少要么不完整;它们的文档很明显不适合生产。

这就是 YaLinqo 诞生的原因。 当时,它是唯一真正实现 LINQ to对象的LINQ端口。 它有 100%个测试覆盖,非常详细的PHPDoc,支持"字符串 lambda"并且在转换过程中不会丢失键。 第一个版本是在php5中实现的,后来它被更新为依靠来自PHP的yield

从那时起,两个库出现在YaLinqo中。 第一个是 Ginq。 YaLinqo不同,它依赖于手动实现的迭代器。 从某种程度上说,它比第一个版本更接近于"php路径",后者依赖于"hackish"迭代器,后者在排序。分组和加入时方便地支持了来自于Symfony的。 许多方法都有来自函数编程的别名,例如"地图"除了"选择"。 文档未详细说明。

另一个库是 Pinq。 它是支持对象和数据库的( 潜在的) 最强大的库。 它支持使用语法分析器解析PHP代码,并可以生成 SQL。 不幸的是,在编写时,惟一的查询提供程序是针对MySQL的,它的状态是"演示"。 我怀疑在生产就绪并开始支持多个DBMS之前,有很多工作要做。 另一个缺点是,它包含了较少的函数,它的函数的featureful。

这三个库都有许可开放源码许可。良好的测试覆盖。文档。支持大量功能,在任何不需要重优化的项目中都可以使用,并且可以使用。 在你需要使用 for 查询相当大的加载项目时,如果你在高负载项目中使用了这些库,那么你可能希望使用良好的旧和 foreach。 但是,我不认为脚本语言对于高负载项目是很好的选择,大多数最重要的逻辑都在DB中。

有趣的是,这三个库的大小非常不同: java包含 4个类,具有零依赖关系,Ginq包含 70个上的类,对symfony访问 MODULE 有一个依赖性,Pinq包含一个上的类,对php解析器具有。 区别在于它们的结构。 YaLinqo只使用PHP数组和回调。 在所有的转换,集合中包含了迭代器类,集合中包含了更多的类和接口,这些类和接口由LINQ在. NET 中产生,包括支持数据库所必需的所有管线: 知识库,解析 等等 ( 我还没有深入研究Pinq的来源。)

关于测试

我在性能测试方面有很少的经验,因这里测试是快速和脏的,没有考虑到精确的结果。 不考虑内存使用情况。 但是,性能的差异是如此之大,我不认为精确度真的是。 如果你在代码中找到一个 Bug,或者可以改进测试,那么可以在GitHub的上找到该项目,pull请求是欢迎的。

在所有的测试中,调用 benchmark_linq_groups 函数,它接受 PHP。YaLinqo。Ginq和Pinq中实现的函数 array。 这个函数使用 foreach 生成的集合,并确保从所有测试返回的结果都是相同的。

在 PHP 5.5.14上执行测试,Windows 7 rx 64 -bit。

测试

让我们从纯开销开始:

benchmark_linq_groups("Iterating over $ITER_MAX ints", 100, null,
 [
 "for" => function () use ($ITER_MAX) {
 $j = null;
 for ($i = 0; $i <$ITER_MAX; $i++)
 $j = $i;
 return$j;
 },
 "array functions" => function () use ($ITER_MAX) {
 $j = null;
 foreach (range(0, $ITER_MAX - 1) as $i)
 $j = $i;
 return$j;
 },
 ],
 [
 function () use ($ITER_MAX) {
 $j = null;
 foreach (E::range(0, $ITER_MAX) as $i)
 $j = $i;
 return$j;
 },
 ],
 [
 function () use ($ITER_MAX) {
 $j = null;
 foreach (G::range(0, $ITER_MAX - 1) as $i)
 $j = $i;
 return$j;
 },
 ],
 [
 function () use ($ITER_MAX) {
 $j = null;
 foreach (P::from(range(0, $ITER_MAX - 1)) as $i)
 $j = $i;
 return$j;
 },
 ]);

生成器函数 range 在Pinq中不可用,所以我使用了内置函数,如它的文档所示。

以下是结果:

Iterating over 1000 ints
------------------------
 PHP [for] 0.00006 sec x1.0 (100%)
 PHP [array functions] 0.00011 sec x1.8 (+83%)
 YaLinqo 0.00041 sec x6.8 (+583%)
 Ginq 0.00075 sec x12.5 (+1150%)
 Pinq 0.00169 sec x28.2 (+2717%)

迭代器浪费大量时间。 Pinq意味着最多- 30倍比 for 慢。 但是,这并不是最令人惊讶的结果,因为你将看到。

让我们生成一个 array,而不仅仅是迭代:

benchmark_linq_groups("Generating array of $ITER_MAX integers", 100, 'consume',
 [
 "for" =>
 function () use ($ITER_MAX) {
 $a = [ ];
 for ($i = 0; $i <$ITER_MAX; $i++)
 $a[] = $i;
 return$a;
 },
 "array functions" =>
 function () use ($ITER_MAX) {
 return range(0, $ITER_MAX - 1);
 },
 ],
 [
 function () use ($ITER_MAX) {
 return E::range(0, $ITER_MAX)->toArray();
 },
 ],
 [
 function () use ($ITER_MAX) {
 return G::range(0, $ITER_MAX - 1)->toArray();
 },
 ],
 [
 function () use ($ITER_MAX) {
 return P::from(range(0, $ITER_MAX - 1))->asArray();
 },
 ]);

和结果:

Generating array of 1000 integers
---------------------------------
 PHP [for] 0.00025 sec x1.3 (+32%)
 PHP [array functions] 0.00019 sec x1.0 (100%)
 YaLinqo 0.00060 sec x3.2 (+216%)
 Ginq 0.00107 sec x5.6 (+463%)
 Pinq 0.00183 sec x9.6 (+863%)

现在YaLinqo比 for的解决方案慢了两个时间。 其他库执行得更糟糕,但令人满意。

让我们从测试数据中计算项目: 订单 Having 多于 5个订单项目;订单 Having的订单数量超过 2次,数量超过 5.

benchmark_linq_groups("Counting values in arrays", 100, null,
 [
 "for" => function () use ($DATA) {
 $numberOrders = 0;
 foreach ($DATA->orders as $order) {
 if (count($order['items'])> 5)
 $numberOrders++;
 }
 return$numberOrders;
 },
 "array functions" => function () use ($DATA) {
 return count(
 array_filter(
 $DATA->orders,
 function ($order) { return count($order['items'])> 5; }
 )
 );
 },
 ],
 [
 function () use ($DATA) {
 return E::from($DATA->orders)
 ->count(function ($order) { return count($order['items'])> 5; });
 },
 "string lambda" => function () use ($DATA) {
 return E::from($DATA->orders)
 ->count('$o ==> count($o["items"])> 5');
 },
 ],
 [
 function () use ($DATA) {
 return G::from($DATA->orders)
 ->count(function ($order) { return count($order['items'])> 5; });
 },
 ],
 [
 function () use ($DATA) {
 return P::from($DATA->orders)
 ->where(function ($order) { return count($order['items'])> 5; })
 ->count();
 },
 ]);
benchmark_linq_groups("Counting values in arrays deep", 100, null,
 [
 "for" => function () use ($DATA) {
 $numberOrders = 0;
 foreach ($DATA->orders as $order) {
 $numberItems = 0;
 foreach ($order['items'] as $item) {
 if ($item['quantity']> 5)
 $numberItems++;
 }
 if ($numberItems> 2)
 $numberOrders++;
 }
 return$numberOrders;
 },
 "array functions" => function () use ($DATA) {
 return count(
 array_filter(
 $DATA->orders,
 function ($order) {
 return count(
 array_filter(
 $order['items'],
 function ($item) { return $item['quantity']> 5; }
 )
 )> 2;
 })
 );
 },
 ],
 [
 function () use ($DATA) {
 return E::from($DATA->orders)
 ->count(function ($order) {
 return E::from($order['items'])
 ->count(function ($item) { return $item['quantity']> 5; })> 2;
 });
 },
 ],
 [
 function () use ($DATA) {
 return G::from($DATA->orders)
 ->count(function ($order) {
 return G::from($order['items'])
 ->count(function ($item) { return $item['quantity']> 5; })> 2;
 });
 },
 ],
 [
 function () use ($DATA) {
 return P::from($DATA->orders)
 ->where(function ($order) {
 return P::from($order['items'])
 ->where(function ($item) { return $item['quantity']> 5; })
 ->count()> 2;
 })
 ->count();
 },
 ]);

注意:第一,带标准 array 函数的功能样式将代码转换为可以读的几乎可以读的楼梯。 其次,"字符串 lambda"在这里没有帮助,因为转义代码中转义码是不可理解的。 第三,Pinq不提供接受谓词的count 函数的重载,所以需要一个方法链。 结果:

Counting values in arrays
-------------------------
 PHP [for] 0.00023 sec x1.0 (100%)
 PHP [array functions] 0.00052 sec x2.3 (+126%)
 YaLinqo 0.00056 sec x2.4 (+143%)
 YaLinqo [string lambda] 0.00059 sec x2.6 (+157%)
 Ginq 0.00129 sec x5.6 (+461%)
 Pinq 0.00382 sec x16.6 (+1561%)
Counting values in arrays deep
------------------------------
 PHP [for] 0.00064 sec x1.0 (100%)
 PHP [array functions] 0.00323 sec x5.0 (+405%)
 YaLinqo 0.00798 sec x12.5 (+1147%)
 Ginq 0.01416 sec x22.1 (+2113%)
 Pinq 0.04928 sec x77.0 (+7600%)

除了pinq的可怕结果外,结果是可以预测的。 我查看了代码- 似乎生成一个完整的集合,然后调用内置 count 在它。

我们筛选数组。条件类似上次,但不是计数,而是生成集合。

benchmark_linq_groups("Filtering values in arrays", 100, 'consume',
 [
 "for" => function () use ($DATA) {
 $filteredOrders = [ ];
 foreach ($DATA->orders as $order) {
 if (count($order['items'])> 5)
 $filteredOrders[] = $order;
 }
 return$filteredOrders;
 },
 "array functions" => function () use ($DATA) {
 return array_filter(
 $DATA->orders,
 function ($order) { return count($order['items'])> 5; }
 );
 },
 ],
 [
 function () use ($DATA) {
 return E::from($DATA->orders)
 ->where(function ($order) { return count($order['items'])> 5; });
 },
 "string lambda" => function () use ($DATA) {
 return E::from($DATA->orders)
 ->where('$order ==> count($order["items"])> 5');
 },
 ],
 [
 function () use ($DATA) {
 return G::from($DATA->orders)
 ->where(function ($order) { return count($order['items'])> 5; });
 },
 ],
 [
 function () use ($DATA) {
 return P::from($DATA->orders)
 ->where(function ($order) { return count($order['items'])> 5; });
 },
 ]);
benchmark_linq_groups("Filtering values in arrays deep", 100,
 function ($e) { consume($e, [ 'items' => null ]); },
 [
 "for" => function () use ($DATA) {
 $filteredOrders = [ ];
 foreach ($DATA->orders as $order) {
 $filteredItems = [ ];
 foreach ($order['items'] as $item) {
 if ($item['quantity']> 5)
 $filteredItems[] = $item;
 }
 if (count($filteredItems)> 0) {
 $order['items'] = $filteredItems;
 $filteredOrders[] = [
 'id' => $order['id'],
 'items' => $filteredItems,
 ];
 }
 }
 return$filteredOrders;
 },
 "array functions" => function () use ($DATA) {
 return array_filter(
 array_map(
 function ($order) {
 return [
 'id' => $order['id'],
 'items' => array_filter(
 $order['items'],
 function ($item) { return $item['quantity']> 5; }
 )
 ];
 },
 $DATA->orders
 ),
 function ($order) {
 return count($order['items'])> 0;
 }
 );
 },
 ],
 [
 function () use ($DATA) {
 return E::from($DATA->orders)
 ->select(function ($order) {
 return [
 'id' => $order['id'],
 'items' => E::from($order['items'])
 ->where(function ($item) { return $item['quantity']> 5; })
 ->toArray()
 ];
 })
 ->where(function ($order) {
 return count($order['items'])> 0;
 });
 },
 "string lambda" => function () use ($DATA) {
 return E::from($DATA->orders)
 ->select(function ($order) {
 return [
 'id' => $order['id'],
 'items' => E::from($order['items'])->where('$v["quantity"]> 5')->toArray()
 ];
 })
 ->where('count($v["items"])> 0');
 },
 ],
 [
 function () use ($DATA) {
 return G::from($DATA->orders)
 ->select(function ($order) {
 return [
 'id' => $order['id'],
 'items' => G::from($order['items'])
 ->where(function ($item) { return $item['quantity']> 5; })
 ->toArray()
 ];
 })
 ->where(function ($order) {
 return count($order['items'])> 0;
 });
 },
 ],
 [
 function () use ($DATA) {
 return P::from($DATA->orders)
 ->select(function ($order) {
 return [
 'id' => $order['id'],
 'items' => P::from($order['items'])
 ->where(function ($item) { return $item['quantity']> 5; })
 ->asArray()
 ];
 })
 ->where(function ($order) {
 return count($order['items'])> 0;
 });
 },
 ]);

使用标准 array 函数的代码很难理解,这主要是由于 array_maparray_filter的参数顺序不一致。

使用LINQ的代码故意不是最优的: 即使以后将丢弃对象,也会生成这些对象。 它是LINQ依赖于"匿名对象"在转换之间传递数据的传统。

以前的结果相比,这些结果甚至是异常的:

Filtering values in arrays
--------------------------
 PHP [for] 0.00049 sec x1.0 (100%)
 PHP [array functions] 0.00072 sec x1.5 (+47%)
 YaLinqo 0.00094 sec x1.9 (+92%)
 YaLinqo [string lambda] 0.00094 sec x1.9 (+92%)
 Ginq 0.00295 sec x6.0 (+502%)
 Pinq 0.00328 sec x6.7 (+569%)
Filtering values in arrays deep
-------------------------------
 PHP [for] 0.00514 sec x1.0 (100%)
 PHP [array functions] 0.00739 sec x1.4 (+44%)
 YaLinqo 0.01556 sec x3.0 (+203%)
 YaLinqo [string lambda] 0.01750 sec x3.4 (+240%)
 Ginq 0.03101 sec x6.0 (+503%)
 Pinq 0.05435 sec x10.6 (+957%)

让我们进行排序:

benchmark_linq_groups("Sorting arrays", 100, 'consume',
 [
 function () use ($DATA) {
 $orderedUsers = $DATA->users;
 usort(
 $orderedUsers,
 function ($a, $b) {
 $diff = $a['rating'] - $b['rating'];
 if ($diff!== 0)
 return -$diff;
 $diff = strcmp($a['name'], $b['name']);
 if ($diff!== 0)
 return$diff;
 $diff = $a['id'] - $b['id'];
 return$diff;
 });
 return$orderedUsers;
 },
 ],
 [
 function () use ($DATA) {
 return E::from($DATA->users)
 ->orderByDescending(function ($u) { return $u['rating']; })
 ->thenBy(function ($u) { return $u['name']; })
 ->thenBy(function ($u) { return $u['id']; });
 },
 "string lambda" => function () use ($DATA) {
 return E::from($DATA->users)
 ->orderByDescending('$v["rating"]')->thenBy('$v["name"]')->thenBy('$v["id"]');
 },
 ],
 [
 function () use ($DATA) {
 return G::from($DATA->users)
 ->orderByDesc(function ($u) { return $u['rating']; })
 ->thenBy(function ($u) { return $u['name']; })
 ->thenBy(function ($u) { return $u['id']; });
 },
 "property path" => function () use ($DATA) {
 return G::from($DATA->users)
 ->orderByDesc('[rating]')->thenBy('[name]')->thenBy('[id]');
 },
 ],
 [
 function () use ($DATA) {
 return P::from($DATA->users)
 ->orderByDescending(function ($u) { return $u['rating']; })
 ->thenByAscending(function ($u) { return $u['name']; })
 ->thenByAscending(function ($u) { return $u['id']; });
 },
 ]);

用于 usort 回调的代码有点吓人,但是通过一些实践,编写for代码非常容易。 使用LINQ的代码非常干净,特别是在Ginq使代码美观的情况下。

结果是不可预测的:

Sorting arrays
--------------
 PHP 0.00037 sec x1.0 (100%)
 YaLinqo 0.00161 sec x4.4 (+335%)
 YaLinqo [string lambda] 0.00163 sec x4.4 (+341%)
 Ginq 0.00402 sec x10.9 (+986%)
 Ginq [property path] 0.01998 sec x54.0 (+5300%)
 Pinq 0.00132 sec x3.6 (+257%)

第一,首次在LINQ库中Pinq是最快的( 扰流板: 还有最后一次。

第二,ginq访问的属性非常慢。 我认为这是不可以用的,因为它们不值得 50x 增加时间。

我们得到了有趣的部分- joining两个基于相等键的数组。

benchmark_linq_groups("Joining arrays", 100, 'consume',
 [
 function () use ($DATA) {
 $ordersByCustomerId = [ ];
 foreach ($DATA->orders as $order)
 $ordersByCustomerId[$order['customerId']][] = $order;
 $pairs = [ ];
 foreach ($DATA->users as $user) {
 $userId = $user['id'];
 if (isset($ordersByCustomerId[$userId])) {
 foreach ($ordersByCustomerId[$userId] as $order) {
 $pairs[] = [
 'order' => $order,
 'user' => $user,
 ];
 }
 }
 }
 return$pairs;
 },
 ],
 [
 function () use ($DATA) {
 return E::from($DATA->orders)
 ->join($DATA->users,
 function ($o) { return $o['customerId']; },
 function ($u) { return $u['id']; },
 function ($o, $u) {
 return [
 'order' => $o,
 'user' => $u,
 ];
 });
 },
 "string lambda" => function () use ($DATA) {
 return E::from($DATA->orders)
 ->join($DATA->users,
 '$o ==> $o["customerId"]', '$u ==> $u["id"]',
 '($o, $u) ==> [
"order" => $o,
"user" => $u,
 ]');
 },
 ],
 [
 function () use ($DATA) {
 return G::from($DATA->orders)
 ->join($DATA->users,
 function ($o) { return $o['customerId']; },
 function ($u) { return $u['id']; },
 function ($o, $u) {
 return [
 'order' => $o,
 'user' => $u,
 ];
 });
 },
 "property path" => function () use ($DATA) {
 return G::from($DATA->orders)
 ->join($DATA->users,
 '[customerId]', '[id]',
 function ($o, $u) {
 return [
 'order' => $o,
 'user' => $u,
 ];
 });
 },
 ],
 [
 function () use ($DATA) {
 return P::from($DATA->orders)
 ->join($DATA->users)
 ->onEquality(
 function ($o) { return $o['customerId']; },
 function ($u) { return $u['id']; }
 )
 ->to(function ($o, $u) {
 return [
 'order' => $o,
 'user' => $u,
 ];
 });
 },
 ]);

pinq的代码与其他代码不同。 它将单个方法调用转换为链。 它增加了可读性,但对于那些习惯了. NET. 中的LINQ方法链的人来说,它看起来是不寻常

和结果:

Joining arrays
--------------
 PHP 0.00021 sec x1.0 (100%)
 YaLinqo 0.00065 sec x3.1 (+210%)
 YaLinqo [string lambda] 0.00070 sec x3.3 (+233%)
 Ginq 0.00103 sec x4.9 (+390%)
 Ginq [property path] 0.00200 sec x9.5 (+852%)
 Pinq 1.24155 sec x5,911.8 (+591084%)

哇哦,不,这不是玩笑。 我认为剧本挂了,但最终又返回了令人吃惊的结果。 Pinq比原始PHP慢 5,912倍。 我找不到在,代码中完全发生这种情况,但看起来基本上是 for-for-if 没有查找。 我完全不希望从实现 500类的开发人员那里得到这一点。

让我们来看看更简单的测试- 聚合( 或者折叠,或者折叠)。

benchmark_linq_groups("Aggregating arrays", 100, null,
 [
 "for" => function () use ($DATA) {
 $sum = 0;
 foreach ($DATA->products as $p)
 $sum += $p['quantity'];
 $avg = 0;
 foreach ($DATA->products as $p)
 $avg += $p['quantity'];
 $avg/= count($DATA->products);
 $min = PHP_INT_MAX;
 foreach ($DATA->products as $p)
 $min = min($min, $p['quantity']);
 $max = -PHP_INT_MAX;
 foreach ($DATA->products as $p)
 $max = max($max, $p['quantity']);
 return"$sum-$avg-$min-$max";
 },
 "array functions" => function () use ($DATA) {
 $sum = array_sum(array_map(function ($p) { return $p['quantity']; }, $DATA->products));
 $avg = array_sum(array_map(function ($p) { return $p['quantity']; }, $DATA->products))/count($DATA->products);
 $min = min(array_map(function ($p) { return $p['quantity']; }, $DATA->products));
 $max = max(array_map(function ($p) { return $p['quantity']; }, $DATA->products));
 return"$sum-$avg-$min-$max";
 },
 ],
 [
 function () use ($DATA) {
 $sum = E::from($DATA->products)->sum(function ($p) { return $p['quantity']; });
 $avg = E::from($DATA->products)->average(function ($p) { return $p['quantity']; });
 $min = E::from($DATA->products)->min(function ($p) { return $p['quantity']; });
 $max = E::from($DATA->products)->max(function ($p) { return $p['quantity']; });
 return"$sum-$avg-$min-$max";
 },
 "string lambda" => function () use ($DATA) {
 $sum = E::from($DATA->products)->sum('$v["quantity"]');
 $avg = E::from($DATA->products)->average('$v["quantity"]');
 $min = E::from($DATA->products)->min('$v["quantity"]');
 $max = E::from($DATA->products)->max('$v["quantity"]');
 return"$sum-$avg-$min-$max";
 },
 ],
 [
 function () use ($DATA) {
 $sum = G::from($DATA->products)->sum(function ($p) { return $p['quantity']; });
 $avg = G::from($DATA->products)->average(function ($p) { return $p['quantity']; });
 $min = G::from($DATA->products)->min(function ($p) { return $p['quantity']; });
 $max = G::from($DATA->products)->max(function ($p) { return $p['quantity']; });
 return"$sum-$avg-$min-$max";
 },
 "property path" => function () use ($DATA) {
 $sum = G::from($DATA->products)->sum('[quantity]');
 $avg = G::from($DATA->products)->average('[quantity]');
 $min = G::from($DATA->products)->min('[quantity]');
 $max = G::from($DATA->products)->max('[quantity]');
 return"$sum-$avg-$min-$max";
 },
 ],
 [
 function () use ($DATA) {
 $sum = P::from($DATA->products)->sum(function ($p) { return $p['quantity']; });
 $avg = P::from($DATA->products)->average(function ($p) { return $p['quantity']; });
 $min = P::from($DATA->products)->minimum(function ($p) { return $p['quantity']; });
 $max = P::from($DATA->products)->maximum(function ($p) { return $p['quantity']; });
 return"$sum-$avg-$min-$max";
 },
 ]);
benchmark_linq_groups("Aggregating arrays custom", 100, null,
 [
 function () use ($DATA) {
 $mult = 1;
 foreach ($DATA->products as $p)
 $mult *= $p['quantity'];
 return$mult;
 },
 ],
 [
 function () use ($DATA) {
 return E::from($DATA->products)->aggregate(function ($a, $p) { return$a * $p['quantity']; }, 1);
 },
 "string lambda" => function () use ($DATA) {
 return E::from($DATA->products)->aggregate('$a * $v["quantity"]', 1);
 },
 ],
 [
 function () use ($DATA) {
 return G::from($DATA->products)->aggregate(1, function ($a, $p) { return$a * $p['quantity']; });
 },
 ],
 [
 function () use ($DATA) {
 return P::from($DATA->products)
 ->select(function ($p) { return $p['quantity']; })
 ->aggregate(function ($a, $q) { return$a * $q; });
 },
 ]);

在第一组函数中没有太多的解释。

在第二组中,我计算乘法(。是,乘积数量的乘积没有多少意义,但是)。 在接受种子的,中没有重载,它总是使用第一个元素( 如果没有元素,它也会无提示返回空。),因这里我必须使用一个方法链。

结果:

Aggregating arrays
------------------
 PHP [for] 0.00059 sec x1.0 (100%)
 PHP [array functions] 0.00193 sec x3.3 (+227%)
 YaLinqo 0.00475 sec x8.1 (+705%)
 YaLinqo [string lambda] 0.00515 sec x8.7 (+773%)
 Ginq 0.00669 sec x11.3 (+1034%)
 Ginq [property path] 0.03955 sec x67.0 (+6603%)
 Pinq 0.03226 sec x54.7 (+5368%)
Aggregating arrays custom
-------------------------
 PHP 0.00007 sec x1.0 (100%)
 YaLinqo 0.00046 sec x6.6 (+557%)
 YaLinqo [string lambda] 0.00057 sec x8.1 (+714%)
 Ginq 0.00046 sec x6.6 (+557%)
 Pinq 0.00610 sec x87.1 (+8615%)

所有LINQ库都执行了错误。 属性访问模式中的Ginq和Pinq执行得非常糟糕。 即使是内置函数也远远不够性能。 for 规则。

最后,使用yalinqo的自述文件中的复杂查询进行最后一次测试,它使用了几个函数和子查询:

benchmark_linq_groups("Process data from ReadMe example", 5,
 function ($e) { consume($e, [ 'products' => null ]); },
 [
 function () use ($DATA) {
 $productsSorted = [ ];
 foreach ($DATA->products as $product) {
 if ($product['quantity']> 0) {
 if (empty($productsSorted[$product['catId']]))
 $productsSorted[$product['catId']] = [ ];
 $productsSorted[$product['catId']][] = $product;
 }
 }
 foreach ($productsSorted as$catId => $products) {
 usort($productsSorted[$catId], function ($a, $b) {
 $diff = $a['quantity'] - $b['quantity'];
 if ($diff!= 0)
 return -$diff;
 $diff = strcmp($a['name'], $b['name']);
 return$diff;
 });
 }
 $result = [ ];
 $categoriesSorted = $DATA->categories;
 usort($categoriesSorted, function ($a, $b) {
 return strcmp($a['name'], $b['name']);
 });
 foreach ($categoriesSorted as $category) {
 $categoryId = $category['id'];
 $result[$category['id']] = [
 'name' => $category['name'],
 'products' => isset($productsSorted[$categoryId])? $productsSorted[$categoryId] : [ ],
 ];
 }
 return$result;
 },
 ],
 [
 function () use ($DATA) {
 return E::from($DATA->categories)
 ->orderBy(function ($cat) { return $cat['name']; })
 ->groupJoin(
 from($DATA->products)
 ->where(function ($prod) { return $prod['quantity']> 0; })
 ->orderByDescending(function ($prod) { return $prod['quantity']; })
 ->thenBy(function ($prod) { return $prod['name']; }),
 function ($cat) { return $cat['id']; },
 function ($prod) { return $prod['catId']; },
 function ($cat, $prods) {
 return array(
 'name' => $cat['name'],
 'products' => $prods );
 }
 );
 },
 "string lambda" => function () use ($DATA) {
 return E::from($DATA->categories)
 ->orderBy('$cat ==> $cat["name"]')
 ->groupJoin(
 from($DATA->products)
 ->where('$prod ==> $prod["quantity"]> 0')
 ->orderByDescending('$prod ==> $prod["quantity"]')
 ->thenBy('$prod ==> $prod["name"]'),
 '$cat ==> $cat["id"]', '$prod ==> $prod["catId"]',
 '($cat, $prods) ==> [
"name" => $cat["name"],
"products" => $prods
 ]');
 },
 ],
 [
 function () use ($DATA) {
 return G::from($DATA->categories)
 ->orderBy(function ($cat) { return $cat['name']; })
 ->groupJoin(
 G::from($DATA->products)
 ->where(function ($prod) { return $prod['quantity']> 0; })
 ->orderByDesc(function ($prod) { return $prod['quantity']; })
 ->thenBy(function ($prod) { return $prod['name']; }),
 function ($cat) { return $cat['id']; },
 function ($prod) { return $prod['catId']; },
 function ($cat, $prods) {
 return array(
 'name' => $cat['name'],
 'products' => $prods );
 }
 );
 },
 ],
 [
 function () use ($DATA) {
 return P::from($DATA->categories)
 ->orderByAscending(function ($cat) { return $cat['name']; })
 ->groupJoin(
 P::from($DATA->products)
 ->where(function ($prod) { return $prod['quantity']> 0; })
 ->orderByDescending(function ($prod) { return $prod['quantity']; })
 ->thenByAscending(function ($prod) { return $prod['name']; })
 )
 ->onEquality(
 function ($cat) { return $cat['id']; },
 function ($prod) { return $prod['catId']; }
 )
 ->to(function ($cat, $prods) {
 return array(
 'name' => $cat['name'],
 'products' => $prods );
 });
 },
 ]);

结果:

Process data from ReadMe example
--------------------------------
 PHP 0.00620 sec x1.0 (100%)
 YaLinqo 0.02840 sec x4.6 (+358%)
 YaLinqo [string lambda] 0.02920 sec x4.7 (+371%)
 Ginq 0.07720 sec x12.5 (+1145%)
 Pinq 2.71616 sec x438.1 (+43707%)

GroupJoin 杀死Pinq的性能。 我想原因与 join 测试中的原因相同。

所有结果

Iterating over 1000 ints
------------------------
 PHP [for] 0.00006 sec x1.0 (100%)
 PHP [array functions] 0.00011 sec x1.8 (+83%)
 YaLinqo 0.00041 sec x6.8 (+583%)
 Ginq 0.00075 sec x12.5 (+1150%)
 Pinq 0.00169 sec x28.2 (+2717%)
Generating array of 1000 integers
---------------------------------
 PHP [for] 0.00025 sec x1.3 (+32%)
 PHP [array functions] 0.00019 sec x1.0 (100%)
 YaLinqo 0.00060 sec x3.2 (+216%)
 Ginq 0.00107 sec x5.6 (+463%)
 Pinq 0.00183 sec x9.6 (+863%)
Generating lookup of 1000 floats, calculate sum
-----------------------------------------------
 PHP 0.00124 sec x1.0 (100%)
 YaLinqo 0.00381 sec x3.1 (+207%)
 YaLinqo [string lambda] 0.00403 sec x3.3 (+225%)
 Ginq 0.01390 sec x11.2 (+1021%)
 Pinq * Not implemented
Counting values in arrays
-------------------------
 PHP [for] 0.00023 sec x1.0 (100%)
 PHP [arrays functions] 0.00052 sec x2.3 (+126%)
 YaLinqo 0.00056 sec x2.4 (+143%)
 YaLinqo [string lambda] 0.00059 sec x2.6 (+157%)
 Ginq 0.00129 sec x5.6 (+461%)
 Pinq 0.00382 sec x16.6 (+1561%)
Counting values in arrays deep
------------------------------
 PHP [for] 0.00064 sec x1.0 (100%)
 PHP [arrays functions] 0.00323 sec x5.0 (+405%)
 YaLinqo 0.00798 sec x12.5 (+1147%)
 Ginq 0.01416 sec x22.1 (+2113%)
 Pinq 0.04928 sec x77.0 (+7600%)
Filtering values in arrays
--------------------------
 PHP [for] 0.00049 sec x1.0 (100%)
 PHP [arrays functions] 0.00072 sec x1.5 (+47%)
 YaLinqo 0.00094 sec x1.9 (+92%)
 YaLinqo [string lambda] 0.00094 sec x1.9 (+92%)
 Ginq 0.00295 sec x6.0 (+502%)
 Pinq 0.00328 sec x6.7 (+569%)
Filtering values in arrays deep
-------------------------------
 PHP [for] 0.00514 sec x1.0 (100%)
 PHP [arrays functions] 0.00739 sec x1.4 (+44%)
 YaLinqo 0.01556 sec x3.0 (+203%)
 YaLinqo [string lambda] 0.01750 sec x3.4 (+240%)
 Ginq 0.03101 sec x6.0 (+503%)
 Pinq 0.05435 sec x10.6 (+957%)
Sorting arrays
--------------
 PHP 0.00037 sec x1.0 (100%)
 YaLinqo 0.00161 sec x4.4 (+335%)
 YaLinqo [string lambda] 0.00163 sec x4.4 (+341%)
 Ginq 0.00402 sec x10.9 (+986%)
 Ginq [property path] 0.01998 sec x54.0 (+5300%)
 Pinq 0.00132 sec x3.6 (+257%)
Joining arrays
--------------
 PHP 0.00016 sec x1.0 (100%)
 YaLinqo 0.00065 sec x4.1 (+306%)
 YaLinqo [string lambda] 0.00070 sec x4.4 (+337%)
 Ginq 0.00105 sec x6.6 (+556%)
 Ginq [property path] 0.00194 sec x12.1 (+1112%)
 Pinq 1.21249 sec x7,577.5 (+757648%)
Aggregating arrays
------------------
 PHP [for] 0.00059 sec x1.0 (100%)
 PHP [array functions] 0.00193 sec x3.3 (+227%)
 YaLinqo 0.00475 sec x8.1 (+705%)
 YaLinqo [string lambda] 0.00515 sec x8.7 (+773%)
 Ginq 0.00669 sec x11.3 (+1034%)
 Ginq [property path] 0.03955 sec x67.0 (+6603%)
 Pinq 0.03226 sec x54.7 (+5368%)
Aggregating arrays custom
-------------------------
 PHP 0.00007 sec x1.0 (100%)
 YaLinqo 0.00046 sec x6.6 (+557%)
 YaLinqo [string lambda] 0.00057 sec x8.1 (+714%)
 Ginq 0.00046 sec x6.6 (+557%)
 Pinq 0.00610 sec x87.1 (+8615%)
Process data from ReadMe example
--------------------------------
 PHP 0.00620 sec x1.0 (100%)
 YaLinqo 0.02840 sec x4.6 (+358%)
 YaLinqo [string lambda] 0.02920 sec x4.7 (+371%)
 Ginq 0.07720 sec x12.5 (+1145%)
 Pinq 2.71616 sec x438.1 (+43707%)

结束语

如果需要对相对较小的数据集执行查询,例如从web服务返回,则可以使用YaLinqo或者 Ginq。

YaLinqo具有更好的性能,具有更多功能,具有更好的文档。 它是一个依赖现代PHP特性的简单库。 它同时支持匿名函数和字符串 lambda ( 在所有种类中)。 它不包含除了iteraror周围的包装器以外的任何类,因这里很容易学习旧的PHP数组。

Ginq使用多个类迭代器。集合和执行器。 由于这个原因,它更接近于来自. NET的LINQ。 然而,它有一个价格。 .NET 不同,用PHP实现的定制词典要比原生数组慢得多。 另一方面,public 类的类是. NET 开发人员的外国人,但是使用SPL的PHP开发人员可以看到它们。 它们也有一个价格- 使用SPL迭代器迭代比 yield 慢得多。 总体而言,Ginq的速度比YaLinqo慢 1.5 -3。

由于简单查询,Pinq的速度慢,没有多少架构可以降低应用程序的速度。 这个库有一个漂亮的网站,支持数据库的独特特性,一个复杂的架构,它是版本 3,所以我很遗憾,这个库绝对无法使用。 我希望开发人员提高性能并至少实现一个完整的具有特色的查询提供程序。 当需要LINQ到数据库时,库可以能成为选择库的库。

另一个要考虑的库是 Underscore.php.,它不是惰性的,但它遵循同样的功能性思想。

其他 图书馆

我在俄文中编写了一篇比较旧的"linq"库的文章: LINQ,Phinq,PHPLinq和 Plinq。 但是,我不能推荐使用其中任何一个。 它们是不完整。未测试。没有文档化和最重要的,它们不是 LINQ - 它们都不支持惰性评估。 详细讨论这些问题会在更新的库的存在中浪费时间。

其中唯一值得提及的库是 PHPLinq。 它支持查询数据库,实际上是很多数据库。 尽管这个库几乎没有测试过,但是函数调用的顺序是固定的( 它更像是生成SQL的DAL ),singlefirst,在生产过程中,我们将不会像这样使用代码,但是你可以自己决定这个函数。

许可证

  • YaLinqoPerf - WTFPL*许可证
  • YaLinqo - 简化的BSD许可证
  • Ginq - MIT许可证
  • Pinq - 许可证+ BSD 3-clause 许可证( 依赖关系)

历史记录

  • 2015-05-30: 第一个版本

COM  PHP  LINQ  GIN