Skip to content

Faust C compiler generates O(n^2) additions for a simple O(n) implementation of prefix sums #1159

@eleses

Description

@eleses
import("stdfaust.lib");

ker = _,_ <: _,!, +;
presum(N) = seq(i, N - 1, si.bus(i), ker, si.bus(N - i - 2));

// test:
M = 4;
durs = par(i, M, hslider("Dur%2i", M - i, 0, M, .001));
plot = hgroup("[1]Times ", 
    par(i, M, vbargraph("Time%2i[style:numerical]", 0.0, M*M)) :> _);

hmm = durs : presum(M);

process = _ <: attach(_, hmm : plot);

Relevant graph has just 3 additions:

Image

But the generated C code has many more...

	float fSlow0 = (float)(dsp->fHslider0);
	float fSlow1 = (float)(dsp->fHslider2);
	float fSlow2 = (float)(dsp->fHslider3);
	dsp->fVbargraph0 = (FAUSTFLOAT)(fSlow2 + fSlow1 + (float)(dsp->fHslider1) + fSlow0);
	dsp->fVbargraph1 = (FAUSTFLOAT)(fSlow2 + fSlow0 + fSlow1);
	dsp->fVbargraph2 = (FAUSTFLOAT)(fSlow1 + fSlow2);
	dsp->fVbargraph3 = (FAUSTFLOAT)(fSlow2);
	float fSlow3 = dsp->fVbargraph3 + dsp->fVbargraph2 + dsp->fVbargraph1 + dsp->fVbargraph0;

Interestingly, if I replace the ker with the one for a single pass of bubble sort (out its N), i.e. ker = _,_ <: min, max; it doesn't do this bad: just 3 fmin and 3 fmax as one may hope. So, it looks like there's something about additions that really triggers this odd behavior. But that's not the whole story, because merely replacing addition with multiplication i.e. ker = _,_ <: _,!, *; also does O(n^2) operations in the C code. However, it's not merely the asymmetric nature of that ker that triggers it. Because this (not very useful) ker = _,_ <: _,!, min; only calls three fmins in the C code. And it's not merely that the operation is non-commutative, because ker = _,_ <: _,!, -; does something pretty weird too:

	float fSlow0 = (float)(dsp->fHslider0);
	float fSlow1 = (float)(dsp->fHslider2);
	float fSlow2 = (float)(dsp->fHslider3);
	dsp->fVbargraph0 = (FAUSTFLOAT)(fSlow2 - (fSlow1 + (float)(dsp->fHslider1) + fSlow0));
	dsp->fVbargraph1 = (FAUSTFLOAT)(fSlow2 - (fSlow0 + fSlow1));
	dsp->fVbargraph2 = (FAUSTFLOAT)(fSlow2);
	dsp->fVbargraph3 = (FAUSTFLOAT)(fSlow2 - fSlow1);

So I'm guessing it's trying to propagate arithmetic expressions (but not others like fmin) back to the beginning of the graph, even when this isn't particularly useful, like in systolic arrangements such as this.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions