HackerReposA FIELD GUIDE TO OPEN SOURCE
ProSponsor the shelf♧ 0
← Back to top all-time
NoiseByNorthwest
php-spx
C2021–2025
NoiseByNorthwest / php-spx

php-spx

A simple & straight-to-the-point PHP profiling extension with its built-in web UI

5Distinct people
5Mentions
0Explicit recommenders
2.6KGitHub stars
REPOSITORY AT A GLANCE

What to know before exploring

What it is

A simple & straight-to-the-point PHP profiling extension with its built-in web UI

GitHub

Where it is used

No documented product use has been verified yet.

What people are saying

No recommendation phrase was found in the indexed comments.

No classified warning is attached to this page. Read the discussions before treating silence as approval.

Also in these discussions

No other repository is repeatedly linked in the same discussions.

This week on Hacker News

Nov 7–Nov 14 UTC · Direct repository links and separately labeled related project stories. Indexed through 11/14/2026, 5:04:57 PM UTC.

No direct repository links were found in the indexed HN discussions over the past seven days. Older discussions are available below.

Mentions over time

HN mentions by year5 dated mentions

Observed mentions: 2021-03-04 11:01:03 UTC to 2025-06-05 14:39:43 UTC.

0122021-01-01 00:00:00 UTC to 2022-01-01 00:00:00 UTC (exclusive): 2 mentions; 1 submissions, 1 comments20212022-01-01 00:00:00 UTC to 2023-01-01 00:00:00 UTC (exclusive): 1 mentions; 0 submissions, 1 comments2023-01-01 00:00:00 UTC to 2024-01-01 00:00:00 UTC (exclusive): 0 mentions; 0 submissions, 0 comments20232024-01-01 00:00:00 UTC to 2025-01-01 00:00:00 UTC (exclusive): 0 mentions; 0 submissions, 0 comments2025-01-01 00:00:00 UTC to 2026-01-01 00:00:00 UTC (exclusive): 2 mentions; 1 submissions, 1 comments2025

Counts come from the repository’s dated source evidence. Index cutoff: 2026-11-02 08:16:59 UTC.

View mention counts and date ranges
UTC ranges include their start and exclude their end. Select a period to filter the source evidence.
PeriodStart (UTC)End (exclusive, UTC)MentionsSubmissionsComments
20212021-01-01 00:00:00 UTC2022-01-01 00:00:00 UTC211
20222022-01-01 00:00:00 UTC2023-01-01 00:00:00 UTC101
20232023-01-01 00:00:00 UTC2024-01-01 00:00:00 UTC000
20242024-01-01 00:00:00 UTC2025-01-01 00:00:00 UTC000
20252025-01-01 00:00:00 UTC2026-01-01 00:00:00 UTC211

What Hacker News says

