3

I'm curious why the size of vptr seems to take 64 bits on 64 bit machines and whether C++ actually require that.

All vptr need to do is to point to vtables, and since vtables cannot take too much memory and can be grouped together 32 bits ought to be more than enough to address them.

How many classes do you have in your program? 1000? 10000? How many virtual functions they have on average? Maybe 100? If the compiler+linker would place all vtables consecutively they cannot take more than a few MB. Addressing the specific vtable with a 32 bit index into the "array of all vtables" should work.

The reason I'm even talking about that is because of certain small classes with virtual functions; sometimes I see a huge arrays of objects of a class with just 2 words + vptr, and that 64-bit vptr has significant impact on memory usage.

Michael
  • 5,775
  • 2
  • 34
  • 53
  • 1
    The vptr is just a pointer to some structure. On 64 bit machines, pointers are 64 bits in size. Sure, you don't need all 64 bits for your vptr, but it's almost always a better idea to leave pointers as full-size, rather than do something like only store 32 bits and extend the pointer when you look up the vtable. – Justin Nov 30 '17 at 01:15
  • @Justin, vptr is a very special pointer into a structure of very limited size. I know vptr usually (maybe always) has the word size number of bits; I'm just curious why it **has** to. – Michael Nov 30 '17 at 01:17
  • 1
    AFAIK, it doesn't have to. It's just always implemented that way – Justin Nov 30 '17 at 01:18
  • 5
    There's nothing in the C++ Standard that requires a pointer to a virtual table let alone defines a specific requirement for it's size. basically you're asking us to tell you why somebody else decided to use an approach that you don't think is effective. – Captain Obvlious Nov 30 '17 at 01:18
  • A C++ shared library is often going to contain some vtables, and at least on Linux is typically loaded above the low 32-bit address space. Requiring a piece of the shared library to be loaded into the first 4 GB would be problematic. – Daniel Schepler Nov 30 '17 at 01:22
  • "_How many classes_" how many types or how many instances? – curiousguy Dec 04 '17 at 20:01
  • Language lawyer question? really? – curiousguy Dec 04 '17 at 20:15

2 Answers2

3

No, it doesn't have to be 64-bit. But, there are several reasons that it is:

  • There are a possibility, that the first member of the class needs a 64-bit alignment, so there is no benefit in this case
  • Usually, vptrs don't take significant memory
  • And the strongest argument: if vptr would be a 32-bit index, then all virtual function calls will be slower (because of the extra memory reference), and would generate larger code. This simply doesn't worth it.

Note, there is a memory model (ILP32, -mx32 switch for gcc), which is rarely used, where pointers are 32-bits, but 64-bit registers can be used.

Micro-optimizing for memory is not in focus nowadays. For example, the compiler is free to reorder members across access specifiers (so padding could be decreased), but no compiler known by me does this.

geza
  • 28,403
  • 6
  • 61
  • 135
  • Also, cross-module virtual calls get more complicated since each module has its own table, so how do you know which table to index into? – Raymond Chen Nov 30 '17 at 03:20
  • 1
    @RaymondChen: the runtime linker could do this: it could merge virtual tables into a big one, and fix the indexes. But yes, that's a problem that need to be solved. – geza Nov 30 '17 at 11:53
  • @raymond: pretty much how you would do it with pointer relocations. Each module appends it vtables to the global vtables array, and stores the offset. All constructors apply that offset when writing the vptr. Derived ctors might add a relative offset. – MSalters Nov 30 '17 at 14:16
  • looking forward to the error "Could not load the module because the global vtable array is too fragmented." – Raymond Chen Nov 30 '17 at 16:16
  • @geza , "Usually, vptrs don't take significant memory" doesn't apply in my case: I've got huge arrays of objects consisting of 2 doubles + vptr, so those vptrs take 1/3-ish of available memory. – Michael Nov 30 '17 at 17:17
  • 1
    @Michael: your case is an exception. But note, for your case, you didn't gain anything with 32-bit vptrs, because of the first point I wrote. Double is usually aligned to 64-bit in 64-bit exes, so there would be a 32-bit padding after the vptr anyway. If 64-bit vptrs are really a problem for you, you might consider not using virtual functions, but rolling your own virtual dispatch mechanism for these classes. This is doable, and if you don't have too much such classes, can be done easily. – geza Nov 30 '17 at 17:48
  • @Michael: btw, you could use a trick, if you don't need all the precision of your doubles: you could store a type index into the lowest bits of your doubles, so you can even save all the space the type index occupied. – geza Nov 30 '17 at 17:54
1

Your suspicion is right, that is indeed allowed by C++. 16 bits might not be realistic, though. And even if all vtables were bigger than 4 GB, they typically wouldn't be 4 billion entries big. A typical stable entry would be 64 bits, so 4 billion entries would take 32 GB.

MSalters
  • 173,980
  • 10
  • 155
  • 350