Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.c > #21415 > unrolled thread

Array addressing styles: arr[i].d[j] versus pArr->d[j], etc.

Started byJohn Reye <jononanon@googlemail.com>
First post2012-05-27 10:30 -0700
Last post2012-05-31 13:09 -0700
Articles 8 — 6 participants

Back to article view | Back to comp.lang.c


Contents

  Array addressing styles: arr[i].d[j] versus pArr->d[j], etc. John Reye <jononanon@googlemail.com> - 2012-05-27 10:30 -0700
    Re: Array addressing styles: arr[i].d[j] versus pArr->d[j], etc. John Reye <jononanon@googlemail.com> - 2012-05-27 10:35 -0700
      Re: Array addressing styles: arr[i].d[j] versus pArr->d[j], etc. pete <pfiland@mindspring.com> - 2012-05-27 14:11 -0400
    Re: Array addressing styles: arr[i].d[j] versus pArr->d[j], etc. Eric Sosman <esosman@ieee-dot-org.invalid> - 2012-05-27 14:01 -0400
      Re: Array addressing styles: arr[i].d[j] versus pArr->d[j], etc. pete <pfiland@mindspring.com> - 2012-05-27 14:20 -0400
    Re: Array addressing styles: arr[i].d[j] versus pArr->d[j], etc. Thomas Richter <thor@math.tu-berlin.de> - 2012-05-27 22:03 +0200
      Re: Array addressing styles: arr[i].d[j] versus pArr->d[j], etc. Johannes Bauer <dfnsonfsduifb@gmx.de> - 2012-05-28 14:54 +0200
    Re: Array addressing styles: arr[i].d[j] versus pArr->d[j], etc. Tim Rentsch <txr@alumni.caltech.edu> - 2012-05-31 13:09 -0700

#21415 — Array addressing styles: arr[i].d[j] versus pArr->d[j], etc.

FromJohn Reye <jononanon@googlemail.com>
Date2012-05-27 10:30 -0700
SubjectArray addressing styles: arr[i].d[j] versus pArr->d[j], etc.
Message-ID<d8247fad-17c7-4105-82c1-364f00dac222@p27g2000vbl.googlegroups.com>
Hi!

In writing code I often find numerous ways of expressing the same
thing.
I have some questions of "style" and "optimization" that I'd like to
ask.

Example1
========

Variant 1a)

arr[i].a = f1();
arr[i].b = f2();
arr[i].c = f3();

OR

Variant 1b)

struct tmp * const pArr = &arr[i]; // arr + i
pArr->a = f1();
pArr->b = f2();
pArr->c = f3();


Here I probably prefer the 1b). But not on reasons of style, but on
the suspicion (*) that some compilers will produce more efficient code
with the 1b).

(*) - what do you think?



Example2
========

Variant 2a)

arr[i].a = f1();
arr[i].b = f2();
arr[i].c = f3();
arr[i].d[j++] = f4();
arr[i].d[j]   = f5();

OR

Variant 2b)

struct tmp * const pArr = &arr[i]; // arr + i
pArr->a = f1();
pArr->b = f2();
pArr->c = f3();
pArr->d[j++] = f4();
pArr->d[j]   = f5();

OR

Variant 2c)

struct tmp * const pArr = &arr[i]; // arr + i
pArr->a = f1();
pArr->b = f2();
pArr->c = f3();
int * pd = &(pArr->d[j++]);
*pd++ = f4();
*pd   = f5();

