Re: Unaligned access trade-offs for SFrame FRE layout

Steven Rostedt <[email protected]> Mon, 15 Sep 2025 12:04:00 -0400
Newsgroups org.kernel.vger.linux-toolchains
Message-ID <[email protected]>
On Sat, 13 Sep 2025 00:56:34 -0700
Indu Bhagat <[email protected]> wrote:

> I think quantifying the performance impact of unaligned accesses for 
> stack tracing using SFrame sections will be larger experiment which will 
> be hardware dependent..
> 
> https://lemire.me/blog/2012/05/31/data-alignment-for-speed-myth-or-reality/

I'm not so sure his example is good enough to show the overhead. So I wrote
a much simpler test. I create an array of 1,000,004 unsigned longs and fill
it with a simple increment.

I then loop over the array and reading at every 5618 increments and modulus
it to 1,000,000. I use 5816 because it is a factor of 1,000,004. By
incrementing it by this number and modulus it with 1,000,000, then the wrap
will go to 1,000,000 + 4. meaning by looping a 1,000,000 times on a
1,000,000 words should hit all of them if they are 4 byte long words or
half of them if they are 8 byte words.

I time a loop of going over all 1,000,000 numbers and simply adding them. I
then report the total time.

The test takes an offset to add before reading. Here's my results on two
machines:

On my workstation: Intel(R) Xeon(R) CPU E5-2620 v4 @ 2.10GHz

  $ ./aligned-access 0
  val = 2296870857426500744
  time = 6822 us
  $ ./aligned-access 1
  val = -2296870857426996608
  time = 7319 us
  $ ./aligned-access 2
  val = 2296870857426500744
  time = 7432 us
  $ ./aligned-access 3
  val = -2296870857426996608
  time = 7522 us
  $ ./aligned-access 4
  val = 2296870857426500744
  time = 6841 us

On my server: Intel(R) Xeon(R) CPU E5-2683 v3 @ 2.00GHz

  $ ./aligned-access 0
  val = 2296870857426500744
  time = 7093 us
  $ ./aligned-access 1
  val = -2296870857426996608
  time = 6948 us
  $ ./aligned-access 2
  val = 2296870857426500744
  time = 6761 us
  $ ./aligned-access 3
  val = -2296870857426996608
  time = 7111 us
  $ ./aligned-access 4
  val = 2296870857426500744
  time = 6940 us
  $ ./aligned-access 5
  val = -2296870857426996608
  time = 6939 us
  $ ./aligned-access 6
  val = 2296870857426500744
  time = 6937 us
  $ ./aligned-access 7
  val = -2296870857426996608
  time = 7254 us
  $ ./aligned-access 8
  val = 2296870857426500744
  time = 6939 us

My workstation is a bit older than my server, and it looks like alignment
does make a difference. For my server, it didn't show any difference.

Thus, it looks like it's only a problem for older machines (on x86). Would
be good to see how the performance of this is on arm64 machines.

But feel free to try it out yourself.

-- Steve
aligned-access.c (text/x-c++src, 1.9 KB)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <errno.h>
#include <time.h>

static char *argv0;

static char *get_this_name(void)
{
	static char *this_name;
	char *arg;
	char *p;

	if (this_name)
		return this_name;

	arg = argv0;
	p = arg+strlen(arg);

	while (p >= arg && *p != '/')
		p--;
	p++;

	this_name = p;
	return p;
}

static void usage(void)
{
	char *p = get_this_name();

	printf("usage: %s [alignement]\n"
	       "  alignment is a number offset\n"
	       "\n",p);
	exit(-1);
}

static void __vdie(const char *fmt, va_list ap, int err)
{
	int ret = errno;
	char *p = get_this_name();

	if (err && errno)
		perror(p);
	else
		ret = -1;

	fprintf(stderr, "  ");
	vfprintf(stderr, fmt, ap);

	fprintf(stderr, "\n");
	exit(ret);
}

void die(const char *fmt, ...)
{
	va_list ap;

	va_start(ap, fmt);
	__vdie(fmt, ap, 0);
	va_end(ap);
}

void pdie(const char *fmt, ...)
{
	va_list ap;

	va_start(ap, fmt);
	__vdie(fmt, ap, 1);
	va_end(ap);
}

static unsigned long long get_time(void)
{
	unsigned long long time;
	struct timespec ts;

	clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
	time = ts.tv_sec * 1000000;
	time += ts.tv_nsec / 1000;

	return time;
}

#define ARRAY_SIZE 1000000

static void fill_array(void *array)
{
	for (int i = 0; i < ARRAY_SIZE; i++) {
		*(long *)(array + sizeof(long) * i) = i;
	}
}

int main (int argc, char **argv)
{
	unsigned long long start, end;
	unsigned long long val = 0;
	unsigned long long idx, i;
	void *array;
	int offset;

	argv0 = argv[0];

	if (argc < 2)
		usage();

	array = malloc((ARRAY_SIZE + 1) * sizeof(long));
	if (!array)
		pdie("Allocating array");

	fill_array(array);

	offset = atoi(argv[1]);

	start = get_time();
	for (i = 0; i < ARRAY_SIZE; i++) {
		idx = (i * 5618) + offset;
		idx %= 1000000;
		val += *(unsigned long *)(array + idx);
	}
	end = get_time();

	printf("val = %lld\n", val);
	printf("time = %lld us\n", end - start);
	
	return 0;
}