Skip to content

BLAS: let SCASUM use SCABS1, as DZASUM uses DCABS1 - #1418

Closed
ACSimon33 wants to merge 1 commit into
Reference-LAPACK:masterfrom
ACSimon33:blas-scasum-uses-scabs1
Closed

ACSimon33 wants to merge 1 commit into
Reference-LAPACK:masterfrom
ACSimon33:blas-scasum-uses-scabs1

Conversation

@ACSimon33

Copy link
Copy Markdown
Collaborator

Description

DZASUM accumulates with STEMP = STEMP + DCABS1(ZX(I)). SCASUM, its single-precision counterpart, spells the same quantity out inline as STEMP + ABS(REAL(CX(I))) + ABS(AIMAG(CX(I))). SCABS1 computes exactly ABS(REAL(Z)) + ABS(AIMAG(Z)), so the two files were doing the same arithmetic by different routes for no stated reason.

The consequence is that SCABS1 has no caller anywhere in the Fortran library. The only references to it on master are BLAS/SRC/DEPRECATED/icamax.f, which no build file lists, and the CBLAS wrappers cblas_scabs1.c and scabs1sub.f, which no test calls. DCABS1 is reached through DZASUM. This PR makes SCASUM call the helper, which brings the two precisions into line and gives SCABS1 a caller.

Coverage

Measured locally with gfortran 13.3.0: BLAS/SRC built -O0 -g --coverage -fno-inline, the four ?blat1 drivers linked against it and run, then gcov -b -n.

Routine Lines before Lines after
scabs1.f 0.00% of 3 100.00% of 3
dcabs1.f 100.00% of 3 100.00% of 3

scasum.f itself is unmoved at 100% of 12 lines and 71.43% of 14 branches, which is what dzasum.f also scores.

This is not bit-for-bit identical

The inline form evaluates (STEMP + |re|) + |im|, two roundings into the accumulator per element. The helper form evaluates STEMP + (|re| + |im|), one. Over 20000 random vectors of length 1000, 19376 gave a different single-precision result.

The new grouping is slightly the more accurate of the two, which is what the grouping predicts: adding the two same-magnitude parts to each other first, then once into a larger running total, rounds less than folding each part in separately. Against a DZASUM reference over 5000 random vectors of length 1000:

Inline (before) Via SCABS1 (after)
mean relative error 4.695e-07 3.422e-07
max relative error 2.434e-06 1.546e-06

For reference, single-precision epsilon is 1.192e-07. SCASUM now also rounds the same way DZASUM always has, so the two precisions agree on more than just the expression.

This matters because SCASUM is not only a user-facing routine: CLAEIN, CLATRS, CLATPS, CLATBS, CLATDF, CTREVC and CTREVC3 call it, as do several EIG test helpers. The suite results below cover that.

Cost

On gfortran 13.3.0, x86-64, n = 100000, timing SCASUM three ways:

Variant -O2 -O2 -flto
inline, as on master 0.118s 0.119s
via SCABS1, this PR 0.133s 0.059s
inline but parenthesised, for reference 0.060s 0.060s

Two separate effects are at work and they pull opposite ways.

The call itself costs about 12%, because without link-time optimization it cannot be inlined across translation units. That is precisely what DZASUM has always paid: substituting the inline expression back into dzasum.f and timing the pair the same way gives 1.13x. On that axis this change brings SCASUM to parity with DZASUM rather than introducing a new cost.

The regrouping is worth a factor of two, and it is the grouping rather than the call or the inlining that earns it. STEMP + (|re| + |im|) puts one dependent addition on the critical path where STEMP + |re| + |im| puts two, which gfortran emits as one addss into the accumulator instead of two. Parenthesising the inline expression, with no call at all, reaches the same 0.060s at plain -O2 and produces bit-identical results to this PR. So the 2x seen under LTO is the grouping becoming visible once the call is inlined away, not a benefit of LTO as such.

The net is that this PR is about 12% slower than master at -O2 and about twice as fast with LTO. DZASUM sits in the same place for the same reason. Only gfortran on x86-64 was timed.

SCASUM spelled out ABS(REAL(..)) + ABS(AIMAG(..)) inline while its
double counterpart called the helper, which left SCABS1 without a
single caller in the library and so untested.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@codecov

codecov Bot commented Sep 15, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 69.36%. Comparing base (a6c6e74) to head (ec3d9d1).
✅ All tests successful. No failed tests found.

Additional details and impacted files

Impacted file tree graph

@@           Coverage Diff           @@
##           master    #1418   +/-   ##
=======================================
  Coverage   69.36%   69.36%           
=======================================
  Files        6122     6122           
  Lines      486711   486711           
  Branches    23268    23268           
=======================================
+ Hits       337584   337587    +3     
+ Misses     148689   148686    -3     
  Partials      438      438           
Components Coverage Δ
BLAS 97.96% <100.00%> (+0.02%) ⬆️
CBLAS 96.98% <ø> (ø)
LAPACK 82.38% <ø> (ø)
LAPACKE 2.17% <ø> (ø)
TMGLIB 55.69% <ø> (ø)
BLAS testing 88.33% <ø> (ø)
CBLAS testing 89.63% <ø> (ø)
LAPACK testing 82.20% <ø> (ø)
LAPACKE testing ∅ <ø> (∅)
Files with missing lines Coverage Δ
BLAS/SRC/scasum.f 100.00% <100.00%> (ø)

... and 1 file with indirect coverage changes


Continue to review full report in Codecov by Harness.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update a6c6e74...ec3d9d1. Read the comment docs.

@ilayn

ilayn commented Sep 15, 2026

Copy link
Copy Markdown
Contributor

I think this is the documented behavior and hence breaking contract and possibly backwards compatinility.

@ACSimon33

ACSimon33 commented Sep 15, 2026

Copy link
Copy Markdown
Collaborator Author

True, it would change the results. I ran some tests to see if there is some consensus among the vendors, but it seems there isn't:

n = 1000, 2000 random vectors:

Implementation SCASUM matches master SCASUM mean rel. error DZASUM matches reference DZASUM mean rel. error
reference, master 100.0% 5.300e-07 100.0% 7.432e-16
reference, this PR 5.0% 3.774e-07 100.0% 7.432e-16
OpenBLAS 0.3.34.dev 5.3% 4.868e-08 8.0% 1.472e-16
oneMKL 2026.1 5.7% 4.904e-08 7.4% 2.680e-16
AOCL-BLAS 5.3.0 100.0% 5.300e-07 4.5% 1.001e-15
cuBLAS 12.6.3.3 6.4% 3.546e-08 8.3% 7.464e-17

So I'm not sure whether this is documented behavior, other than it being like that for over 20 years.

I'd still like the two routines to operate similarly, so we could replace DCABS1 in DZASUM with TEMP + (|re| + |im|). That would keep the results the same while improving performance through the inlined call. It is the last DCABS1 call in the entire library, and @langou already suggested removing these helper routines in #1200. And #1218 did exactly that for ?axpy.

@ilayn

ilayn commented Sep 15, 2026

Copy link
Copy Markdown
Contributor

I happen to translate all Fortran code in SciPy and I am feeling like this also relates to the overflow concerns because I saw this definition a bit too often to attribute to mistakes. I wonder if there is any historical reasoning why this 1- norm is adopted instead of 2- for complex number magnitude since norms are equivalent if the value is not important but ordering is I can "sense" the rationale behind it.

@ACSimon33

Copy link
Copy Markdown
Collaborator Author

Closing in favor of #1421

@ACSimon33 ACSimon33 closed this Sep 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants