[comp.std.c] pointers for speed

shaunc@gold.gvg.tek.com (Shaun Case) (02/26/91)

I know that when you repeatedly access something like

foo.a.b.c.d[11].value

it is better to declare a (register) pointer, assign it the address
of foo.a.b.c.d[11].value, and do manipulations on that, since it
is faster.

However, I am dealing with a heavily nested structure, such as:

struct a {
	struct aa x;
	struct ab y;
	struct ac z;
	} le_struct;

typedef struct aa {
	struct one something;
	struct two nothing;
	struct three everything;
	struct four whatever;
	};


typedef struct one {
	char foo;
	char foo2;
	short blah;
	short blah2;
	};

And...

I want to access the various members of a.x.something (.foo, .foo2,
.blah, .blah2) quickly.  I have a function that I pass a pointer to 
type struct a, thus:

void do_stuff (struct *a ptr)
{}

and what I want to do is something like

register char *fastptr;

fastptr = &(ptr->x.something) 

and then access fastptr->foo, fastptr->foo2, etc.

I can't seem to get it to work.  Is this possible?
Help would be greatly appreciated, since the structure
I am working with is 6 levels deep at some points, and
I have to access every non-char element within it at least
once.  

( I think that from my foggy earlier days, what I am
trying to do is equivalent to Pascal's WITH feature,
in case what I was describing above isn't clear.  It's
been so long since I've used Pascal, tho, that I can
no longer be sure.  Thankfully.  :-)  )

// Shaun //

dave@cs.arizona.edu (Dave P. Schaumann) (02/26/91)

In article <1998@gold.gvg.tek.com> shaunc@gold.gvg.tek.com (Shaun Case) writes:
>I know that when you repeatedly access something like
>
>foo.a.b.c.d[11].value
>
>it is better to declare a (register) pointer, assign it the address
>of foo.a.b.c.d[11].value, and do manipulations on that, since it
>is faster.

Oh really?  Seems to me that &foo.a.b.c.d[0] is a constant that can be
computed *at compile time*.  That means that (unless your comiler is
totally brain-dead) accessing foo.a.b.c.d[0] should be as fast as accessing
a normal element of an array of the same type.

Yes, calculating the address does take a bit longer, but that calculation is
done once by the compiler, and is known forever more.

Now, if you had foo->a->b->c->d, you would certainly want to keep the
address of d in a variable, rather than indirecting 4 times.

-- 
Dave Schaumann      | Is this question undecidable?
dave@cs.arizona.edu |

torek@elf.ee.lbl.gov (Chris Torek) (02/26/91)

(This is a topic for comp.lang.c, not comp.std.c; I have redirected
followups there.)

In article <1998@gold.gvg.tek.com> shaunc@gold.gvg.tek.com (Shaun Case) writes:
>I know that when you repeatedly access something like
>
>foo.a.b.c.d[11].value
>
>it is better to declare a (register) pointer, assign it the address
>of foo.a.b.c.d[11].value, and do manipulations on that, since it
>is faster.

This is an unsupported assertion, and in fact it often turns out to
be no faster (though usually no slower either).

In particular, given ...

>a heavily nested structure ...
>I want to access the various members of a.x.something (.foo, .foo2,
>.blah, .blah2) quickly.

... structures that are nested in this manner (where variable `a' is
a structure containing a member `x' which itself is a structure
containing a member `something' which itself is a structure containing
members `foo', `foo2', etc.) are typically implemented such that
computing &a.x.something.foo requires exactly as much work as computing
&a.x or &a.x.something.  For instance, on the 68010, the difference
between:

	a.x.something.foo = 3;

and:

	local_var = 3;

is, in essence,

	moveq	#3,a5@260

and

	moveq	#3,a7@-32

both of which require exactly the same amount of time and space.

On the other hand, writing

	ptr->x.something.foo = 3;
	ptr->x.something.foo2 = 3 * 3;
	ptr->x.something.blah = "headache";

is generally perceived to be less clear than writing:

	s->foo = 3;
	s->foo2 = 3 * 3;
	s->blah = "headache";

and if you agree, you should use the latter.

>I have a function that I pass a pointer to type struct a, thus:
>
>void do_stuff (struct *a ptr)
>{}
>
>and what I want to do is something like
>
>register char *fastptr;
>
>fastptr = &(ptr->x.something) 
>
>and then access fastptr->foo, fastptr->foo2, etc.

It would help greatly if your examples were legal C....

To take the conceptual error first:  Declaring `fastptr' as `register
char *fastptr' means that `fastptr' points to `char's.  It does not
point to structures.  Since it does not point to structures, you may
not use it to obtain elements of structures.  If you want a pointer
to point to one or more structures of type `struct glorp', you must
declare it as a pointer to `struct glorp':

		struct glorp *g;

Next, if `ptr' is to point to a `struct a', the proper syntax is:

	void do_stuff(struct a *ptr)