5 sources
Its not evident to me what this does... It says that it "prints per query and average metrics", but it seems to just output some extremely generic stats. Does "query" actually mean request here? I see there's a wordpress-specific wpdb mechanism - though it just seems to report total time spent on queries, and the slowest query. Surely something like Query Monitor is a far better tool for that? A tool that everyone should be aware of and using is php-spx - its absolutely glorious for performance profiling/investigation. xhgui is similar, but bulkier and (in my experience) less accurate/insightful. https://github.com/NoiseByNorthwest/php-spx
In Phptop: Simple PHP ressource profiler, safe and useful for production sites
In my experience, Xdebug is not suited to the task. It has a big impact on the executed process, so profiling is slow and biased. The results need some extra processing to get interpreted, which is a pain when experimenting with code changes. It's been a long time since I last used Xdebug to profile anything, so maybe the situation is better now. My preferred tool is php-spx. Once the extension is loaded into PHP, within seconds you get a flamegraph of where time is spent. It's also easy to apply to command-line scripts. And it's versatile enough (with the right settings) to profile long-running or memory heavy scripts. https://github.com/NoiseByNorthwest/php-spx SPX could be loaded with docker-compose like this article does for Xdebug. But if you already have a PHP environment, the easiest way to install it is to compile it (sudo apt ins
Read the rest
tall php-dev && make && cp modules/spx.so /usr/lib/php/....).
In How to profile your PHP applications with Xdebug
Well, I have ADHD. I've found the most effective approach (on top of treatment) that helps me retain focus is reexec-on-save, a la `while :; do tput clear; $thing; inotifywait -q -e moved_to .; done`. I usually have a dozen of those in old shell histories (^R FTW). (Ha, my laptop actually has exactly 12, and my other machine has 23 - although ignoredups is off...) $thing might be `bash ./script.sh` (because my text editor's atomic rename doesn't understand execute bits >.>), `php script.php` or `gcc -O0 script.c && ./script`. (Also, as an aside I used to use `-e close_write $file` until I realized watching even giant directories is equivalently efficient to watching a file.) Shell scripts (the small kind that run few subprocesses) are typically fast. Likewise, small C programs of <1000-2000 lines compile just about instantly on modern har
Read the rest
dware; and where modern hardware isn't available and what I'm trying to do doesn't leverage too many libraries or whatnot, tcc has been able to swing the balance firmly in my favor in the past, which has been great. But for better or worse, PHP is currently the language I use the most. Because it's faster than Python and Ruby. A while back I wanted to do a bit of analysis on a dataset of information that was only published as a set of PDF documents... yayyy. But after timidly gunzipping the stream blocks and googling random bits of PDF's command language ("wat even is this"), I discovered to my complete surprise that it was trivial to interpret the text coordinate system and my first "haha let's see how bad this is" actually produced readable text on pretty much the first go. (To be pedantic, step #-1 was "draw little boxes where the text should be", step #0 was "how to x,y correctly" (with a side serving of "...those boxes look quite reasonably positioned..."), and step #1 was "replace boxes with texWHAT it worked?!") With rendering basically... viable (in IIRC 300-500 LOC O.o), the next step was the boring stir-the-soup-for-144-hours bespoke state machine that cross-correlated text coordinates with field meanings ("okay, that's a heading, and the next text instruction draws the field value underneath. OK, assert that the heading is bold, the value is not, and they're both exactly the same (floating-point) Y position"). While that part took a while, it was mostly extremely easy, because I was pretty much linearly writing the script "from start to finish", ie just chipping away at the rock face of the task at hand until I processed an entire document, then the next document ("oh no"), then the next one ("ugh") and so forth ("wait, the edge cases are... decreasing? :D"). My workflow was pretty much founded entirely on the above-noted method where I would re-exec the script from scratch upon save. Loading/gunzipping a given PDF and getting to the point where the little pipeline would crash would typically complete well before I had a chance to release the CTRL key after hitting CTRL+S. So while the process was objectively quite like stirring soup, it did not feel like that at all and I was able to kind of float a bit as my brain cohesively integrated the mental model of the architecture I was building without any distractions, pauses or forced context switches getting jammed in the mental encoding process like so many wrenches. Soon 15 documents were handled correctly, then 20, then 30, then 100 ("oooh, if all the items on the page add up exactly right it pushes line 2 of the summary heading down to the second page! Hmmm... how on earth to special-case that without refactoring to look at more than 1 page at a time..."), and then I hit some sort of threshold and it suddenly just started ticking through PDFs like crazy without asserting. Which was both awesome and a Problem™: the thing ran at something like ~60 PDFs/sec, and while jumping to just after the last successfully-processed PDF on restart worked great when the code crashed constantly, now I was sitting spinning for tens of seconds, getting distracted as I anticipated the next crash. ADHD(R)(TM). I wasn't surprised to learn from htop that the script was disk-bound; for some reason my ZFS mirror setup will happily read sequentially at 200MB/s, but thousands-of-tiny-files situations are... suffice to say apt unconditionally takes 60 seconds to install the smallest thing, unless the entire package db is in the FS cache. I'm not sure why. The PDFs were sharded sanely, but they were still in separate files. So I decided to pack them all into a giant blob, and since there weren't too many PDFs and they were numbered sequentially I used a simple offset-based index at the front of the blob where `fseek(data_start + (<PDF ID> * 4)); $o = fread(4); fseek($o);` would give me random seeking when I needed it, or I could just fseek() to data_start and start reading directly. Reading the blob instead promptly pegged a single CPU core (yay!), and gave me IIRC ~150-200+ PDFs/sec. This was awesome. But I was still just a tiny bit curious, so after googling around for a profiler and having a small jawdrop moment about SPX (https://github.com/NoiseByNorthwest/php-spx), I had a tentative look at what was actually using the most CPU (via `SPX_ENABLED=1 php ./script.php`, which will automatically print a one-page profile trace to stdout at graceful exit or ^C). Oh. The PDF stack machine interpreter is what's taking all the CPU time. That tiny 100 line function was the smallest in the whole script. lol So, I moved that function to the preprocessor/packer, then (after some headscratching) serialized the array of tokenized commands/strings into the blob by prefixing commands with \xFF and strings with \xFF\xFE\xFF so I could explode() on \xFF and tell commands from strings by checking if the previous entry was \xFE (and just skip entries of '\xFE' when I found them) :D. Then I reran the preprocessor to regenerate the pack file. $ php convert_dlcache.php Scanning... 24/66060 67,927 entries [4.27 sec] Sorting... 10013-343271 [1.61 sec] data_start=1373094 * 67,920/67,927 99.99% 83/s 00:00 13:58 343259 Thankfully I've only needed to run the preprocessor rarely, like for example I only needed to run it twice today because it promptly truncated its pack file when I accidentally reran it after the first run (yay). Yes, it really does take ~14 minutes, and yes, there are just under 70,000 PDFs. Then I reran the pipeline script. $ php conv2.php 67,927/67,927 890/s 01:16 00:00 343271 Complete Oh. 900 PDFs/sec. Uh, what happens if I... set up a simple pcntl_fork() + stream_socket_pair() multi-process worker system...? $ php conv2.php Reading... done 4 workers, (16,982 x 3) + (16,981 x 1) 62,162/67,927 1,800/s (469/s 166319, 444/s 237743, 442/s 302338, 444/s 340327) 00:34 00:03 ^C "Oh. Okay." ":D" (The workers disappear from the output as they complete, so I ^C'd it just before they all exited, at 3 seconds left.) So. 68,000 PDFs/sec on a not particularly amazing 3.3Ghz i3-3220 with 1600MHz RAM. PHP 8's JIT is aweso--wait, is the JIT actually on? Oh. It's off by default. $ php -dopcache.enable_cli=1 -dopcache.jit_buffer_size=32M conv2.php Reading... done 4 workers, (16,982 x 3) + (16,981 x 1) 58,206/67,927 2,537/s (669/s 163803, 623/s 230254, 619/s 300420, 627/s 338470) 00:22 00:03 *Blinks* (In small voice) "I am processing/unit-testing 68k documents in 22 seconds. At over two and a half thousand PDFs a second." I also noticed that PID USER PRI NI VIRT RES SHR S CPU% MEM% TIME+ Command 31511 i336 20 0 276M 19864 5736 R 100. 0.2 0:18.25 php conv2.php 31513 i336 20 0 276M 19856 5732 R 100. 0.2 0:18.35 php conv2.php 31512 i336 20 0 276M 19728 5600 R 100. 0.2 0:18.32 php conv2.php 31510 i336 20 0 276M 19764 5648 R 97.7 0.2 0:18.03 php conv2.php 31509 i336 20 0 275M 34072 20036 S 0.7 0.4 0:00.19 php conv2.php the workers are only using 20MB RAM each. This is with object deferencing going on, after I kinda started going crosseyed and properly moved my JBOGF (Just a Bunch of Globals and Functions) into a proper class (albeit a static one, since I'm still learning, and construction/deconstruction would probably slow things down too). I also note that with the JIT off, VIRT is 115M, RES is 16,312K and SHR is 2,412K for all workers, with the master taking 29M RES and 15M SHR. -- Why'd I write all this?! To make the point that a) live reexec on save is an awesome programming model when it can be applied, and b) PHP is my incredibly awkward gold standard reference, lol. (In rerunning everything for this post I discovered the JIT and learned my script could go even faster!) I've been keeping vague tabs on recent language developments from a bit of a distance for a while now, and am very interested to dig into Rust and Zig's incremental compilation capabilities at some point. Based on what I'm hearing I don't know if I should just go dive in yet though - to be entirely honest, there are multiple realms of applications I simply cannot write in PHP - including things as simple as console CLIs, because the neccessary TTY I/O control (eg, turning TTY echo off and reading 1 char at a time) remain unavailable (see also: multiple years-old long-forgotten bugs/feature requests). I don't really want to discover what's possible only to find myself between a rock and a hard place as my programs start to grow and I hit invisible quadratic brick walls that persist after I enable every "fastest possible" setting the language offers. That happened a few years ago when I tried to play around with FLTK in C++; once build times were around the 3-4 second mark (*with* -O0 and precompiled headers), regardless of the fact that I'd been working on the project for enough time I was invested in it, I just gave up. Much more recently I installed .NET Core the other day to run and dissect a small F# program. Running `dotnet fsi` cold took 5 seconds to reach a REPL prompt (ouch), and while warmed-up reruns would only take 1 second, I found actually loading a ~300-line program would take a non-reducible ~4-5 seconds. Everyone's different, and sometimes things people say can look crazy, or stupid, or "...how even...", but they can still be true for that person. For me, waiting 4 seconds for an interpreter/runtime/etc to reach a useful milestone such as "I can actually interact at all with it" is untenable. Yup. I go straight to "I've waited for this thing to do the thing 200 times today <cartoon punching fight cloud>" after the 3rd pause. I am utterly incompatible with the Old Guard "code's compiling!" way of doing things. It puts me straight into defensive not-in-my-comfort-zone mode. Completely open-minded about Crystal, which I've heard repeatedly good things about. Yes, of course, I haven't tested it (yet), again because I don't want to go "wow this is awesome... except I don't have the patience for it :(". The status quo noted above remains an actively unsolved problem; trying to figure out how many interesting things I can wedge into the "focusable space" defined by "how big I can make my program before the programming languge slows down" is getting boring. Perhaps I'm just stuck here because I'm using older(ish) hardware, and once I finally figure out that problem I'll suddenly realize all of this deep analysis was unnecessary. heh
In Crystal 1.0 – What to expect

MAKE YOURSELF AT HOME

A few shortcuts

/
Search the collection
?
Open this guide
Esc
Close dialogs and navigation

Your saved repositories and interests stay in this browser.