Hi hackers,

This is v4 of the patch adding a stats_last_updated column to
pg_stat_statements.

The thread is somehow shifted to things that are not related to the
patch initial idea. I decided to return to the beginning.
I simplified the implementation to use GetCurrentTimestamp() directly
instead of calculating the timestamp from
GetCurrentStatementStartTimestamp() + total_time. The previous optimization was
premature, benchmark testing proves GetCurrentTimestamp() adds no
measurable overhead.

Test environment: Dockerized Linux x86_64 under Windows host (the
worst possible combination), PostgreSQL 19devel, gcc-14.2.0
Test: 1,000,000 iterations of PERFORM 1 (fastest possible statement)

Unpatched PostgreSQL:
  track='none' (baseline): 562.07ms average
  track='all' (tracking):  719.17ms average
  Overhead: 157.10ms (27.9%)

Patched PostgreSQL (with stats_last_updated):
  track='none' (baseline): 548.95ms average
  track='all' (tracking):  732.50ms average
  Overhead: 183.55ms (33.4%)

Direct comparison (what matters):
  Unpatched track='all': 719.17ms
  Patched track='all':   732.50ms
  Difference: +13.33ms (+1.85%)
  Per-statement: 13.33ms / 1,000,000 = 13 nanoseconds

The baseline comparison shows patched is actually faster (-13ms),
which is impossible.
This confirms the 13ms variance is a measurement noise, not real overhead.

Real-world impact for 100ms query is 0.000013% overhead

GetCurrentTimestamp() is the standard approach used throughout PostgreSQL for
monitoring features. The measured overhead of 13 nanoseconds per statement is
negligible for any realistic workload and well within measurement noise.

The implementation captures GetCurrentTimestamp() before acquiring the spinlock,
so no syscall occurs while holding the lock. This is simple, correct, and has
no measurable performance impact.

Changes from v2-v3:
- Simplified implementation to use GetCurrentTimestamp() directly
- Removed complex calculation with GetCurrentStatementStartTimestamp()
  and total_time computation (premature optimization)
- Added comprehensive benchmark testing (unpatched vs patched)
- Benchmark testing shows no measurable overhead (<2% in synthetic tests)
- Measured overhead: 13ns per statement (1.85% for 1M iteration test,
  negligible for real queries >0.1ms)

Changes from v1:
- Rename column from last_executed to stats_last_updated (Christoph Berg)
- Move timestamp from Counters struct to pgssEntry for better semantics
- Place column at end of view to match stats_since naming convention
- Fixed whitespace errors
- Moved tests to entry_timestamp.sql (Sami Imseih)
- Updated PGSS_FILE_HEADER to handle structure change


Patch, benchmark script and raw results are attached.

Best regards,
Pavlo Golub
UNPATCHED PostgreSQL

Test 1: track='none' (clean baseline, no pg_stat_statements overhead)
- Run 1: 575.843 ms
- Run 2: 554.241 ms  
- Run 3: 556.117 ms

Average: 562.07 ms

Test 2: track='all' (with pg_stat_statements tracking)
- Run 1: 725.412 ms
- Run 2: 713.809 ms
- Run 3: 718.276 ms

Average: 719.17 ms

---

PATCHED PostgreSQL (with stats_last_updated column)

Test 1: track='none' (clean baseline, no pg_stat_statements overhead)
- Run 1: 540.163 ms
- Run 2: 568.065 ms
- Run 3: 538.595 ms  

Average: 548.95 ms

Test 2: track='all' (with pg_stat_statements + stats_last_updated)
- Run 1: 737.379 ms
- Run 2: 725.067 ms
- Run 3: 735.044 ms

Average: 732.50 ms

Attachment: v4-0001-pg_stat_statements-Add-stats_last_updated-column.patch
Description: Binary data

Attachment: benchmark.sql
Description: Binary data

Reply via email to