Here I'd definatley NOT use 2a) because I suspect that double array-
indexing via 2 `[]'-operators is not efficient.
I'd use either 2b) or 2c).


What are you're opinions on this? Which style do you use?
Any tips are highly appreciated. Thanks.

[toc] | [next] | [standalone]


#21416

FromJohn Reye <jononanon@googlemail.com>
Date2012-05-27 10:35 -0700
Message-ID<4657521a-93ce-465f-a76e-8aa6854558c3@w24g2000vby.googlegroups.com>
In reply to#21415
For a concrete example, consider the following:
Do you prefer fill_array1() or fill_array2()




#define ARR_ELEMS 4

struct st1
{
  char * name;
  int i;
  int ringbuf[ARR_ELEMS];
  unsigned ringbuf_current_index;
};



#define ST1_ELEMS 10

struct st1       st[ST1_ELEMS];
unsigned statistics[ST1_ELEMS];
                 /*     |        */
                 /* common index */



char *gen_name()
{
  static char a[] = "asdf";
  return a;
}

int gen_value()
{
  static int a = 0;
  return a++;
}


unsigned inc_ringbuf_index(unsigned i)
{
  if (++i == ARR_ELEMS)
    return 0U;
  return i;
}



void fill_array1(unsigned i)
{
  st[i].i = i;
  st[i].name = gen_name(i);
  st[i].ringbuf[ st[i].ringbuf_current_index  ] = gen_value();
  st[i].ringbuf_current_index =
inc_ringbuf_index( st[i].ringbuf_current_index );
  statistics[i]++;
}


void fill_array2(unsigned i)
{
  struct st1 * const pst = &st[i]; // st + i
  unsigned idx = pst->ringbuf_current_index;

  pst->i = i;
  pst->name = gen_name(i);
  pst->ringbuf[ idx ] = gen_value();
  pst->ringbuf_current_index = inc_ringbuf_index( idx );
  statistics[i]++;
}

[toc] | [prev] | [next] | [standalone]


#21419

Frompete <pfiland@mindspring.com>
Date2012-05-27 14:11 -0400
Message-ID<4FC26E34.56C@mindspring.com>
In reply to#21416
John Reye wrote:
> 
> For a concrete example, consider the following:
> Do you prefer fill_array1() or fill_array2()
> 
> #define ARR_ELEMS 4
> 
> struct st1
> {
>   char * name;
>   int i;
>   int ringbuf[ARR_ELEMS];
>   unsigned ringbuf_current_index;
> };
> 
> #define ST1_ELEMS 10
> 
> struct st1       st[ST1_ELEMS];
> unsigned statistics[ST1_ELEMS];
>                  /*     |        */
>                  /* common index */
> 
> char *gen_name()
> {
>   static char a[] = "asdf";
>   return a;
> }
> 
> int gen_value()
> {
>   static int a = 0;
>   return a++;
> }
> 
> unsigned inc_ringbuf_index(unsigned i)
> {
>   if (++i == ARR_ELEMS)
>     return 0U;
>   return i;
> }
> 
> void fill_array1(unsigned i)
> {
>   st[i].i = i;
>   st[i].name = gen_name(i);
>   st[i].ringbuf[ st[i].ringbuf_current_index  ] = gen_value();
>   st[i].ringbuf_current_index =
> inc_ringbuf_index( st[i].ringbuf_current_index );
>   statistics[i]++;
> }
> 
> void fill_array2(unsigned i)
> {
>   struct st1 * const pst = &st[i]; // st + i
>   unsigned idx = pst->ringbuf_current_index;
> 
>   pst->i = i;
>   pst->name = gen_name(i);
>   pst->ringbuf[ idx ] = gen_value();
>   pst->ringbuf_current_index = inc_ringbuf_index( idx );
>   statistics[i]++;
> }

The array addressing style that I would use
depends on the code is doing.
If (i) remains unchanged in a function,
such as you have shown in your fill_array functions,
then I might be inclined to go with fill_array2.

But, if I were cycling through the array

    for (i = 0; ST1_ELEMS > i; ++i) {
        puts(st[i].name);
    }

then I would use the first array addressing style instead.

-- 
pete

[toc] | [prev] | [next] | [standalone]


#21418

FromEric Sosman <esosman@ieee-dot-org.invalid>
Date2012-05-27 14:01 -0400
Message-ID<jptq6q$27g$1@dont-email.me>
In reply to#21415
On 5/27/2012 1:30 PM, John Reye wrote:
> Hi!
>
> In writing code I often find numerous ways of expressing the same
> thing.
> I have some questions of "style" and "optimization" that I'd like to
> ask.
> [...]
> (*) - what do you think?

     I think this is Question 20.14 on the comp.lang.c Frequently
Asked Questions (FAQ) page, <http://www.c-faq.com/>.

-- 
Eric Sosman
esosman@ieee-dot-org.invalid

[toc] | [prev] | [next] | [standalone]


#21420

Frompete <pfiland@mindspring.com>
Date2012-05-27 14:20 -0400
Message-ID<4FC27072.638B@mindspring.com>
In reply to#21418
Eric Sosman wrote:
> 
> On 5/27/2012 1:30 PM, John Reye wrote:
> > Hi!
> >
> > In writing code I often find numerous ways of expressing the same
> > thing.
> > I have some questions of "style" and "optimization" that I'd like to
> > ask.
> > [...]
> > (*) - what do you think?
> 
>      I think this is Question 20.14 on the comp.lang.c Frequently
> Asked Questions (FAQ) page, <http://www.c-faq.com/>.

I think John Reye's question was more about
whether to use a const qualified variable (pst) 
in a function 
to hold the sum of two variables (st + i),
when the sum was going to be used frequently
and the values of the two variables 
were not going to change within the function.

I would be inclined to use a const qualified variable in such a case.

-- 
pete

[toc] | [prev] | [next] | [standalone]


#21424

FromThomas Richter <thor@math.tu-berlin.de>
Date2012-05-27 22:03 +0200
Message-ID<jpu19n$bm5$1@news.belwue.de>
In reply to#21415
On 27.05.2012 19:30, John Reye wrote:
> Hi!
>
> In writing code I often find numerous ways of expressing the same
> thing.
> I have some questions of "style" and "optimization" that I'd like to
> ask.
>
> Example1
> ========
>
> Variant 1a)
>
> arr[i].a = f1();
> arr[i].b = f2();
> arr[i].c = f3();
>
> OR
>
> Variant 1b)
>
> struct tmp * const pArr =&arr[i]; // arr + i
> pArr->a = f1();
> pArr->b = f2();
> pArr->c = f3();
>
>
> Here I probably prefer the 1b). But not on reasons of style, but on
> the suspicion (*) that some compilers will produce more efficient code
> with the 1b).
>
> (*) - what do you think?

"Premature optimization is the root of all evil.". Your coding style 
shouldn't be based on suspicions on what the compiler might or might not 
be able to do. Go for clarity. For a simple program as given, I would 
pick a) because it is more readable. And should profiling *really* 
identify this as a bottleneck, then, and only then, consider alternatives.

Frankly, I believe any decent compiler will likely generate identical 
code for both cases.

[toc] | [prev] | [next] | [standalone]


#21467

FromJohannes Bauer <dfnsonfsduifb@gmx.de>
Date2012-05-28 14:54 +0200
Message-ID<jpvsgl$tul$1@news.albasani.net>
In reply to#21424
On 27.05.2012 22:03, Thomas Richter wrote:

>> Example1
>> ========
>>
>> Variant 1a)
>>
>> arr[i].a = f1();
>> arr[i].b = f2();
>> arr[i].c = f3();
>>
>> OR
>>
>> Variant 1b)
>>
>> struct tmp * const pArr =&arr[i]; // arr + i
>> pArr->a = f1();
>> pArr->b = f2();
>> pArr->c = f3();
>
> Frankly, I believe any decent compiler will likely generate identical
> code for both cases.

Just for fun I tried this out on x86-64 Linux with gcc 4.6.2 (knowing
full well this is a *language* group, but oh well...).

Fun fact: 1a) for my constellation produces *faster* code than 1b)! Very
cool. If you look at the assembly of 1a)

  400512:	48 81 ec 08 10 00 00 	sub    $0x1008,%rsp
  400519:	48 8d 5c 24 04       	lea    0x4(%rsp),%rbx
  40051e:	48 8d ac 24 04 10 00 	lea    0x1004(%rsp),%rbp
  400525:	00

Loop:
  400526:	e8 2f 01 00 00       	callq  40065a <f1>
  40052b:	89 03                	mov    %eax,(%rbx)
  40052d:	e8 2e 01 00 00       	callq  400660 <f2>
  400532:	89 43 04             	mov    %eax,0x4(%rbx)
  400535:	e8 2c 01 00 00       	callq  400666 <f3>
  40053a:	89 43 08             	mov    %eax,0x8(%rbx)
  40053d:	48 83 c3 20          	add    $0x20,%rbx
  400541:	48 39 eb             	cmp    %rbp,%rbx
  400544:	75 e0                	jne    400526 <main+0x16>

and compare against 1b)

  400512:	48 81 ec 08 10 00 00 	sub    $0x1008,%rsp
  400519:	31 db                	xor    %ebx,%ebx

Loop:
  40051b:	48 89 dd             	mov    %rbx,%rbp
  40051e:	48 c1 e5 05          	shl    $0x5,%rbp
  400522:	48 8d 04 24          	lea    (%rsp),%rax
  400526:	48 01 c5             	add    %rax,%rbp
  400529:	e8 34 01 00 00       	callq  400662 <f1>
  40052e:	89 45 04             	mov    %eax,0x4(%rbp)
  400531:	e8 32 01 00 00       	callq  400668 <f2>
  400536:	89 45 08             	mov    %eax,0x8(%rbp)
  400539:	e8 30 01 00 00       	callq  40066e <f3>
  40053e:	89 45 0c             	mov    %eax,0xc(%rbp)
  400541:	48 83 c3 01          	add    $0x1,%rbx
  400545:	48 81 fb 80 00 00 00 	cmp    $0x80,%rbx
  40054c:	75 cd                	jne    40051b <main+0xb>

I.e. variant 1a) does a load effective address once and increments by
the size of the struct each time, using the dispacement to assign the
values to the struct members.

Variant 2a) counts through 0...(n-1) and does a LEA *each* iteration.

Very cool result IMO (especially since it nicely reinforces the point
that "optimizing" code thinking it will run faster is a stupid idea
without knowing *exactly* what happens).

Best regards,
Joe

-- 
>> Wo hattest Du das Beben nochmal GENAU vorhergesagt?
> Zumindest nicht öffentlich!
Ah, der neueste und bis heute genialste Streich unsere großen
Kosmologen: Die Geheim-Vorhersage.
 - Karl Kaos über Rüdiger Thomas in dsa <hidbv3$om2$1@speranza.aioe.org>

[toc] | [prev] | [next] | [standalone]


#21595

FromTim Rentsch <txr@alumni.caltech.edu>
Date2012-05-31 13:09 -0700
Message-ID<kfnsjegyuxq.fsf@x-alumni2.alumni.caltech.edu>
In reply to#21415
John Reye <jononanon@googlemail.com> writes:

> Hi!
>
> In writing code I often find numerous ways of expressing the same
> thing.
> I have some questions of "style" and "optimization" that I'd like to
> ask.
>
> Example1
> ========
>
> Variant 1a)
>
> arr[i].a = f1();
> arr[i].b = f2();
> arr[i].c = f3();
>
> OR
>
> Variant 1b)
>
> struct tmp * const pArr = &arr[i]; // arr + i
> pArr->a = f1();
> pArr->b = f2();
> pArr->c = f3();
>
>
> Here I probably prefer the 1b). But not on reasons of style, but on
> the suspicion (*) that some compilers will produce more efficient code
> with the 1b).
>
> (*) - what do you think?
>
>
>
> Example2
> ========
>
> Variant 2a)
>
> arr[i].a = f1();
> arr[i].b = f2();
> arr[i].c = f3();
> arr[i].d[j++] = f4();
> arr[i].d[j]   = f5();
>
> OR
>
> Variant 2b)
>
> struct tmp * const pArr = &arr[i]; // arr + i
> pArr->a = f1();
> pArr->b = f2();
> pArr->c = f3();
> pArr->d[j++] = f4();
> pArr->d[j]   = f5();
>
> OR
>
> Variant 2c)
>
> struct tmp * const pArr = &arr[i]; // arr + i
> pArr->a = f1();
> pArr->b = f2();
> pArr->c = f3();
> int * pd = &(pArr->d[j++]);
> *pd++ = f4();
> *pd   = f5();
>
> Here I'd definatley NOT use 2a) because I suspect that double array-
> indexing via 2 `[]'-operators is not efficient.
> I'd use either 2b) or 2c).
>
>
> What are you're opinions on this? Which style do you use?
> Any tips are highly appreciated. Thanks.

IMO any appeal to efficiency here is misguided.  Common subexpression
optimization, which is all that's needed here, has been around more
than 40 years.  The pointer variants are unlikely to run any faster
than the indexing-based versions, and actually may run slower (as some
have noted).  As is the case with almost all micro-optimizations, the
best choice is the one that expresses what the developer means to do
most simply and most directly, and let the compiler take care of
generating good code, because in most such cases it will do a better
job.

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.c


csiph-web