performance bottlenecks down to the function level
The Symfony profiler shows what happened during a request, but Blackfire shows why it took so long. With callgraphs, reference profiles and CI integration, Blackfire profiling exposes exactly which function calls are really slowing a Symfony application down.
Table of contents
- 1. Blackfire versus the built-in Symfony profiler
- 2. Installing the probe and connecting it to Symfony
- 3. Reading callgraphs: wall time, CPU time, I/O
- 4. Profiling requests directly from the Symfony profiler
- 5. Reference profiles and spotting regressions
- 6. Blackfire recorder for user sessions
- 7. Integrating Blackfire build into the CI pipeline
- 8. Identifying Doctrine and Twig calls in the callgraph
- 9. Blackfire compared to other profiling approaches
- 10. Summary
- 11. FAQ
1. Blackfire versus the built-in Symfony profiler
Blackfire profiling solves a problem the built-in Symfony profiler deliberately does not address: the web debug toolbar shows the total runtime of a request and roughly how much time goes to Doctrine, Twig and HTTP calls, but it does not show which single function within those categories causes the largest share. This is exactly where Blackfire steps in: it captures every single function call in the PHP process with exact runtime, memory usage and call frequency, displayed as an interactive callgraph.
The second major difference lies in comparability. While the Symfony profiler only shows the last request, Blackfire permanently stores every profile and allows a direct side-by-side comparison of two profiles. That makes it possible to see exactly which function suddenly takes more time after a deployment, instead of merely suspecting that something got slower. For Symfony teams that want to systematically prevent performance regressions, this comparison mechanism is the decisive added value over pure ad hoc debugging.
2. Installing the probe and connecting it to Symfony
The technical foundation of Blackfire profiling is a PHP extension called the probe, installed on every server that should be profiled. Once the probe is active, any single request can be triggered via a special header or via the Blackfire browser extension, without affecting normal requests at all. The decisive design point: without an active profiling header, the probe behaves completely neutral and has practically no overhead on regular operation.
For Symfony projects there is additionally the blackfire/php-sdk bundle, which enables programmatic profiling directly from PHP code. This is especially valuable for background processes such as Messenger consumers or cron jobs, which cannot be triggered via an HTTP header but still need detailed Blackfire profiling, because they often cause the biggest and hardest to debug performance problems.
# Install the Blackfire probe and agent inside a Symfony container
curl -sL https://packagecloud.io/gpg.key | apt-key add -
echo "deb http://packages.blackfire.io/debian any main" | tee /etc/apt/sources.list.d/blackfire.list
apt-get update && apt-get install -y blackfire-agent blackfire-php
# Configure server id and token from your Blackfire account
blackfire-agent -register -server-id=SERVER_ID -server-token=SERVER_TOKEN
# Trigger a single profiled request against a running Symfony app
blackfire curl https://staging.example.com/checkout/cart
3. Reading callgraphs: wall time, CPU time, I/O
The callgraph is the central visual element of Blackfire profiling. Every box in the graph represents a function, its size shows the relative time share, and arrows show the call hierarchy. The decisive difference from simple profilers: Blackfire strictly separates wall time, meaning the actually elapsed time including waiting on external resources, from CPU time, meaning the time the processor actually spent computing.
This separation is especially valuable for Symfony applications with many external API calls: a function with high wall time but low CPU time spends most of its time waiting, for example on an external API or a slow database connection. A function with high CPU time, on the other hand, points to inefficient code itself, such as a nested loop or an expensive serialization. Blackfire displays both metrics in parallel within the same callgraph, so the distinction becomes immediately visible.
4. Profiling requests directly from the Symfony profiler
An often overlooked feature: anyone with the Blackfire browser extension installed sees an extra button right inside the Symfony web debug toolbar that creates a Blackfire profile for exactly the request currently being viewed. This completely eliminates the detour through the command line and lets you spontaneously profile during normal development work as soon as a page subjectively feels slow.
This approach is especially effective combined with the Symfony profiler storage: once a suspicious request has been identified in the profiler history, that same request can be sent to Blackfire with a single click, without having to manually recreate the exact parameters, session cookies or POST data. This seamless integration is one of the main reasons Blackfire profiling is so widespread in the Symfony community.
<?php
// src/Command/ProfileImportCommand.php
declare(strict_types=1);
namespace App\Command;
use Blackfire\ClientConfiguration;
use Blackfire\Client as BlackfireClient;
use Blackfire\Profile\Configuration as ProfileConfiguration;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
/**
* Wraps a long-running background job in a Blackfire profiling session,
* since it cannot be triggered via a browser HTTP header.
*/
#[AsCommand(name: 'app:import:products')]
final class ProfileImportCommand extends Command
{
protected function execute(InputInterface $input, OutputInterface $output): int
{
$config = new ClientConfiguration();
$config->setClientId($_ENV['BLACKFIRE_CLIENT_ID']);
$config->setClientToken($_ENV['BLACKFIRE_CLIENT_TOKEN']);
$blackfire = new BlackfireClient($config);
$probe = $blackfire->createProbe(new ProfileConfiguration());
$this->productImporter->importAll(); // the actual expensive work
$blackfire->endProbe($probe);
return Command::SUCCESS;
}
}
5. Reference profiles and spotting regressions
The real added value of Blackfire profiling only shows once you compare two profiles over time. A profile is marked as a reference, typically a profile from a known well performing version of the application. Every new profile can then be compared directly against this reference, with Blackfire automatically highlighting which functions take more or less time than in the reference.
In practice, this mechanism is used to test concrete performance hypotheses: a developer suspects that a new Doctrine query is slower than the old implementation, profiles both versions and compares them directly. The comparison shows not only the total runtime but also whether the difference actually lies in the suspected function or arose at an unexpected place in the code that would never have been noticed without Blackfire.
6. Blackfire recorder for user sessions
Some performance problems only occur during complex, multi-step user interactions, such as a complete checkout flow with several form steps. For such scenarios, Blackfire offers the recorder, which records a complete browser session and turns it into a repeatable scenario. This scenario can then be replayed as often as needed with active profiling, without manually redoing the interaction every time.
This is especially valuable for regression testing: a checkout flow recorded once can be automatically re-profiled after every deployment, and Blackfire profiling immediately shows whether a specific code change has slowed down the entire flow, even if the slowdown is spread across several consecutive requests and would not stand out in a single request profile.
7. Integrating Blackfire build into the CI pipeline
The most rigorous use of Blackfire profiling is integrating it into the continuous integration pipeline through the concept of builds. A build defines concrete assertions, for example that a certain route must never cause more than 50 milliseconds of CPU time or more than 20 SQL queries. If the build runs in the CI pipeline against every pull request, the build fails as soon as a code change exceeds one of these limits, long before the code reaches production.
This automation fundamentally changes how performance is handled: instead of discovering performance problems only after user complaints in production, Blackfire as a CI gate prevents regressions from ever being merged in the first place. For Symfony teams with many contributors, this is the most reliable way to consistently enforce performance standards without having to manually profile every pull request.
# .blackfire.yml — assertions checked automatically on every CI run
tests:
"Checkout page must stay fast":
path: "/checkout"
assertions:
- "main.wall_time < 200ms"
- "metrics.sql.queries.count < 15"
- "metrics.symfony.container_boot.wall_time < 30ms"
"Product listing must not regress":
path: "/products?category=example"
assertions:
- "main.peak_memory < 32mb"
8. Identifying Doctrine and Twig calls in the callgraph
A common use case of Blackfire profiling in Symfony projects is identifying Doctrine related bottlenecks. The callgraph shows every single PDOStatement::execute call with its exact runtime, which makes the classic N plus 1 problem immediately visible: instead of a single query with a join, hundreds of nearly identical but separately executed queries suddenly appear in the callgraph.
Analyzing Twig rendering is similarly helpful. Complex templates with many nested includes and expensive filters show up in the callgraph as a deep nesting of Twig\Template::render calls. Blackfire makes it visible whether rendering time is evenly distributed or whether a single, often repeatedly included component causes most of the time, a pattern that is easily missed without profiling.
9. Blackfire compared to other profiling approaches
To position Blackfire profiling within the overall toolbox for Symfony performance, a direct comparison with the alternatives developers typically have to choose from helps.
| Tool | Level of detail | Comparability over time | CI integration |
|---|---|---|---|
| Symfony web debug toolbar | Rough categories per request | No, only the last request | None |
| Xdebug profiler (Cachegrind) | Very high, every function | Manual with external tool | Cumbersome, no native format |
| Blackfire | Very high, every function | Yes, native reference profiles | Yes, via .blackfire.yml builds |
| APM (New Relic, Datadog) | Medium, traces instead of callgraph | Yes, via dashboards | Limited, more for monitoring |
The practical difference: Xdebug delivers comparably deep data to Blackfire, but without native comparison and CI features, and with noticeable overhead that makes Xdebug unsuitable for production profiling. APM tools excel at continuous monitoring across many servers, but are weaker at pinpoint, function-level deep analysis of a single request. Blackfire deliberately positions itself between both worlds: deep enough for the function level, but lightweight enough for targeted on demand profiling in production.
Mironsoft
Symfony performance analysis, Blackfire integration and CI gates
Do you really know which function is slowing down your Symfony app?
We set up Blackfire profiling in your Symfony application, create reference profiles for critical routes, and integrate performance assertions as a fixed part of your CI pipeline.
Profiling setup
Probe installation and integration into the Symfony profiler
Callgraph analysis
Find concrete function bottlenecks in Doctrine, Twig and custom code
CI gates
Set up performance assertions as a pull request check
10. Summary
Blackfire profiling goes where the built-in Symfony profiler stops: instead of rough categories, the callgraph shows every single function with exact wall time, CPU time and memory usage. The combination of probe installation, direct integration into the Symfony web debug toolbar, and the recorder for complex user sessions covers practically every profiling scenario, from single requests to multi-step checkout flows.
The biggest strategic advantage lies in automation through reference profiles and CI builds: performance regressions are not first discovered in production but already block the pull request that caused them. For Symfony teams that want to establish performance as a fixed part of their quality assurance, Blackfire is the tool that turns profiling from an occasional debugging measure into a continuous, automated process.
Blackfire Profiling for Symfony Apps — the essentials at a glance
Core principle
The probe extension captures every function call with wall time, CPU time and memory usage as a callgraph.
Reference profiles
Mark a profile as a baseline and compare every new profile directly against it to spot regressions.
Recorder
Record complete user sessions and profile them repeatedly, ideal for multi-step checkout flows.
CI integration
.blackfire.yml assertions block pull requests that exceed defined performance limits.