Composing these and adding the assignment to make `g' point to the
glorp member of the structure to which `ptr' points, we get:

	void do_stuff(struct a *ptr) {
		struct glorp *g;

		g = &ptr->x.something;

At this point you can use `g->foo', `g->foo2', and so on exactly
as above.  It will likely be no faster (but more readable and/or
less cumbersome) than using `ptr->x.something.foo', etc.

On the other hand, if `ptr' were to point to a structure in which the
sub-structures are not directly imbedded, but rather found through
pointers, then:

	void do_stuff(struct object *o) {
		struct value *v;

		/*
		 * Pick up object's current value pointer,
		 * which is found by reading its attributes
		 * to find its current binding, and then
		 * reading the binding to find the current value.
		 */
		v = o->o_attr->a_binding->b_value;
		...
	}

in this case using `v' is more likely (but still not guaranteed!) to
be faster than writing `o->o_attr->a_binding->b_value' each time.  It
is also almost certain to make those editing the code happier.
-- 
In-Real-Life: Chris Torek, Lawrence Berkeley Lab EE div (+1 415 486 5427)
Berkeley, CA		Domain:	torek@ee.lbl.gov

volpe@camelback.crd.ge.com (Christopher R Volpe) (02/26/91)

In article <955@caslon.cs.arizona.edu>, dave@cs.arizona.edu (Dave P.
Schaumann) writes:
|>In article <1998@gold.gvg.tek.com> shaunc@gold.gvg.tek.com (Shaun
Case) writes:
|>>I know that when you repeatedly access something like
|>>
|>>foo.a.b.c.d[11].value
|>>
|>>it is better to declare a (register) pointer, assign it the address
|>>of foo.a.b.c.d[11].value, and do manipulations on that, since it
|>>is faster.
|>
|>Oh really?  Seems to me that &foo.a.b.c.d[0] is a constant that can be
|>computed *at compile time*.  

Oh really? What if the type of "foo.a.b.c.d" is "struct bar *" and you
initialize it with:
    foo.a.b.c.d = (struct bar *) malloc(15 * sizeof(struct bar));

Then neither &foo.a.b.c.d[0] nor &foo.a.b.c.d[11] are compile time
constants.                        
==================
Chris Volpe
G.E. Corporate R&D
volpecr@crd.ge.com

dave@cs.arizona.edu (Dave P. Schaumann) (02/27/91)

In article <17114@crdgw1.crd.ge.com> volpe@camelback.crd.ge.com (Christopher R Volpe) writes:
|In article <955@caslon.cs.arizona.edu>, dave@cs.arizona.edu (Dave P.
|Schaumann) writes:
|||In article <1998@gold.gvg.tek.com> shaunc@gold.gvg.tek.com (Shaun
|Case) writes:
||||I know that when you repeatedly access something like
||||
||||foo.a.b.c.d[11].value
||||
||||it is better to declare a (register) pointer, assign it the address
||||of foo.a.b.c.d[11].value, and do manipulations on that, since it
||||is faster.
|||
|||Oh really?  Seems to me that &foo.a.b.c.d[0] is a constant that can be
|||computed *at compile time*.  
|
|Oh really? What if the type of "foo.a.b.c.d" is "struct bar *" and you
|initialize it with:
|    foo.a.b.c.d = (struct bar *) malloc(15 * sizeof(struct bar));
|
|Then neither &foo.a.b.c.d[0] nor &foo.a.b.c.d[11] are compile time
|constants.                        

I think you're wrong.  If the base address of foo is known at compile time,
then &foo.a.b.c.d[0] is simply a constant offset from this base address,
and this constant *must* calculatable at compile time for the compiler to
generate code to access these fields.  And even if the base address of foo is
unknown at compile time, the expression foo.a.b.c.d still represents a known
constant offset from an unknown base address.

If you have foo.baz as the same type as foo.a.b.c.d, accessing foo.baz will
take exactly the same amount of time *at run time* as foo.a.b.c.d, since they
both represent a (different) fixed offset from the same base address.

-- 
		Dave Schaumann		dave@cs.arizona.edu
'Dog Gang'!  Where do they get off calling us the 'Dog Gang'?  I'm beginning to
think the party's over.  I'm beginning to think maybe we don't need a dog.  Or
maybe we need a *new* dog.  Or maybe we need a *cat*! - Amazing Stories

volpe@kirkwood.crd.ge.com (Christopher R Volpe) (02/28/91)

In article <964@caslon.cs.arizona.edu>, dave@cs.arizona.edu (Dave P.
Schaumann) writes:
|>In article <17114@crdgw1.crd.ge.com> volpe@camelback.crd.ge.com
(Christopher R Volpe) writes:
|>|In article <955@caslon.cs.arizona.edu>, dave@cs.arizona.edu (Dave P.
|>|Schaumann) writes:
|>|||Oh really?  Seems to me that &foo.a.b.c.d[0] is a constant that can be
|>|||computed *at compile time*.  
|>|
|>|Oh really? What if the type of "foo.a.b.c.d" is "struct bar *" and you
|>|initialize it with:
|>|    foo.a.b.c.d = (struct bar *) malloc(15 * sizeof(struct bar));
|>|
|>|Then neither &foo.a.b.c.d[0] nor &foo.a.b.c.d[11] are compile time
|>|constants.                        
|>
|>I think you're wrong.  If the base address of foo is known at compile time,
|>then &foo.a.b.c.d[0] is simply a constant offset from this base address,
|>and this constant *must* calculatable at compile time for the compiler to
|>generate code to access these fields.  And even if the base address of foo is
|>unknown at compile time, the expression foo.a.b.c.d still represents a known
|>constant offset from an unknown base address.

I don't think so. If the base address of {foo} is known at compile time,
then the ADDRESS of {foo.a.b.c.d} is known at compile time. However,
{&foo.a.b.c.d[0]} is by definition the VALUE of {foo.a.b.c.d}, which is
the value returned by malloc, which is NOT known at compile time. Look again
at what that expression is computing. It's the address of the first element
of an array whose storage was allocated at run time. How can that be known
at compile time?

==================
Chris Volpe
G.E. Corporate R&D
volpecr@crd.ge.com