为何array_filter()比foreach+if组合执行速度慢?
array_filter() slower than foreach + if, and how to speed it up? Great question—let’s break this down clearly:
Why array_filter() lags behind
The core reason for the speed gap is callback function overhead. When you use array_filter() with an anonymous closure, PHP has to do extra work for every element in the array:
- It invokes the closure each time, which involves stack setup and teardown for function calls.
- The
use ($q)syntax adds overhead to bind the variable from the parent scope into the closure’s isolated scope.
In contrast, your foreach loop runs the strpos check directly in the current scope—no function call overhead, no scope binding, just straight execution. Even though array_filter() is a built-in function, its flexibility (supporting any custom callback) comes with this unavoidable performance cost. Your test results (1.7-2.1x speed difference) make perfect sense here: the more elements you process, the more cumulative overhead the closure calls add up, widening the gap.
How to speed up array_filter()
You’ll never make it as fast as raw foreach (since callback overhead can’t be eliminated entirely), but you can narrow the gap with these tweaks:
1. Use arrow functions (PHP 7.4+)
Arrow functions have lower overhead than traditional closures because they automatically inherit variables from the parent scope (no need for explicit use), and their internal implementation is more lightweight.
Example:
$res = array_filter($arr, fn($val) => strpos($val, $q) !== false);
2. Skip closures with global variables (not recommended for readability)
If you’re willing to trade some code cleanliness for speed, use a named function with a global variable to avoid closure scope binding:
$q = '555'; // Declare as global function filter_match($val) { global $q; return strpos($val, $q) !== false; } $res = array_filter($arr, 'filter_match');
This cuts down on closure creation and scope binding overhead, making array_filter() noticeably faster than the closure version—though still slower than foreach.
3. Leverage built-in functions directly (when applicable)
If your check logic can use a PHP built-in function, passing its name directly avoids custom callback overhead. For PHP 8.0+, str_contains() is a perfect fit for your use case:
$res = array_filter($arr, fn($val) => str_contains($val, $q));
(While this still uses an arrow function, str_contains is a native opcode which is faster than manual strpos checks in some scenarios.)
4. Precompile complex check logic (for future changes)
If you plan to switch to more complex checks (like regex), precompile your pattern to avoid repeated compilation overhead:
$pattern = preg_quote($q, '/'); $regex = "/$pattern/"; $res = array_filter($arr, fn($val) => preg_match($regex, $val));
This is especially useful if your check logic becomes more elaborate later.
Final note
Even with these optimizations, array_filter() will likely never match the raw speed of foreach—it can’t escape callback function overhead entirely. But if you prefer the cleaner, declarative syntax of array_filter(), these tweaks will get you as close as possible.
内容的提问来源于stack exchange,提问作者ABelikov

