benchmark_linq_groups
function: for bare PHP, YaLinqo, Ginq and Pinq, respectively. 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
in Pinq is missing, the documentation says to use the standard function. What, in fact, we are doing.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%)
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%)
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(); }, ]);
count
function that accepts a predicate, so you have to build a chain of methods. As it turns out later, this is far from the only limitation of Pinq: there are very few methods and they are very limited.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%)
count()
is called on it ... But it's still too early to be surprised! 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_map
and array_filter
arguments in different order, as a result, it is difficult to understand what happens array_filter
.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
ugly, but, usort
adjusted, you can write such functions without thinking. Sorting with LINQ looks almost perfectly clean. This is also the first time that you can take advantage of the pleasures of “access to properties” in Ginq - the code can no longer be made more beautiful.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%)
benchmark_linq_groups("Joining arrays", 100, 'consume', [ function () use ($DATA) { $usersByIds = [ ]; foreach ($DATA->users as $user) $usersByIds[$user['id']][] = $user; $pairs = [ ]; foreach ($DATA->orders as $order) { $id = $order['customerId']; if (isset($usersByIds[$id])) { foreach ($usersByIds[$id] as $user) { $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, ]; }); }, ]);
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%)
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%)
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%)
Source: https://habr.com/ru/post/259155/
All Articles