Skip to content

Commit ca62edd

Browse files
authored
Swept the stack-heavy measurement across placements, and qualified its result (#638)
#636 reported that a stack in BTCM gave a threefold tighter spread than DRAM0 for stack-heavy work. That measurement used a single code placement, which is the methodology #631 and #633 exist to correct: the cache benchmark got an alignment sweep and the interrupt handler got one, and this measurement never did. It was noticed when #637 added two threads to the same image and the figure moved -- both spreads came out near 6500 and the minima rose 15%. The recursive body is now generated at four placements and all four are measured, per placement, in one image. placement BTCM min / spread DRAM0 min / spread offset 0 41854 / 6850 42036 / 6880 offset 16 48670 / 1946 48792 / 1978 offset 32 42388 / 6914 42752 / 7018 offset 48 48914 / 1860 49198 / 6786 Reproducible across runs to within a few hundred cycles. Spread is dominated by code placement rather than by the memory holding the stack. It ranges from 1860 to 6914 depending on where the body falls in a cache line, and placement also moves the minimum by 17%, from 41854 to 49214. Against that, the memory contributes a consistent but small advantage: BTCM's minimum is lower at all four placements, by 0.4% to 0.9%. BTCM's spread beats DRAM0's decisively at one placement of the four, offset 48, at 1860 against 6786. At the other three the two are within 2% of each other. So the effect #636 reported is real where it occurs and is not a property of the part: quoting it as one invited the reader to expect it everywhere. #636's claim should be read as qualified by this. A stack in BTCM buys a small consistent improvement in the best case and a large improvement in spread at some code placements and not others. Anyone building a determinism argument on it needs the placement sweep in the loop, not a single figure. The pad nops that displace each placement execute on every recursion level rather than once, so each placement carries a slightly different constant cost, about 0.6% at the widest. That cancels in the BTCM against DRAM0 comparison, which is made at the same placement, and does not affect spread within one. Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>
1 parent 1ef4933 commit ca62edd

1 file changed

Lines changed: 119 additions & 63 deletions

File tree

ports/cortex_r52/gnu/example_build/s32z280_evb/demo_s32z280.c

Lines changed: 119 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,7 @@ static void ctx_partner_entry(ULONG which)
289289
#define DEEP_SAMPLES 64U
290290
#define DEEP_DEPTH 24U
291291
#define DEEP_FRAME 16U /* words: 64 bytes, one cache line per frame */
292+
#define DEEP_PLACEMENTS 4U
292293

293294
static TX_THREAD deep_btcm_thread;
294295
static TX_THREAD deep_dram_thread;
@@ -301,36 +302,65 @@ static unsigned char deep_btcm_stack[4096];
301302
static unsigned char deep_dram_stack[4096];
302303

303304
static unsigned int deep_samples[CTX_PAIRS][DEEP_SAMPLES];
304-
static unsigned int deep_lo[CTX_PAIRS];
305-
static unsigned int deep_hi[CTX_PAIRS];
306-
static unsigned int deep_mean[CTX_PAIRS];
307-
static unsigned int deep_over[CTX_PAIRS];
305+
static unsigned int deep_lo[CTX_PAIRS][DEEP_PLACEMENTS];
306+
static unsigned int deep_hi[CTX_PAIRS][DEEP_PLACEMENTS];
307+
static unsigned int deep_mean[CTX_PAIRS][DEEP_PLACEMENTS];
308+
static unsigned int deep_over[CTX_PAIRS][DEEP_PLACEMENTS];
308309
static volatile unsigned int deep_done[CTX_PAIRS];
309310

310-
__attribute__((noinline))
311-
static unsigned int deep_touch(unsigned int depth)
312-
{
313-
volatile unsigned int frame[DEEP_FRAME];
314-
unsigned int acc = 0U;
315-
unsigned int i;
316-
317-
for (i = 0U; i < DEEP_FRAME; i++)
318-
{
319-
frame[i] = depth + i;
320-
}
321-
322-
if (depth > 0U)
323-
{
324-
acc = deep_touch(depth - 1U);
325-
}
311+
/* Four placements of the recursive body, each 64-byte aligned and then
312+
displaced within its line, and the measurement sweeps all four.
313+
314+
The first version of this measurement had one placement, which is the
315+
methodology this example spent two changes correcting elsewhere. It showed
316+
BTCM with a threefold tighter spread than DRAM0; that did not survive an
317+
image with two more threads in it, where both spreads came out near 6500 and
318+
the minima moved 15%. A single placement was reporting where the linker had
319+
put the code.
320+
321+
The pad nops execute on every recursion level rather than once, so each
322+
placement carries a slightly different constant cost -- about 0.6% at the
323+
widest pad. That cancels in the comparison that matters, because BTCM and
324+
DRAM0 are measured at the same placement, and it does not affect the spread
325+
within a placement. */
326+
327+
#define MAKE_DEEP_TOUCH(name, pad_words) \
328+
__attribute__((aligned(64), noinline)) \
329+
static unsigned int name(unsigned int depth) \
330+
{ \
331+
volatile unsigned int frame[DEEP_FRAME]; \
332+
unsigned int acc = 0U; \
333+
unsigned int i; \
334+
\
335+
__asm__ volatile(".rept " #pad_words "\n\tnop\n\t.endr"); \
336+
\
337+
for (i = 0U; i < DEEP_FRAME; i++) \
338+
{ \
339+
frame[i] = depth + i; \
340+
} \
341+
\
342+
if (depth > 0U) \
343+
{ \
344+
acc = name(depth - 1U); \
345+
} \
346+
\
347+
for (i = 0U; i < DEEP_FRAME; i++) \
348+
{ \
349+
acc += frame[i]; \
350+
} \
351+
\
352+
return acc; \
353+
}
326354

327-
for (i = 0U; i < DEEP_FRAME; i++)
328-
{
329-
acc += frame[i];
330-
}
355+
MAKE_DEEP_TOUCH(deep_touch_a, 0)
356+
MAKE_DEEP_TOUCH(deep_touch_b, 4)
357+
MAKE_DEEP_TOUCH(deep_touch_c, 8)
358+
MAKE_DEEP_TOUCH(deep_touch_d, 12)
331359

332-
return acc;
333-
}
360+
static unsigned int (*const deep_touches[DEEP_PLACEMENTS])(unsigned int) =
361+
{
362+
deep_touch_a, deep_touch_b, deep_touch_c, deep_touch_d
363+
};
334364

335365
static void deep_entry(ULONG which)
336366
{
@@ -345,47 +375,67 @@ static void deep_entry(ULONG which)
345375
{
346376
unsigned int before;
347377
unsigned int after;
378+
unsigned int slot = i / (DEEP_SAMPLES / DEEP_PLACEMENTS);
379+
380+
if (slot >= DEEP_PLACEMENTS)
381+
{
382+
slot = DEEP_PLACEMENTS - 1U;
383+
}
348384

349385
cache_clean_all();
350386
cache_invalidate_dcache_all();
351387

352388
before = timer_read_cycles();
353-
(void) deep_touch(DEEP_DEPTH);
389+
(void) deep_touches[slot](DEEP_DEPTH);
354390
after = timer_read_cycles();
355391

356392
deep_samples[which][i] = after - before;
357393
}
358394

359-
/* Post-process: the minimum sets the scale, and anything past twice it was
360-
interrupted rather than slow. */
395+
/* Post-processed per placement. A figure spanning placements would mix the
396+
four constant costs and hide exactly what this sweep exists to show. */
361397

362-
lo = 0xFFFFFFFFU;
363-
for (i = 0U; i < DEEP_SAMPLES; i++)
364-
{
365-
if (deep_samples[which][i] < lo) { lo = deep_samples[which][i]; }
366-
}
367-
368-
hi = 0U; sum = 0UL; counted = 0U; over = 0U;
369-
for (i = 0U; i < DEEP_SAMPLES; i++)
370398
{
371-
unsigned int v = deep_samples[which][i];
399+
unsigned int slot;
400+
unsigned int each = DEEP_SAMPLES / DEEP_PLACEMENTS;
372401

373-
if (v > (lo * 2U))
402+
for (slot = 0U; slot < DEEP_PLACEMENTS; slot++)
374403
{
375-
over++;
376-
}
377-
else
378-
{
379-
if (v > hi) { hi = v; }
380-
sum += (unsigned long) v;
381-
counted++;
404+
unsigned int s;
405+
unsigned int lo = 0xFFFFFFFFU;
406+
unsigned int hi = 0U;
407+
unsigned long sum = 0UL;
408+
unsigned int counted = 0U;
409+
unsigned int over = 0U;
410+
411+
for (s = slot * each; s < ((slot + 1U) * each); s++)
412+
{
413+
if (deep_samples[which][s] < lo) { lo = deep_samples[which][s]; }
414+
}
415+
416+
for (s = slot * each; s < ((slot + 1U) * each); s++)
417+
{
418+
unsigned int v = deep_samples[which][s];
419+
420+
if (v > (lo * 2U))
421+
{
422+
over++;
423+
}
424+
else
425+
{
426+
if (v > hi) { hi = v; }
427+
sum += (unsigned long) v;
428+
counted++;
429+
}
430+
}
431+
432+
deep_lo[which][slot] = lo;
433+
deep_hi[which][slot] = hi;
434+
deep_mean[which][slot] = (counted > 0U) ? (unsigned int) (sum / counted) : 0U;
435+
deep_over[which][slot] = over;
382436
}
383437
}
384438

385-
deep_lo[which] = lo;
386-
deep_hi[which] = hi;
387-
deep_mean[which] = (counted > 0U) ? (unsigned int) (sum / counted) : 0U;
388-
deep_over[which] = over;
389439
deep_done[which] = 1U;
390440
}
391441

@@ -551,32 +601,38 @@ static void judge_entry(ULONG input)
551601
linflexd_puts("\n");
552602
}
553603

554-
linflexd_puts("stack-heavy work, cycles (24 frames, cold cache)\n");
604+
linflexd_puts("stack-heavy work, cycles (24 frames, cold cache, 4 placements)\n");
555605
{
556-
static const char *const dwhere[CTX_PAIRS] = { "stack in BTCM ",
557-
"stack in DRAM0" };
606+
static const char *const dwhere[CTX_PAIRS] = { "BTCM ", "DRAM0" };
558607
unsigned int q;
608+
unsigned int slot;
559609

560610
for (q = 0U; q < CTX_PAIRS; q++)
561611
{
562-
linflexd_puts(" ");
563-
linflexd_puts(dwhere[q]);
564612
if (deep_done[q] == 0U)
565613
{
614+
linflexd_puts(" ");
615+
linflexd_puts(dwhere[q]);
566616
linflexd_puts(": did not finish\n");
617+
continue;
567618
}
568-
else
619+
620+
for (slot = 0U; slot < DEEP_PLACEMENTS; slot++)
569621
{
622+
linflexd_puts(" ");
623+
linflexd_puts(dwhere[q]);
624+
linflexd_puts(" offset ");
625+
demo_dec((unsigned long) (slot * 16U));
570626
linflexd_puts(": min ");
571-
demo_dec(deep_lo[q]);
627+
demo_dec(deep_lo[q][slot]);
572628
linflexd_puts(" mean ");
573-
demo_dec(deep_mean[q]);
629+
demo_dec(deep_mean[q][slot]);
574630
linflexd_puts(" max ");
575-
demo_dec(deep_hi[q]);
576-
linflexd_puts(" interrupted ");
577-
demo_dec(deep_over[q]);
578-
linflexd_puts(" of ");
579-
demo_dec(DEEP_SAMPLES);
631+
demo_dec(deep_hi[q][slot]);
632+
linflexd_puts(" spread ");
633+
demo_dec(deep_hi[q][slot] - deep_lo[q][slot]);
634+
linflexd_puts(" cut ");
635+
demo_dec(deep_over[q][slot]);
580636
linflexd_puts("\n");
581637
}
582638
}

0 commit comments

Comments
 (0)