www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Class Instance allocations

reply bearophile <bearophileHUGS lycos.com> writes:
This page says "The pointer returned from new() must be to memory aligned to
the default alignment. This is 8 on win32 systems.":
http://www.digitalmars.com/d/2.0/memory.html

Is that true? Is this required by the tag bits added by the GC scans? Does
std.c.stdlib.malloc always return aligned to 8 on Windows?

Below in the same page, in the Mark/Release section:

class Foo {
...
    new(size_t sz)
    {   void *p;
	p = &buffer[bufindex];
	bufindex += sz;
	if (bufindex > buffer.length)
	    throw new OutOfMemory;
	return p;
    }
...

If sz is not a multiple of 8 then new() can return a pointer not aligned to 8:

import std.outofmemory: OutOfMemoryException;
import std.gc: addRange, removeRange;
import std.c.stdlib: malloc, free;
import std.c.stdio: printf;

class Foo {
    static void[] buffer;
    static int bufIndex;
    static const int bufSize = 100;
    int x; //**********

    static this() {
        void* p = malloc(bufSize);
        if (!p)
            throw new OutOfMemoryException;
        addRange(p, p + bufSize);
        buffer = p[0 .. bufSize];
    }

    static ~this() {
        if (buffer.length) {
            removeRange(buffer.ptr);
            free(buffer.ptr);
            buffer = null;
        }
    }

    new(size_t sz) {
        void* p = &buffer[bufIndex];
        bufIndex += sz;
        if (bufIndex > buffer.length)
            throw new OutOfMemoryException;
        printf("%d %d\n", cast(size_t)p, cast(size_t)p % 8); // ******
        return p;
    }

    delete(void* p) {
        assert(0);
    }

    static int mark() {
        return bufIndex;
    }

    static void release(int i) {
        bufIndex = i;
    }
}

void main() {
    int m = Foo.mark();
    Foo f1 = new Foo;  // allocate
    Foo f2 = new Foo;  // allocate
    Foo.release(m);    // deallocate f1 and f2
}

Output, f2 reference is now always aligned to 4 bytes:
1382368 0
1382380 4

So, does the new() in this class (that has that extra "x" field) need to be
fixed to be sure the pointers it returns are aligned to 8?

The Foo class in the Mark/Release section has some other little bugs, that I
think I have fixed here. D docs need something like the Andrei's book
machinery, where D code snippets are actually run (Python docs too run their
snippets).

Bye,
bearophile
Jan 11 2010
parent reply Justin Johansson <no spam.com> writes:
I understand your quandary.

For me "Win32" == "32 bits" == "4 bytes" == "sizeof(ubiquitous machine 
register)".

So why on earth, since Visual Studio 2001 (and before?), was the default 
alignment set to 8 bytes for the M$ C/C++ compiler code generation 
option?  This I just do not understand (so, of course, enlightenment is 
welcome).

Beers for the New Years,

Justin Johansson



bearophile wrote:
 This page says "The pointer returned from new() must be to memory aligned to
the default alignment. This is 8 on win32 systems.":
 http://www.digitalmars.com/d/2.0/memory.html
 
 Is that true? Is this required by the tag bits added by the GC scans? Does
