digitalmars.D - D needs emplacement new
- Namespace (33/35) Apr 25 2015 Consider both of these scripts: test_gc_new.d
- Adam D. Ruppe (21/21) Apr 25 2015 You could just call the constructor again to reuse an object. I
- Namespace (3/24) Apr 25 2015 Nice name, fits better. But that should be in phobos, don't you
- Adam D. Ruppe (3/5) Apr 25 2015 meh, it might be nice to have, but not being in phobos isn't a
- Namespace (28/33) Apr 25 2015 I'm sure newcomers (especially those coming from C++) would be
- weaselcat (3/37) Apr 25 2015 hmm...
- weaselcat (4/47) Apr 25 2015 woops, accidentally posted before finishing
- Namespace (1/4) Apr 25 2015 Non-Class. ;)
- weaselcat (4/8) Apr 25 2015 There's a class overload 3 down
- Namespace (9/19) Apr 25 2015 Which accepts a void[] chunk:
- Namespace (24/24) Apr 26 2015 Since I'm unable to rebuild phobos on my Windows 8.1 PC, would
- Namespace (10/10) Apr 27 2015 Since nobody wants to take that work, can at least someone
- anonymous (30/39) Apr 27 2015 Collected memory will be reused for new allocations. The GC won't
- "Casper =?UTF-8?B?RsOmcmdlbWFuZCI=?= <shorttail hotmail.com> (2/4) Apr 27 2015 Doesn't the GC regard every 4/8 sequence of bytes as a pointer?
- Adam D. Ruppe (3/4) Apr 27 2015 Only on the stack. On the heap, it knows what type the data is
- anonymous (3/25) May 01 2015 Probably issue 9614 - Uninitialized holes in function stack
- weaselcat (3/24) Apr 25 2015 doesn't this have the issue of not calling the ctor/dtor of
- Adam D. Ruppe (5/7) Apr 25 2015 You could also use .destroy(copy_of_obj); instead of __dtor which
Consider both of these scripts: test_gc_new.d (http://dpaste.dzfl.pl/4f36b165c502) and test_gc_emplace_new.d (http://dpaste.dzfl.pl/ff4e3c35479f) and these results: test_gc_new:test_gc_new.exe "--DRT-gcopt=profile:1"Number of collections: 140 Total GC prep time: 0 milliseconds Total mark time: 5 milliseconds Total sweep time: 9 milliseconds Total page recovery time: 2 milliseconds Max Pause Time: 0 milliseconds Grand total GC time: 18 milliseconds GC summary: 5 MB, 140 GC 18 ms, Pauses 6 ms < 0 ms test_gc_emplace_new:test_gc_emplace_new.exe "--DRT-gcopt=profile:1"Number of collections: 2 Total GC prep time: 0 milliseconds Total mark time: 0 milliseconds Total sweep time: 0 milliseconds Total page recovery time: 0 milliseconds Max Pause Time: 0 milliseconds Grand total GC time: 0 milliseconds GC summary: 5 MB, 2 GC 0 ms, Pauses 0 ms < 0 ms As you can see, thanks to the emplacement, I have 138 less GC collections. In C++ you can do new (f) Foo(...) instead of my New!(Foo)(f, ...) The C++ version is a lot more readable, nicer and safer. Therefore D should (since it says it is a system language like C++) add an equal syntax or, at least, implement a library version like mine to allow people a nice and easy way to reuse their object. We already have emplace (which my implementation uses) but to write emplace((cast(void*) obj)[0 .. __traits(classInstanceSize, Foo)], ...) is not nice, not intuitive and not safe and can be improved.
Apr 25 2015
You could just call the constructor again to reuse an object. I guess you should also reinitialize the memory, but that's pretty easy too. Instead of placement new to reuse an object, make a function like reset() or reinitialize() that destructs the first, then copies the init back over and calls the constructor again. Something like this: void reinitialize(ClassName, CtorArgs...)(ClassName obj, CtorArgs args) { assert(typeid(obj) == typeid(ClassName), "Don't use this on interfaces or base classes!"); static if(__traits(hasMember, obj, "__dtor")) obj.__dtor(); (cast(void*) obj)[0 .. typeid(obj).init.length] = typeid(obj).init[]; static if(__traits(hasMember, obj, "__ctor")) obj.__ctor(args); } Then, you create it however up front and just reinitialize it when you're all set. I think that is a bit clearer in meaning than placement new as well.
Apr 25 2015
On Saturday, 25 April 2015 at 18:46:52 UTC, Adam D. Ruppe wrote:You could just call the constructor again to reuse an object. I guess you should also reinitialize the memory, but that's pretty easy too. Instead of placement new to reuse an object, make a function like reset() or reinitialize() that destructs the first, then copies the init back over and calls the constructor again. Something like this: void reinitialize(ClassName, CtorArgs...)(ClassName obj, CtorArgs args) { assert(typeid(obj) == typeid(ClassName), "Don't use this on interfaces or base classes!"); static if(__traits(hasMember, obj, "__dtor")) obj.__dtor(); (cast(void*) obj)[0 .. typeid(obj).init.length] = typeid(obj).init[]; static if(__traits(hasMember, obj, "__ctor")) obj.__ctor(args); } Then, you create it however up front and just reinitialize it when you're all set. I think that is a bit clearer in meaning than placement new as well.Nice name, fits better. But that should be in phobos, don't you think?
Apr 25 2015
On Saturday, 25 April 2015 at 18:50:59 UTC, Namespace wrote:Nice name, fits better. But that should be in phobos, don't you think?meh, it might be nice to have, but not being in phobos isn't a big deal to me for little utilities like this.
Apr 25 2015
On Saturday, 25 April 2015 at 18:57:13 UTC, Adam D. Ruppe wrote:On Saturday, 25 April 2015 at 18:50:59 UTC, Namespace wrote:I'm sure newcomers (especially those coming from C++) would be delighted if they had the possibility to reuse their memory. In addition, it could be impossible for them to write such "hack" itself. I already hear the cries: "D wasted memory" and: "I can not reuse my memory". And then it would be less attractive for such people, to switch to D. I would suggest something like: ---- T emplace(T, U...)(ref T obj, auto ref U args) if (is(T == class)) { if (!obj) return null; import std.conv : emplace; // temporary, as long it is not in std.conv enum ClassSizeOf = __traits(classInstanceSize, T); void[] buf = (cast(void*) obj)[0 .. ClassSizeOf]; return emplace!(T)(buf, args); } T emplaceOrNew(T, U...)(ref T obj, auto ref U args) if (is(T == class)) { if (!obj) return new T(args); return emplace(obj, args); } ----Nice name, fits better. But that should be in phobos, don't you think?meh, it might be nice to have, but not being in phobos isn't a big deal to me for little utilities like this
Apr 25 2015
On Saturday, 25 April 2015 at 19:09:15 UTC, Namespace wrote:On Saturday, 25 April 2015 at 18:57:13 UTC, Adam D. Ruppe wrote:hmm...On Saturday, 25 April 2015 at 18:50:59 UTC, Namespace wrote:I'm sure newcomers (especially those coming from C++) would be delighted if they had the possibility to reuse their memory. In addition, it could be impossible for them to write such "hack" itself. I already hear the cries: "D wasted memory" and: "I can not reuse my memory". And then it would be less attractive for such people, to switch to D. I would suggest something like: ---- T emplace(T, U...)(ref T obj, auto ref U args) if (is(T == class)) { if (!obj) return null; import std.conv : emplace; // temporary, as long it is not in std.conv enum ClassSizeOf = __traits(classInstanceSize, T); void[] buf = (cast(void*) obj)[0 .. ClassSizeOf]; return emplace!(T)(buf, args); } T emplaceOrNew(T, U...)(ref T obj, auto ref U args) if (is(T == class)) { if (!obj) return new T(args); return emplace(obj, args); } ----Nice name, fits better. But that should be in phobos, don't you think?meh, it might be nice to have, but not being in phobos isn't a big deal to me for little utilities like this
Apr 25 2015
On Saturday, 25 April 2015 at 19:14:41 UTC, weaselcat wrote:On Saturday, 25 April 2015 at 19:09:15 UTC, Namespace wrote:woops, accidentally posted before finishing I was going to say that this should probably just be an overload of emplace if it isn't already, seems odd if it isn't.On Saturday, 25 April 2015 at 18:57:13 UTC, Adam D. Ruppe wrote:hmm...On Saturday, 25 April 2015 at 18:50:59 UTC, Namespace wrote:I'm sure newcomers (especially those coming from C++) would be delighted if they had the possibility to reuse their memory. In addition, it could be impossible for them to write such "hack" itself. I already hear the cries: "D wasted memory" and: "I can not reuse my memory". And then it would be less attractive for such people, to switch to D. I would suggest something like: ---- T emplace(T, U...)(ref T obj, auto ref U args) if (is(T == class)) { if (!obj) return null; import std.conv : emplace; // temporary, as long it is not in std.conv enum ClassSizeOf = __traits(classInstanceSize, T); void[] buf = (cast(void*) obj)[0 .. ClassSizeOf]; return emplace!(T)(buf, args); } T emplaceOrNew(T, U...)(ref T obj, auto ref U args) if (is(T == class)) { if (!obj) return new T(args); return emplace(obj, args); } ----Nice name, fits better. But that should be in phobos, don't you think?meh, it might be nice to have, but not being in phobos isn't a big deal to me for little utilities like this
Apr 25 2015
hmm...constructs an object of non-class type T at that address.Non-Class. ;)
Apr 25 2015
On Saturday, 25 April 2015 at 19:16:21 UTC, Namespace wrote:There's a class overload 3 down T emplace(T, Args...)(void[] chunk, auto ref Args args) if (is(T == class));hmm...constructs an object of non-class type T at that address.Non-Class. ;)
Apr 25 2015
On Saturday, 25 April 2015 at 19:21:28 UTC, weaselcat wrote:On Saturday, 25 April 2015 at 19:16:21 UTC, Namespace wrote:Which accepts a void[] chunk: ---- Foo f = new Foo(...); emplace!(Foo)((cast(void*) f)[0 .. __traits(classInstanceSize, Foo)], ...); ---- That doesn't look like a "simple and friendly" manner for me. And it is also still not intuitive, not nice and not safe.There's a class overload 3 down T emplace(T, Args...)(void[] chunk, auto ref Args args) if (is(T == class));hmm...constructs an object of non-class type T at that address.Non-Class. ;)
Apr 25 2015
Since I'm unable to rebuild phobos on my Windows 8.1 PC, would someone else be interested, to apply a PR? The code (including comment) would be this: ---- /** Given an existing object $(D obj), reinitialize the object of $(D class) type $(D T) at that address. The constructor is passed the arguments $(D Args), if any. Returns: The reinitialized object */ T emplace(T, Args...)(ref T obj, auto ref Args args) if (is(T == class)) { enforce!ConvException(obj !is null, "emplace: Object is null and cannot be reinitialized"); enum classSize = __traits(classInstanceSize, T); void[] buf = (cast(void*) obj)[0 .. classSize]; return emplace!T(buf, args); } ---- Unfortunately, I do not have much time to deal with the phobos-rebuilding problem, but I still want to improve phobos.
Apr 26 2015
Since nobody wants to take that work, can at least someone explain me what is going on if I don't reinitialize the memorY? I create 1000 Foo's 1000 times. After the first iteration there are 1000 unused Foo objects and the GC wants to reallocate another 1000 Foo's. Now, what happen? The GC sees that the previous 1000 objects are unused, remove them and allocate new and fresh memory for the current 1000 Foo's? Or does the GC see 1000 unused Foo's and reuse the memory? How can I observe if the later is true? If I store the pointer to the object to compare them, the GC will recognize that and will not finalize the object.
Apr 27 2015
On Monday, 27 April 2015 at 11:47:46 UTC, Namespace wrote:I create 1000 Foo's 1000 times. After the first iteration there are 1000 unused Foo objects and the GC wants to reallocate another 1000 Foo's. Now, what happen? The GC sees that the previous 1000 objects are unused, remove them and allocate new and fresh memory for the current 1000 Foo's? Or does the GC see 1000 unused Foo's and reuse the memory?Collected memory will be reused for new allocations. The GC won't necessarily reuse the exact memory of the 1000 old Foos for the 1000 new Foos, but chances are it actually will.How can I observe if the later is true? If I store the pointer to the object to compare them, the GC will recognize that and will not finalize the object.You can store the pointer as a size_t on the GC heap, and the GC will not regard it as a pointer. Alternatively, you can store it on the C heap, which is also ignored by the GC. Using size_t: ---- void main() { import std.stdio; import core.memory: GC; auto pointerInDisguise = new size_t; *pointerInDisguise = cast(size_t) cast(void*) new Object; /* Not sure why stomping is necessary, but without this, the first try below fails. */ static void stomp() {ubyte[1024] x;} stomp(); GC.collect(); writeln(cast(size_t) cast(void*) new Object == *pointerInDisguise); /* prints "true" => memory is reused */ GC.collect(); writeln(cast(size_t) cast(void*) new Object == *pointerInDisguise); /* prints "true" => memory is reused */ } ----
Apr 27 2015
On Monday, 27 April 2015 at 13:12:51 UTC, anonymous wrote:You can store the pointer as a size_t on the GC heap, and the GC will not regard it as a pointer.Doesn't the GC regard every 4/8 sequence of bytes as a pointer?
Apr 27 2015
On Monday, 27 April 2015 at 13:29:03 UTC, Casper Færgemand wrote:Doesn't the GC regard every 4/8 sequence of bytes as a pointer?Only on the stack. On the heap, it knows what type the data is and if it has a pointer or not.
Apr 27 2015
On Monday, 27 April 2015 at 13:12:51 UTC, anonymous wrote:---- void main() { import std.stdio; import core.memory: GC; auto pointerInDisguise = new size_t; *pointerInDisguise = cast(size_t) cast(void*) new Object; /* Not sure why stomping is necessary, but without this, the first try below fails. */Probably issue 9614 - Uninitialized holes in function stack frames confuses GC - https://issues.dlang.org/show_bug.cgi?id=9614static void stomp() {ubyte[1024] x;} stomp(); GC.collect(); writeln(cast(size_t) cast(void*) new Object == *pointerInDisguise); /* prints "true" => memory is reused */ GC.collect(); writeln(cast(size_t) cast(void*) new Object == *pointerInDisguise); /* prints "true" => memory is reused */ } ----
May 01 2015
On Saturday, 25 April 2015 at 18:46:52 UTC, Adam D. Ruppe wrote:You could just call the constructor again to reuse an object. I guess you should also reinitialize the memory, but that's pretty easy too. Instead of placement new to reuse an object, make a function like reset() or reinitialize() that destructs the first, then copies the init back over and calls the constructor again. Something like this: void reinitialize(ClassName, CtorArgs...)(ClassName obj, CtorArgs args) { assert(typeid(obj) == typeid(ClassName), "Don't use this on interfaces or base classes!"); static if(__traits(hasMember, obj, "__dtor")) obj.__dtor(); (cast(void*) obj)[0 .. typeid(obj).init.length] = typeid(obj).init[]; static if(__traits(hasMember, obj, "__ctor")) obj.__ctor(args); } Then, you create it however up front and just reinitialize it when you're all set. I think that is a bit clearer in meaning than placement new as well.doesn't this have the issue of not calling the ctor/dtor of nested objects?
Apr 25 2015
On Saturday, 25 April 2015 at 19:08:52 UTC, weaselcat wrote:doesn't this have the issue of not calling the ctor/dtor of nested objects?You could also use .destroy(copy_of_obj); instead of __dtor which should handle that, I think. Constructors are fine because there's no default construction in D anyway - the object's this() would be calling them anyway.
Apr 25 2015