www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - How do you deal with scoped allocations?

reply "Namespace" <rswhite4 googlemail.com> writes:
Since my last thread doesn't get much attention I like to ask 
here: How did you deal with temporary memory? Let's assume that 
the size is only known at runtime.
I have this situation e.g. in Dgame in the capture method: I get 
the pixel data from my Window with glReadPixel but it is 
reversed. So I have to reverse it again, but I still need at 
least temporary memory for one pixel-line which stores the 
currently swapped pixels. So how would you solve such a situation?

Since D doesn't offer VLA's and alloca is broken (besides the 
ugly syntax),
I use a scoped wrapper (since scope doesn't do the job):

----
struct scoped(A : T[], T) {
	T[] arr;

	alias arr this;

	this(T[] arr) {
		this.arr = arr;

		writefln("Get %d %s's (ptr = %x)", arr.length, T.stringof, 
arr.ptr);
	}

	~this() {
		GC.free(this.arr.ptr);
		this.arr = null;
		GC.minimize();
	}
}

void main() {
     // need temp memory
     scoped!(int[]) arr = new int[n];
}
----

And what do you use?
Dec 07 2013
next sibling parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
On Saturday, 7 December 2013 at 22:32:59 UTC, Namespace wrote:
 Since D doesn't offer VLA's and alloca is broken (besides the 
 ugly syntax),
 I use a scoped wrapper (since scope doesn't do the job):
I like to use a helper template that defines a static array with a typical size. Then, if the runtime requirement is less than that, it can simply slice into the static array, and if not, then do the alloc/free pair.
Dec 07 2013
parent reply "Namespace" <rswhite4 googlemail.com> writes:
On Saturday, 7 December 2013 at 23:21:05 UTC, Adam D. Ruppe wrote:
 On Saturday, 7 December 2013 at 22:32:59 UTC, Namespace wrote:
 Since D doesn't offer VLA's and alloca is broken (besides the 
 ugly syntax),
 I use a scoped wrapper (since scope doesn't do the job):
I like to use a helper template that defines a static array with a typical size. Then, if the runtime requirement is less than that, it can simply slice into the static array, and if not, then do the alloc/free pair.
You mean something like that? ---- struct Helper(T, uint StackSize = 128) { static T[StackSize] buffer = void; static T[] opCall(size_t n) { if (n <= StackSize) return buffer[0 .. n]; return new T[n]; } } void main() { int[] arr = Helper!int(512); } ----
Dec 07 2013
next sibling parent "Adam D. Ruppe" <destructionator gmail.com> writes:
On Saturday, 7 December 2013 at 23:26:05 UTC, Namespace wrote:
 You mean something like that?
Yes.
Dec 07 2013
prev sibling parent reply "David Nadlinger" <code klickverbot.at> writes:
On Saturday, 7 December 2013 at 23:26:05 UTC, Namespace wrote:
 struct Helper(T, uint StackSize = 128) {
 	static T[StackSize] buffer = void;
Using a static variable here means that there is only one such buffer per thread (and e.g. it wouldn't be possible to use the helper twice in the same call stack) - is this really what you want? David
Dec 07 2013
parent reply "Namespace" <rswhite4 googlemail.com> writes:
On Sunday, 8 December 2013 at 00:05:42 UTC, David Nadlinger wrote:
 On Saturday, 7 December 2013 at 23:26:05 UTC, Namespace wrote:
 struct Helper(T, uint StackSize = 128) {
 	static T[StackSize] buffer = void;
Using a static variable here means that there is only one such buffer per thread (and e.g. it wouldn't be possible to use the helper twice in the same call stack) - is this really what you want? David
That is not what I want or what I use. But it seems that Adam D. Ruppe use such a thing. What would you use?
Dec 07 2013
parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
On Sunday, 8 December 2013 at 00:11:18 UTC, Namespace wrote:
 But it seems that Adam D. Ruppe use such a thing. What would 
 you use?
I didn't pay close enough attention; I don't use static, just the same general pattern there of static array up to a certain size.
Dec 07 2013
parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
On Sunday, 8 December 2013 at 00:16:46 UTC, Adam D. Ruppe wrote:
 the same general pattern there of static array up to a certain 
 size.
static array being "T[max_size]", not "static T[max_size]" just so it uses the stack for most things.
Dec 07 2013
parent "Namespace" <rswhite4 googlemail.com> writes:
On Sunday, 8 December 2013 at 00:17:48 UTC, Adam D. Ruppe wrote:
 On Sunday, 8 December 2013 at 00:16:46 UTC, Adam D. Ruppe wrote:
 the same general pattern there of static array up to a certain 
 size.
static array being "T[max_size]", not "static T[max_size]" just so it uses the stack for most things.
So rather something like this: ---- import std.stdio; struct Helper(T, uint StackSize = 128) { T[StackSize] buffer = void; T[] allocate(size_t n) { if (n <= StackSize) return buffer[0 .. n]; return new T[n]; } } void main() { auto h = Helper!int(); int[] arr = h.allocate(512); } ---- Yes? I don't like it, because it isn't a one liner. Isn't it possible in one line? A personal dream of me would be: scope int[] arr = new int[n]; or the implementation of DIP46. :)
Dec 08 2013
prev sibling next sibling parent reply Dmitry Olshansky <dmitry.olsh gmail.com> writes:
08-Dec-2013 02:32, Namespace пишет:
 Since my last thread doesn't get much attention I like to ask here: How
 did you deal with temporary memory? Let's assume that the size is only
 known at runtime.
 I have this situation e.g. in Dgame in the capture method: I get the
 pixel data from my Window with glReadPixel but it is reversed. So I have
 to reverse it again, but I still need at least temporary memory for one
 pixel-line which stores the currently swapped pixels. So how would you
 solve such a situation?

 Since D doesn't offer VLA's and alloca is broken (besides the ugly syntax),
 I use a scoped wrapper (since scope doesn't do the job):

 ----
 struct scoped(A : T[], T) {
      T[] arr;

      alias arr this;

      this(T[] arr) {
          this.arr = arr;

          writefln("Get %d %s's (ptr = %x)", arr.length, T.stringof,
 arr.ptr);
      }

      ~this() {
          GC.free(this.arr.ptr);
          this.arr = null;
          GC.minimize();
This is slow. Just use malloc & free, why touch GC at all?
      }
 }

 void main() {
      // need temp memory
      scoped!(int[]) arr = new int[n];
 }
 ----

 And what do you use?
-- Dmitry Olshansky
Dec 08 2013
parent reply "Namespace" <rswhite4 googlemail.com> writes:
On Sunday, 8 December 2013 at 09:14:44 UTC, Dmitry Olshansky 
wrote:
 08-Dec-2013 02:32, Namespace пишет:
 Since my last thread doesn't get much attention I like to ask 
 here: How
 did you deal with temporary memory? Let's assume that the size 
 is only
 known at runtime.
 I have this situation e.g. in Dgame in the capture method: I 
 get the
 pixel data from my Window with glReadPixel but it is reversed. 
 So I have
 to reverse it again, but I still need at least temporary 
 memory for one
 pixel-line which stores the currently swapped pixels. So how 
 would you
 solve such a situation?

 Since D doesn't offer VLA's and alloca is broken (besides the 
 ugly syntax),
 I use a scoped wrapper (since scope doesn't do the job):

 ----
 struct scoped(A : T[], T) {
     T[] arr;

     alias arr this;

     this(T[] arr) {
         this.arr = arr;

         writefln("Get %d %s's (ptr = %x)", arr.length, 
 T.stringof,
 arr.ptr);
     }

     ~this() {
         GC.free(this.arr.ptr);
         this.arr = null;
         GC.minimize();
This is slow. Just use malloc & free, why touch GC at all?
     }
 }

 void main() {
     // need temp memory
     scoped!(int[]) arr = new int[n];
 }
 ----

 And what do you use?
Because it's more D'ish. That is what D offers. Why is it slower than malloc + free?
Dec 08 2013
next sibling parent "John Colvin" <john.loughran.colvin gmail.com> writes:
On Sunday, 8 December 2013 at 09:25:41 UTC, Namespace wrote:
 On Sunday, 8 December 2013 at 09:14:44 UTC, Dmitry Olshansky 
 wrote:
 08-Dec-2013 02:32, Namespace пишет:
 Since my last thread doesn't get much attention I like to ask 
 here: How
 did you deal with temporary memory? Let's assume that the 
 size is only
 known at runtime.
 I have this situation e.g. in Dgame in the capture method: I 
 get the
 pixel data from my Window with glReadPixel but it is 
 reversed. So I have
 to reverse it again, but I still need at least temporary 
 memory for one
 pixel-line which stores the currently swapped pixels. So how 
 would you
 solve such a situation?

 Since D doesn't offer VLA's and alloca is broken (besides the 
 ugly syntax),
 I use a scoped wrapper (since scope doesn't do the job):

 ----
 struct scoped(A : T[], T) {
    T[] arr;

    alias arr this;

    this(T[] arr) {
        this.arr = arr;

        writefln("Get %d %s's (ptr = %x)", arr.length, 
 T.stringof,
 arr.ptr);
    }

    ~this() {
        GC.free(this.arr.ptr);
        this.arr = null;
        GC.minimize();
This is slow. Just use malloc & free, why touch GC at all?
    }
 }

 void main() {
    // need temp memory
    scoped!(int[]) arr = new int[n];
 }
 ----

 And what do you use?
Because it's more D'ish. That is what D offers. Why is it slower than malloc + free?
Well, for a start you're calling GC.minimize every time you leave the scope, which is an expensive call. Just using new and GC.free would be fine, but there is really no need to burden the GC with this at all; it's a textbook case for C's malloc/free.
Dec 08 2013
prev sibling parent reply Dmitry Olshansky <dmitry.olsh gmail.com> writes:
08-Dec-2013 13:25, Namespace пишет:
 On Sunday, 8 December 2013 at 09:14:44 UTC, Dmitry Olshansky wrote:
 08-Dec-2013 02:32, Namespace пишет:
[snip]
 And what do you use?
Because it's more D'ish.
There is no such objective quality as being D'ish. GC.malloc/GC.free are no better then malloc/free in code style, readability or observable effect.
 That is what D offers.
D offers you C run-time as well. In fact it builds on top of it. See also (hopefully) soon to be formally reviewed std.allocator.
 Why is it slower than malloc + free?
1. Because GC.minimize does a hell of a work to optimize the whole GC heap. For starters it does collection so as to get rid of floating garbage (AFAIK). 2. You have a trivial use case of deterministic allocation - you know precisely when to deallocate a block. In this setting putting allocated block into tracing GC heap is a waste of time should a collection happen. -- Dmitry Olshansky
Dec 08 2013
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 12/8/13 4:29 AM, Dmitry Olshansky wrote:
 08-Dec-2013 13:25, Namespace пишет:
 On Sunday, 8 December 2013 at 09:14:44 UTC, Dmitry Olshansky wrote:
 08-Dec-2013 02:32, Namespace пишет:
[snip]
 And what do you use?
Because it's more D'ish.
There is no such objective quality as being D'ish. GC.malloc/GC.free are no better then malloc/free in code style, readability or observable effect.
If you put (arguably by mistake) a scoped member inside a class, the malloc'd memory will leak but the GC-allocated memory will be ultimately collected. Andrei
Dec 08 2013
prev sibling next sibling parent "John Colvin" <john.loughran.colvin gmail.com> writes:
On Saturday, 7 December 2013 at 22:32:59 UTC, Namespace wrote:
 Since my last thread doesn't get much attention I like to ask 
 here: How did you deal with temporary memory? Let's assume that 
 the size is only known at runtime.
 I have this situation e.g. in Dgame in the capture method: I 
 get the pixel data from my Window with glReadPixel but it is 
 reversed. So I have to reverse it again, but I still need at 
 least temporary memory for one pixel-line which stores the 
 currently swapped pixels. So how would you solve such a 
 situation?

 Since D doesn't offer VLA's and alloca is broken (besides the 
 ugly syntax),
 I use a scoped wrapper (since scope doesn't do the job):

 ----
 struct scoped(A : T[], T) {
 	T[] arr;

 	alias arr this;

 	this(T[] arr) {
 		this.arr = arr;

 		writefln("Get %d %s's (ptr = %x)", arr.length, T.stringof, 
 arr.ptr);
 	}

 	~this() {
 		GC.free(this.arr.ptr);
 		this.arr = null;
 		GC.minimize();
 	}
 }

 void main() {
     // need temp memory
     scoped!(int[]) arr = new int[n];
 }
 ----

 And what do you use?
From my probably somewhat incomplete understanding of such things: This is not a good use-case for the gc. Use the c heap or just let the gc do its job normally.
Dec 08 2013
prev sibling next sibling parent reply "thedeemon" <dlang thedeemon.com> writes:
On Saturday, 7 December 2013 at 22:32:59 UTC, Namespace wrote:
 Since my last thread doesn't get much attention I like to ask 
 here: How did you deal with temporary memory?
I just use scope(exit) delete buf; Your solution seems to do a GC every time some buffer goes out of scope, which is slooow.
Dec 08 2013
next sibling parent reply Joseph Rushton Wakeling <joseph.wakeling webdrake.net> writes:
On 08/12/13 19:18, thedeemon wrote:
 I just use
      scope(exit) delete buf;
Deprecated or at least disapproved of, no?
Dec 08 2013
parent reply "Namespace" <rswhite4 googlemail.com> writes:
On Sunday, 8 December 2013 at 18:33:39 UTC, Joseph Rushton 
Wakeling wrote:
 On 08/12/13 19:18, thedeemon wrote:
 I just use
     scope(exit) delete buf;
Deprecated or at least disapproved of, no?
Sadly yes.
Dec 08 2013
parent reply "monarch_dodra" <monarchdodra gmail.com> writes:
On Sunday, 8 December 2013 at 18:55:26 UTC, Namespace wrote:
 On Sunday, 8 December 2013 at 18:33:39 UTC, Joseph Rushton 
 Wakeling wrote:
 On 08/12/13 19:18, thedeemon wrote:
 I just use
    scope(exit) delete buf;
Deprecated or at least disapproved of, no?
Sadly yes.
Unless I'm mistaken, the only thing that was deprecated was scoped as an attribute to variables. The scope *statement* is till valid, and one of D's more powerful arguments in "simple and exception safe programming".
Dec 09 2013
parent reply "Namespace" <rswhite4 googlemail.com> writes:
On Monday, 9 December 2013 at 12:29:44 UTC, monarch_dodra wrote:
 On Sunday, 8 December 2013 at 18:55:26 UTC, Namespace wrote:
 On Sunday, 8 December 2013 at 18:33:39 UTC, Joseph Rushton 
 Wakeling wrote:
 On 08/12/13 19:18, thedeemon wrote:
 I just use
   scope(exit) delete buf;
Deprecated or at least disapproved of, no?
Sadly yes.
Unless I'm mistaken, the only thing that was deprecated was scoped as an attribute to variables. The scope *statement* is till valid, and one of D's more powerful arguments in "simple and exception safe programming".
'delete' was meant. ;)
Dec 09 2013
parent "monarch_dodra" <monarchdodra gmail.com> writes:
On Monday, 9 December 2013 at 12:32:36 UTC, Namespace wrote:
 On Monday, 9 December 2013 at 12:29:44 UTC, monarch_dodra wrote:
 On Sunday, 8 December 2013 at 18:55:26 UTC, Namespace wrote:
 On Sunday, 8 December 2013 at 18:33:39 UTC, Joseph Rushton 
 Wakeling wrote:
 On 08/12/13 19:18, thedeemon wrote:
 I just use
  scope(exit) delete buf;
Deprecated or at least disapproved of, no?
Sadly yes.
Unless I'm mistaken, the only thing that was deprecated was scoped as an attribute to variables. The scope *statement* is till valid, and one of D's more powerful arguments in "simple and exception safe programming".
'delete' was meant. ;)
*slaps face*
Dec 09 2013
prev sibling parent reply "Namespace" <rswhite4 googlemail.com> writes:
 I just use
     scope(exit) delete buf;

 Your solution seems to do a GC every time some buffer goes out 
 of scope, which is slooow.
I don't need scoped allocations that often. Otherwise I wouldn't use GC.minimize ;) or wouldn't use the GC at all. John Colvin: > Well, for a start you're calling GC.minimize every time you leave > the scope, which is an expensive call. That was my own decision and is of course not required, but I like to clean up the wasted space. [1] And as I said, I don't use scope allocations that often. > Just using new and GC.free would be fine, but there is really no > need to burden the GC with this at all; it's a textbook case for > C's malloc/free. The GC relies also on malloc / free. Dmitry Olshansky: > There is no such objective quality as being D'ish. > GC.malloc/GC.free are no better then malloc/free in code style, > readability or observable effect. But I don't use GC.malloc / GC.free. I use new which is IMO a lot nicer and more clean. Of course I now that new is only D syntax for malloc but it is still more clean. > D offers you C run-time as well. In fact it builds on top of it. > See also (hopefully) soon to be formally reviewed std.allocator. It is still C even if D offers the access to the C standard lib. > 1. Because GC.minimize does a hell of a work to optimize the whole GC > heap. For starters it does collection so as to get rid of floating > garbage (AFAIK). See [1]
Dec 08 2013
parent reply "John Colvin" <john.loughran.colvin gmail.com> writes:
On Sunday, 8 December 2013 at 19:00:29 UTC, Namespace wrote:
  > Just using new and GC.free would be fine, but there is 
 really no
  > need to burden the GC with this at all; it's a textbook case 
 for
  > C's malloc/free.
 The GC relies also on malloc / free.
Yes, it does. However, obviously it does a lot more than just call malloc/free. If you statically know the lifetime of a chunk of memory, then there really is no point invoking all that extra heavyweight code* when you can just DIY at no extra cost.** *and adding to the collection burden (if triggered while the memory is in scope) **However, take care with unique pointers from inside the allocated block to resources outside the block, as the GC might not be able to find them later (see P.S.). P.S. does anyone know how the GC interacts with core.stdc.free? I.e. if you free a pointer, but don't null the pointer, presumably the GC will still scan the memory despite it being freed. Isn't this undefined behaviour?
Dec 09 2013
next sibling parent "thedeemon" <dlang thedeemon.com> writes:
On Monday, 9 December 2013 at 09:13:20 UTC, John Colvin wrote:

 P.S. does anyone know how the GC interacts with core.stdc.free? 
 I.e. if you free a pointer, but don't null the pointer, 
 presumably the GC will still scan the memory despite it being 
 freed. Isn't this undefined behaviour?
From what I understand, if you malloc a buffer it lies outside GC's managed heap, so a pointer to this place will not be followed during GC scan. Unless you explicitly told it to, by calling GC.addRange().
Dec 09 2013
prev sibling parent Rainer Schuetze <r.sagitario gmx.de> writes:
On 09.12.2013 10:13, John Colvin wrote:
 On Sunday, 8 December 2013 at 19:00:29 UTC, Namespace wrote:
  > Just using new and GC.free would be fine, but there is really no
  > need to burden the GC with this at all; it's a textbook case for
  > C's malloc/free.
 The GC relies also on malloc / free.
Yes, it does. However, obviously it does a lot more than just call malloc/free. If you statically know the lifetime of a chunk of memory, then there really is no point invoking all that extra heavyweight code* when you can just DIY at no extra cost.** *and adding to the collection burden (if triggered while the memory is in scope) **However, take care with unique pointers from inside the allocated block to resources outside the block, as the GC might not be able to find them later (see P.S.).
I'm not sold on preferring C's malloc/free instead of the GC. - The operations GC.malloc and GC.free are very similar to the operations of the C functions (if no collection is triggered). The current implementation uses OS calls to get more memory from the system, only few extra data is allocated via malloc. GC.malloc/free might be missing a few optimizations (like lock-free allocations for small memory chunks), but they were a lot faster than what dmc's C runtime used to be - before the latter was switched to simply call the OS heap functions very recently. - if the GC.malloced memory does not contain pointers (e.g. allocated with new int[N]), it isn't scanned by the GC. Determining whether a pointer actually refers to GC managed memory or not might be faster for unmanaged memory, but this depends on the memory addresses. - if the GC.malloced memory might contain pointers to GC managed memory (like object references), you need to add and remove the memory range so that the GC scans the memory, but especially removing the range gets pretty expensive if a lot of ranges are added.
 P.S. does anyone know how the GC interacts with core.stdc.free? I.e. if
 you free a pointer, but don't null the pointer, presumably the GC will
 still scan the memory despite it being freed. Isn't this undefined
 behaviour?
Like thedeemon said, a pointer not pointing to GC managed memory is ignored, so this should do no harm.
Dec 13 2013
prev sibling parent Denis Shelomovskij <verylonglogin.reg gmail.com> writes:
08.12.2013 2:32, Namespace пишет:
 Since my last thread doesn't get much attention I like to ask here: How
 did you deal with temporary memory?
The algorithm is always this: 1. Use function stack frame for small allocations. 2. Use thread local stack allocator if temporary allocations corresponds LIFO principle and use thread local heap otherwise. As for usability it must be a single function call and D type system have to rest.
 And what do you use?
As I answered in previous thread: http://forum.dlang.org/thread/nxrxojbzbrfkkkkwvkqj forum.dlang.org#post-l77th1:24icc:241:40digitalmars.com -- Денис В. Шеломовский Denis V. Shelomovskij
Dec 12 2013