std.c.stdlib.malloc always return aligned to 8 on Windows?
 
 Below in the same page, in the Mark/Release section:
 
 class Foo {
 ...
     new(size_t sz)
     {   void *p;
 	p = &buffer[bufindex];
 	bufindex += sz;
 	if (bufindex > buffer.length)
 	    throw new OutOfMemory;
 	return p;
     }
 ...
 
 If sz is not a multiple of 8 then new() can return a pointer not aligned to 8:
 
 import std.outofmemory: OutOfMemoryException;
 import std.gc: addRange, removeRange;
 import std.c.stdlib: malloc, free;
 import std.c.stdio: printf;
 
 class Foo {
     static void[] buffer;
     static int bufIndex;
     static const int bufSize = 100;
     int x; //**********
 
     static this() {
         void* p = malloc(bufSize);
         if (!p)
             throw new OutOfMemoryException;
         addRange(p, p + bufSize);
         buffer = p[0 .. bufSize];
     }
 
     static ~this() {
         if (buffer.length) {
             removeRange(buffer.ptr);
             free(buffer.ptr);
             buffer = null;
         }
     }
 
     new(size_t sz) {
         void* p = &buffer[bufIndex];
         bufIndex += sz;
         if (bufIndex > buffer.length)
             throw new OutOfMemoryException;
         printf("%d %d\n", cast(size_t)p, cast(size_t)p % 8); // ******
         return p;
     }
 
     delete(void* p) {
         assert(0);
     }
 
     static int mark() {
         return bufIndex;
     }
 
     static void release(int i) {
         bufIndex = i;
     }
 }
 
 void main() {
     int m = Foo.mark();
     Foo f1 = new Foo;  // allocate
     Foo f2 = new Foo;  // allocate
     Foo.release(m);    // deallocate f1 and f2
 }
 
 Output, f2 reference is now always aligned to 4 bytes:
 1382368 0
 1382380 4
 
 So, does the new() in this class (that has that extra "x" field) need to be
fixed to be sure the pointers it returns are aligned to 8?
 
 The Foo class in the Mark/Release section has some other little bugs, that I
think I have fixed here. D docs need something like the Andrei's book
machinery, where D code snippets are actually run (Python docs too run their
snippets).
 
 Bye,
 bearophile

Jan 13 2010
next sibling parent reply "Robert Jacques" <sandford jhu.edu> writes:
On Wed, 13 Jan 2010 10:29:35 -0500, Justin Johansson <no spam.com> wrote:

 I understand your quandary.

 For me "Win32" == "32 bits" == "4 bytes" == "sizeof(ubiquitous machine  
 register)".

 So why on earth, since Visual Studio 2001 (and before?), was the default  
 alignment set to 8 bytes for the M$ C/C++ compiler code generation  
 option?  This I just do not understand (so, of course, enlightenment is  
 welcome).

 Beers for the New Years,

 Justin Johansson

If I recall correctly, doubles require 8-byte alignment. So you object therefore must require 8-byte alignment.
Jan 13 2010
parent Justin Johansson <no spam.com> writes:
Robert Jacques wrote:
 On Wed, 13 Jan 2010 10:29:35 -0500, Justin Johansson <no spam.com> wrote:
 
 I understand your quandary.

 For me "Win32" == "32 bits" == "4 bytes" == "sizeof(ubiquitous machine 
 register)".

 So why on earth, since Visual Studio 2001 (and before?), was the 
 default alignment set to 8 bytes for the M$ C/C++ compiler code 
 generation option?  This I just do not understand (so, of course, 
 enlightenment is welcome).

 Beers for the New Years,

 Justin Johansson

If I recall correctly, doubles require 8-byte alignment. So you object therefore must require 8-byte alignment.

Instances of my Foo class require 17 bytes so therefore all objects require 17 byte alignment. But for my Bar class it's 42. ???
Jan 13 2010
prev sibling next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
Justin Johansson:
 So why on earth, since Visual Studio 2001 (and before?), was the default 
 alignment set to 8 bytes for the M$ C/C++ compiler code generation 
 option?

Maybe because for compatibility reasons they can't require a 16 bytes alignment (as OS X does, I think) as SSE registers appreciate.
Is that true? Is this required by the tag bits added by the GC scans? Does
std.c.stdlib.malloc always return aligned to 8 on Windows?<<


And no one has given me such answers yet. And currently the D GC seems to always return aligned to 16 bytes (even chunks of memory smaller than 16 bytes). Bye, bearophile
Jan 13 2010
next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
bearophile:
 And currently the D GC seems to always return aligned to 16 bytes (even chunks
of memory smaller than 16 bytes).<

