Thread: Efficiently access member variables

  1. #1
    Registered User
    Join Date
    Dec 2006
    Location
    Canada
    Posts
    3,229

    Efficiently access member variables

    If I have a function that needs very fast (down to instructions level) access to a few variables in a struct, how should I do it?

    The program in question is an interpreting emulator. The fetch/decode/execute function needs very fast access to the (emulated) registers.

    The logically correct way to do this would be to use a context struct (C style) or have them as member variables and function (C++ style).

    Code:
    struct Context {
         int regs[16];
    }
    
    void f(Context *ctx)
    {
         // do stuff with ctx->regs[]
    }
    ...
    Then
    
    Context ctx;
    f(&ctx);
    However, that means all accesses to regs need to go through at least a layer of redirection (de-referencing ctx).

    Or in C++
    Code:
    class C
    {
         int regs[16];
         ...
         void f()
         {
              // do things with regs[]
         }
    };
    which will compile to exactly the same thing (except ctx would be called "this").

    A more efficient way is to make them static
    Code:
    void f()
    {
         static int regs[16];
         // do things with regs[]
    }
    This way would save a de-reference.

    However, that's ugly. Is there a way to get best of both worlds? Somehow "instantiating" code?

  2. #2
    Registered User
    Join Date
    Jun 2005
    Posts
    6,815
    You're going to have a "dereference" anyway. If you're not accessing a member of a struct, you will still be accessing members of an array (which in C and C++, is often adding an offset to a pointer, and dereferencing the resultant pointer).

    If you want to avoid the "dereference" to access a member of a struct, just pass a pointer to regs around. To either a static member function of a class, or a "normal" (as in not a member of a class) function.

    You might be able to do some tricks with template instantiation, which allow the compiler to optimise out the struct accessing. But YMMV will vary with that, depending on compiler implementation.
    Last edited by grumpy; 03-31-2012 at 06:03 PM.
    Right 98% of the time, and don't care about the other 3%.

    If I seem grumpy or unhelpful in reply to you, or tell you you need to demonstrate more effort before you can expect help, it is likely you deserve it. Suck it up, Buttercup, and read this, this, and this before posting again.

  3. #3
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,661
    In the case of C++, then ctx.f() is (usually?) an inline function when f() is a small function declared within the class itself.

    Also, if your array subscripts are constants, there is no additional run-time overhead compared to accessing named variables, since the compiler will do all the address calculation at compile time.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  4. #4
    [](){}(); manasij7479's Avatar
    Join Date
    Feb 2011
    Location
    *nullptr
    Posts
    2,657
    I'm not sure how well it will perform in comparison, but you can try offsetof(3): offset of structure member - Linux man page .

  5. #5
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    I don't think there is any way to reasonably save the dereference. If you make them static data, then you instantly lock yourself in to a single-threaded emulator. It just seems like the wrong design.

    Put trust in the compiler. After inlining, cross-module optimizations etc, you may find that the context pointer gets placed directly in a (real) register and lives there for a long time, pretty much wiping out the cost of that dereference.
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  6. #6
    Registered User
    Join Date
    Dec 2006
    Location
    Canada
    Posts
    3,229
    In the case of C++, then ctx.f() is (usually?) an inline function when f() is a small function declared within the class itself.

    Also, if your array subscripts are constants, there is no additional run-time overhead compared to accessing named variables, since the compiler will do all the address calculation at compile time.
    That's true, but it seems like inlining won't get rid of the dereferencing by itself. The subscripts are not always constants. Since it's a RISC arch, almost all instructions work on all registers. Though I suppose that's a possible area of optimization.

    I'm not sure how well it will perform in comparison, but you can try offsetof(3): offset of structure member - Linux man page .
    I believe that's what the compiler will do either way.

    I don't think there is any way to reasonably save the dereference. If you make them static data, then you instantly lock yourself in to a single-threaded emulator. It just seems like the wrong design.

    Put trust in the compiler. After inlining, cross-module optimizations etc, you may find that the context pointer gets placed directly in a (real) register and lives there for a long time, pretty much wiping out the cost of that dereference.
    Yeah I really don't want to go down the static route. I guess that makes sense. LTO + inlining will probably have the pointer end up in a register.

    I think I'll just leave it there for now, and see what happens. Speed may not be a problem at all anyways, since I'm only trying to emulate an ARM core at 16.8MHz (GameBoy Advance), so I still have about 100 host cycles to 1 emulated cycle, which should be enough.

  7. #7
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    Since inlining is simply a suggestion to the compiler I would check the assembly language that is created from your code to ensure it actually did inline them...that is if performance is super critical. If not then I wouldn't go through the trouble. Often times I have been surprised by the un-optimized nature of some assembly output (at least from MSVS) even with all the doo-dads and optimizations turned on.

  8. #8
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Quote Originally Posted by VirtualAce View Post
    Since inlining is simply a suggestion to the compiler I would check the assembly language that is created from your code to ensure it actually did inline them...that is if performance is super critical. If not then I wouldn't go through the trouble. Often times I have been surprised by the un-optimized nature of some assembly output (at least from MSVS) even with all the doo-dads and optimizations turned on.
    I've noticed that the major modern compilers all seem to be very aggressive with inlining nowadays, sometimes inlining surprisingly complex functions if it believes it might lead to better optimization. Also, when evaluating a piece of assembly code, remember that the compiler cannot assume anything about pointer aliases, even though human programmers often understand that certain pointers cannot alias each other, and the compiler therefore sometimes does things which appear stupid such as repeatedly dereferencing the same memory location. Many could probably by fixed by use of the restrict keyword.
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  9. #9
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by brewbuck View Post
    the restrict keyword.
    Hmmm, thanks. I'll be sure to abuse this one.
    Last edited by MK27; 04-03-2012 at 06:41 AM.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  10. #10
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    Be aware of the flaky and often bizarre support for `restrict' or `__restrict__'.

    That is one of things that should absolutely be used through macro.

    Soma

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Cannot access a non-static member
    By codebot in forum C# Programming
    Replies: 9
    Last Post: 09-26-2010, 03:06 PM
  2. How do I access this member?
    By Doagie in forum C Programming
    Replies: 10
    Last Post: 10-17-2009, 04:47 PM
  3. Providing access to member variables between classes
    By Tonto in forum C++ Programming
    Replies: 11
    Last Post: 06-19-2006, 01:06 PM
  4. why does this have private member access?
    By major_small in forum C++ Programming
    Replies: 9
    Last Post: 07-16-2004, 01:52 PM
  5. Class member variables only readable by member functions?
    By _Elixia_ in forum C++ Programming
    Replies: 4
    Last Post: 10-10-2003, 03:52 PM