I hope to be wrong :-)
Jan 13 2010
parent =?UTF-8?B?UGVsbGUgTcOlbnNzb24=?= <pelle.mansson gmail.com> writes:
On 01/13/2010 06:18 PM, bearophile wrote:
 bearophile:
 And currently the D GC seems to always return aligned to 16 bytes (even chunks
of memory smaller than 16 bytes).<

I hope to be wrong :-)

it somewhere around here.
Jan 13 2010
prev sibling parent bearophile <bearophileHUGS lycos.com> writes:
Steven Schveighoffer:
 I'm not sure if SSE register requires alignment.  Typically  
 on systems that do not support loading from a byte-aligned memory segment  
 will throw a bus error if you try to do it.

I think SSE registers require 8 byte alignment, but some of their operations are faster with data aligned on 16 bytes. In future this 16 byte alignment requirement will probably be slowly dropped (already partially dropped in core i7). At that point the 16 byte alignment of the D GC can become overkill.
 I think the reason the allocator must return 8-byte aligned memory is  
 because the allocator doesn't know what you're going to put in there, so  
 it must handle the worst case scenario (doubles).

I think it may also be a matter of tag bits added to pointers by the mark phase of the GC. I think two bits may suffice, so I don't know why the alignment to 8. Anyway, the new() method in the class I've shown at the top of this thread is wrong then, because sometimes it returns aligned to 4. It needs some extra machinery to fix this. Bye, bearophile
Jan 14 2010
prev sibling next sibling parent "Simen kjaeraas" <simen.kjaras gmail.com> writes:
Justin Johansson <no spam.com> wrote:
  If I recall correctly, doubles require 8-byte alignment. So you object  
 therefore must require 8-byte alignment.

Instances of my Foo class require 17 bytes so therefore all objects require 17 byte alignment. But for my Bar class it's 42. ???

If a member of your class requires n-byte alignment, your class will require (k*n)-byte alignment (where k is a positive integer), to ensure the member has its required alignment. Example: doubles require 8-byte alignment. You create class foo { double bar; }. If your foo is offset by 2 bytes from an 8-byte border, so is bar, and thus its alignment is broken. -- Simen
Jan 13 2010
prev sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Wed, 13 Jan 2010 12:16:30 -0500, bearophile <bearophileHUGS lycos.com>  
wrote:

 Justin Johansson:
 So why on earth, since Visual Studio 2001 (and before?), was the default
 alignment set to 8 bytes for the M$ C/C++ compiler code generation
 option?

Maybe because for compatibility reasons they can't require a 16 bytes alignment (as OS X does, I think) as SSE registers appreciate.

It depends on the architecture/register. On most chips, if you load a register from a segment of memory, that memory has to be n-byte aligned where n is the number of bytes for the register. But there are exceptions. I'm not sure if SSE register requires alignment. Typically on systems that do not support loading from a byte-aligned memory segment will throw a bus error if you try to do it.
 Is that true? Is this required by the tag bits added by the GC scans?  
 Does std.c.stdlib.malloc always return aligned to 8 on Windows?<<


And no one has given me such answers yet. And currently the D GC seems to always return aligned to 16 bytes (even chunks of memory smaller than 16 bytes).

I would guess that pointers on a 32-bit architecture need to be 4-byte aligned, otherwise, a struct like this: struct S { int *x; int *y; } would have to be minimum of 16 bytes! I think the reason the allocator must return 8-byte aligned memory is because the allocator doesn't know what you're going to put in there, so it must handle the worst case scenario (doubles). BTW, 16 bytes is the minimum size the GC returns, and I think it's a somewhat arbitrary decision. The minimum you could possibly return is 8 bytes, and it becomes a judgement call that depends on other factors if you want to increase the minimum size (less overhead per block vs. more wasted space). -Steve
Jan 14 2010