www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - How to use D without the GC ?

reply Vinod K Chandran <kcvinu82 gmail.com> writes:
Hi all,
I am planning to write some D code without GC. But I have no 
prior experience with it. I have experience using manual memory 
management languages. But D has so far been used with GC. So I 
want to know what pitfalls it has and what things I should watch 
out for. Also, I want to know what high level features I will be 
missing.
Thanks in advance.
Jun 11
next sibling parent reply matheus <matheus gmail.com> writes:
On Tuesday, 11 June 2024 at 13:00:50 UTC, Vinod K Chandran wrote:
 ...
Similar posts that may help: https://forum.dlang.org/thread/hryadrwplyezihwagiox forum.dlang.org https://forum.dlang.org/thread/dblfikgnzqfmmglwdxdg forum.dlang.org Matheus.
Jun 11
parent Vinod K Chandran <kcvinu82 gmail.com> writes:
On Tuesday, 11 June 2024 at 13:35:19 UTC, matheus wrote:
 On Tuesday, 11 June 2024 at 13:00:50 UTC, Vinod K Chandran 
 wrote:
 ...
Similar posts that may help: https://forum.dlang.org/thread/hryadrwplyezihwagiox forum.dlang.org https://forum.dlang.org/thread/dblfikgnzqfmmglwdxdg forum.dlang.org Matheus.
Thank you Matheus, let me check that. :)
Jun 11
prev sibling next sibling parent reply Kagamin <spam here.lot> writes:
1) arena allocator makes memory manageable with occasional cache 
invalidation problem
2) no hashtable no problem
3) error handling depends on your code complexity, but even in 

exception or you don't
4) I occasionally use CTFE, where ` nogc` is a nuisance
5) polymorphism can be a little quirky
Jun 11
next sibling parent Vinod K Chandran <kcvinu82 gmail.com> writes:
On Tuesday, 11 June 2024 at 14:59:24 UTC, Kagamin wrote:
 1) arena allocator makes memory manageable with occasional 
 cache invalidation problem
 2) no hashtable no problem
 3) error handling depends on your code complexity, but even in 

 an exception or you don't
 4) I occasionally use CTFE, where ` nogc` is a nuisance
 5) polymorphism can be a little quirky
Oh thank you Kagamin. That's some valuable comments. I will take special care.
Jun 11
prev sibling parent drug007 <drug2004 bk.ru> writes:
On 11.06.2024 17:59, Kagamin wrote:
 1) arena allocator makes memory manageable with occasional cache 
 invalidation problem
 2) no hashtable no problem
[OT] could you elaborate what problems they cause?
 3) error handling depends on your code complexity, but even in complex 

 you don't
 4) I occasionally use CTFE, where ` nogc` is a nuisance
 5) polymorphism can be a little quirky
Jun 11
prev sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On Tuesday, 11 June 2024 at 13:00:50 UTC, Vinod K Chandran wrote:
 Hi all,
 I am planning to write some D code without GC. But I have no 
 prior experience with it. I have experience using manual memory 
 management languages. But D has so far been used with GC. So I 
 want to know what pitfalls it has and what things I should 
 watch out for. Also, I want to know what high level features I 
 will be missing.
 Thanks in advance.
I could answer the question directly, but it seems others have already done so. I would instead ask the reason for wanting to write D code without the GC. In many cases, you can write code without *regularly* using the GC (i.e. preallocate, or reuse buffers), but still use the GC in the sense that it is there as your allocator. A great example is exceptions. Something that has the code `throw new Exception(...)` is going to need the GC in order to build that exception. But if your code is written such that this never (normally) happens, then you aren't using the GC for that code. So I would call this kind of style writing code that avoids creating garbage. To me, this is the most productive way to minimize GC usage, while still allowing one to use D as it was intended. -Steve
Jun 11
parent reply Vinod K Chandran <kcvinu82 gmail.com> writes:
On Tuesday, 11 June 2024 at 16:54:44 UTC, Steven Schveighoffer 
wrote:
 I would instead ask the reason for wanting to write D code 
 without the GC.

 -Steve
Hi Steve, Two reasons. 1. I am writting a dll to use in Python. So I am assuming that manual memory management is better for this project. It will give finer control to me. 2. To squeeze out the last bit of performance from D.
Jun 11
next sibling parent reply monkyyy <crazymonkyyy gmail.com> writes:
On Tuesday, 11 June 2024 at 17:15:07 UTC, Vinod K Chandran wrote:
 On Tuesday, 11 June 2024 at 16:54:44 UTC, Steven Schveighoffer 
 wrote:
 I would instead ask the reason for wanting to write D code 
 without the GC.

 -Steve
Hi Steve, Two reasons. 1. I am writting a dll to use in Python. So I am assuming that manual memory management is better for this project. It will give finer control to me. 2. To squeeze out the last bit of performance from D.
rather then worring about the gc, just have 95% of data on the stack
Jun 11
parent reply Vinod K Chandran <kcvinu82 gmail.com> writes:
On Wednesday, 12 June 2024 at 01:35:26 UTC, monkyyy wrote:
 rather then worring about the gc, just have 95% of data on the 
 stack
How's that even possible ? AFAIK, we need heap allocated memory in order to make GUI lib as a DLL. So creating things in heap and modify it, that's the nature of my project.
Jun 12
parent monkyyy <crazymonkyyy gmail.com> writes:
On Wednesday, 12 June 2024 at 16:50:04 UTC, Vinod K Chandran 
wrote:
 On Wednesday, 12 June 2024 at 01:35:26 UTC, monkyyy wrote:
 rather then worring about the gc, just have 95% of data on the 
 stack
How's that even possible ? AFAIK, we need heap allocated memory in order to make GUI lib as a DLL. So creating things in heap and modify it, that's the nature of my project.
gui is a bit harder and maybe aim for 70% but if you went down the rabbit hole you could have strings be in an "arena" of which the first 5000 chars are a global scope array; or full me and just an array that doesnt expand
Jun 12
prev sibling next sibling parent reply DrDread <DrDread cheese.com> writes:
On Tuesday, 11 June 2024 at 17:15:07 UTC, Vinod K Chandran wrote:
 On Tuesday, 11 June 2024 at 16:54:44 UTC, Steven Schveighoffer 
 wrote:
 I would instead ask the reason for wanting to write D code 
 without the GC.

 -Steve
Hi Steve, Two reasons. 1. I am writting a dll to use in Python. So I am assuming that manual memory management is better for this project. It will give finer control to me. 2. To squeeze out the last bit of performance from D.
the GC only runs on allocation. if you want to squeeze out the last bit of performance, you should preallocate all bufferes anyway, and GC vs no GC doesn't matter. also just slap nogc on your main function to avoid accidential allocations.
Jun 12
parent Vinod K Chandran <kcvinu82 gmail.com> writes:
On Wednesday, 12 June 2024 at 09:44:05 UTC, DrDread wrote:
 also just slap  nogc on your main function to avoid accidential 
 allocations.
Thanks for the suggestion. Let me check that idea.
Jun 12
prev sibling next sibling parent reply Sergey <kornburn yandex.ru> writes:
On Tuesday, 11 June 2024 at 17:15:07 UTC, Vinod K Chandran wrote:
 On Tuesday, 11 June 2024 at 16:54:44 UTC, Steven Schveighoffer 
 wrote:

 Two reasons.
 1. I am writting a dll to use in Python. So I am assuming that
Btw are you going to use PyD or doing everything manually from scratch?
Jun 12
parent reply Vinod K Chandran <kcvinu82 gmail.com> writes:
On Wednesday, 12 June 2024 at 10:16:26 UTC, Sergey wrote:
 Btw are you going to use PyD or doing everything manually from 
 scratch?
Does PyD active now ? I didn't tested it. My approach is using "ctypes" library with my dll. Ctypes is the fastes FFI in my experience. I tested Cython, Pybind11 and CFFI. But None can beat the speed of ctypes. Currently the fastest experiments were the dlls created in Odin & C3. Both are non-GC languages.
Jun 12
parent reply evilrat <evilrat666 gmail.com> writes:
On Wednesday, 12 June 2024 at 17:00:14 UTC, Vinod K Chandran 
wrote:
 On Wednesday, 12 June 2024 at 10:16:26 UTC, Sergey wrote:
 Btw are you going to use PyD or doing everything manually from 
 scratch?
Does PyD active now ? I didn't tested it. My approach is using "ctypes" library with my dll. Ctypes is the fastes FFI in my experience. I tested Cython, Pybind11 and CFFI. But None can beat the speed of ctypes. Currently the fastest experiments were the dlls created in Odin & C3. Both are non-GC languages.
It is probably not that well maintained, but it definitely works with python 3.10 and maybe even 3.11, i use it to interface with pytorch and numpy and PIL, but my use case is pretty simple, i just write some wrapper python functions to run inference and pass images back and forth using embedded py_stmts. the only problem is that it seems to leak a lot PydObjects so i have to manually free them, even scope doesn't helps with that which is sad. example classifier python ```python def inference(image: Image): """ Predicts the image class and returns confidences for every class To get the class one can use the following code > conf = inference(image) > index = conf.argmax() > cls = classes[index] """ ch = len(image.getbands()) has_transparency = image.info.get('transparency', None) is not None if ch > 3 or has_transparency: image = image.convert("RGB") image_tensor = prep_transform(image).float() image_tensor = image_tensor.unsqueeze_(0) #if torch.cuda.is_available(): with torch.inference_mode(): output = model(image_tensor) index = output.data.numpy() return index ``` and some of D functions ```d ImageData aiGoesBrrrr(string path, int strength = 50) { try { if (!pymod) py_stmts("import sys; sys.path.append('modules/xyz')"); initOnce!pymod(py_import("xyz.inference")); if (!pymod.hasattr("model")) pymod.model = pymod.method("load_model", "modules/xyz/pre_trained/weights.pth"); PydObject ipath = py(path); scope(exit) destroy(ipath); auto context = new InterpContext(); context.path = ipath; context.py_stmts(" from PIL import Image image = Image.open(path) ch = len(image.getbands()) if ch > 3: image = image.convert('RGB') "); // signature: def run(model, imagepath, alpha=45) -> numpy.Array PydObject output = pymod.method("run", pymod.model, context.image, 100-strength); context.output = output; scope(exit) destroy(output); PydObject shape = output.getattr("shape"); scope(exit) destroy(shape); // int n = ...; int c = shape[2].to_d!int; int w = shape[1].to_d!int; int h = shape[0].to_d!int; // numpy array void* raw_ptr = output.buffer_view().item_ptr([0,0,0]); ubyte* d_ptr = cast(ubyte*) raw_ptr; ubyte[] d_img = d_ptr[0..h*w*c]; return ImageData(d_img.dup, h ,w ,c); } catch (PythonException e) { // oh no... auto context = new InterpContext(); context.trace = new PydObject(e.traceback); context.py_stmts("from traceback import format_tb; trace = format_tb(trace)"); printerr(e.py_message, "\n", context.trace.to_d!string); } return ImageData.init; ```
Jun 12
next sibling parent Ferhat =?UTF-8?B?S3VydHVsbXXFnw==?= <aferust gmail.com> writes:
On Wednesday, 12 June 2024 at 18:58:49 UTC, evilrat wrote:
 On Wednesday, 12 June 2024 at 17:00:14 UTC, Vinod K Chandran 
 wrote:
 [...]
It is probably not that well maintained, but it definitely works with python 3.10 and maybe even 3.11, i use it to interface with pytorch and numpy and PIL, but my use case is pretty simple, i just write some wrapper python functions to run inference and pass images back and forth using embedded py_stmts. the only problem is that it seems to leak a lot PydObjects so i have to manually free them, even scope doesn't helps with that which is sad. [...]
You can use libonnx via importc to do inference of pytorch models after converting them *.onnx. in this way you won't need python at all. Please refer to the etichetta. instead of PIL for preprocessing just use DCV. https://github.com/trikko/etichetta
Jun 12
prev sibling parent Vinod K Chandran <kcvinu82 gmail.com> writes:
On Wednesday, 12 June 2024 at 18:58:49 UTC, evilrat wrote:
 the only problem is that it seems to leak a lot PydObjects so i 
 have to manually free them, even scope doesn't helps with that 
 which is sad.
Oh I see. I did some experiments with nimpy and pybind11. Both experiments were resulted in slower than ctypes dll calling method. That's why I didn't take much interest in binding with Python C API. Even Cython is slower compare to ctypes. But it can be used when we call the dll in Cython and call the cython code from python. But then you will have to face some other obstacles. In my case, callback functions are the reason. When using a dll in cython, you need to pass a cython function as callback and inside that func, you need to convert everything into pyobject back and forth. That will take time. Imagine that you want to do some heavy lifting in a mouse move event ? No one will be happy with at snail's pace. But yeah, Cython is a nice language and we can create an entire gui lib in Cython but the execution speed is 2.5X slower than my current c3 dll.
Jun 12
prev sibling parent reply bachmeier <no spam.net> writes:
On Tuesday, 11 June 2024 at 17:15:07 UTC, Vinod K Chandran wrote:

 Hi Steve,
 Two reasons.
 1. I am writting a dll to use in Python. So I am assuming that 
 manual memory management is better for this project. It will 
 give finer control to me.
 2. To squeeze out the last bit of performance from D.
On the second point, I would be interested in hearing what you find out. In general, I have not had any luck with speeding things up inside loops using manual memory management. The only that's worked is avoiding allocations and reusing already allocated memory. You're splitting things into GC-allocated memory and manually managed memory. There's also SafeRefCounted, which handles the malloc and free for you.
Jun 12
next sibling parent reply bachmeier <no spam.net> writes:
A SafeRefCounted example with main marked  nogc:

```
import std;
import core.stdc.stdlib;

struct Foo {
   double[] data;
   double * ptr;
   alias data this;

    nogc this(int n) {
     ptr = cast(double*) malloc(n*double.sizeof);
     data = ptr[0..n];
     printf("Data has been allocated\n");
   }

    nogc ~this() {
     free(ptr);
     printf("Data has been freed\n");
   }
}

 nogc void main() {
   auto foo = SafeRefCounted!Foo(3);
   foo[0..3] = 1.5;
   printf("%f %f %f\n", foo[0], foo[1], foo[2]);
}
```
Jun 12
next sibling parent Vinod K Chandran <kcvinu82 gmail.com> writes:
On Wednesday, 12 June 2024 at 15:33:39 UTC, bachmeier wrote:
 A SafeRefCounted example with main marked  nogc:
Thanks for the sample. It looks tempting! Let me check that.
Jun 12
prev sibling parent reply Vinod K Chandran <kcvinu82 gmail.com> writes:
On Wednesday, 12 June 2024 at 15:33:39 UTC, bachmeier wrote:
 A SafeRefCounted example with main marked  nogc:

 ```
 import std;
 import core.stdc.stdlib;

 struct Foo {
   double[] data;
   double * ptr;
   alias data this;

    nogc this(int n) {
     ptr = cast(double*) malloc(n*double.sizeof);
     data = ptr[0..n];
     printf("Data has been allocated\n");
   }
  }

 ```
Why not just use `ptr` ? Why did you `data` with `ptr` ?
Jun 12
parent reply bachmeier <no spam.net> writes:
On Wednesday, 12 June 2024 at 18:36:26 UTC, Vinod K Chandran 
wrote:
 On Wednesday, 12 June 2024 at 15:33:39 UTC, bachmeier wrote:
 A SafeRefCounted example with main marked  nogc:

 ```
 import std;
 import core.stdc.stdlib;

 struct Foo {
   double[] data;
   double * ptr;
   alias data this;

    nogc this(int n) {
     ptr = cast(double*) malloc(n*double.sizeof);
     data = ptr[0..n];
     printf("Data has been allocated\n");
   }
  }

 ```
Why not just use `ptr` ? Why did you `data` with `ptr` ?
Try `foo[10] = 1.5` and `foo.ptr[10] = 1.5`. The first correctly throws an out of bounds error. The second gives `Segmentation fault (core dumped)`.
Jun 12
next sibling parent reply Vinod K Chandran <kcvinu82 gmail.com> writes:
On Wednesday, 12 June 2024 at 18:57:41 UTC, bachmeier wrote:
 Try `foo[10] = 1.5` and `foo.ptr[10] = 1.5`. The first 
 correctly throws an out of bounds error. The second gives 
 `Segmentation fault (core dumped)`.
We can use it like this, i think. ``` struct Foo { double * ptr; uint capacity; uint legnth; alias data this; } ``` And then we use an index, we can perform a bound check. I am not sure but I hope this will work.
Jun 12
parent bachmeier <no spam.net> writes:
On Wednesday, 12 June 2024 at 20:31:34 UTC, Vinod K Chandran 
wrote:
 On Wednesday, 12 June 2024 at 18:57:41 UTC, bachmeier wrote:
 Try `foo[10] = 1.5` and `foo.ptr[10] = 1.5`. The first 
 correctly throws an out of bounds error. The second gives 
 `Segmentation fault (core dumped)`.
We can use it like this, i think. ``` struct Foo { double * ptr; uint capacity; uint legnth; alias data this; } ``` And then we use an index, we can perform a bound check. I am not sure but I hope this will work.
Yes, you can do that, but then you're replicating what you get for free by taking a slice. You'd have to write your own opIndex, opSlice, etc., and I don't think there's any performance benefit from doing so.
Jun 12
prev sibling parent reply drug007 <drug2004 bk.ru> writes:
On 12.06.2024 21:57, bachmeier wrote:
 On Wednesday, 12 June 2024 at 18:36:26 UTC, Vinod K Chandran wrote:
 On Wednesday, 12 June 2024 at 15:33:39 UTC, bachmeier wrote:
 A SafeRefCounted example with main marked  nogc:

 ```
 import std;
 import core.stdc.stdlib;

 struct Foo {
   double[] data;
   double * ptr;
   alias data this;

    nogc this(int n) {
     ptr = cast(double*) malloc(n*double.sizeof);
     data = ptr[0..n];
     printf("Data has been allocated\n");
   }
  }

 ```
Why not just use `ptr` ? Why did you `data` with `ptr` ?
Try `foo[10] = 1.5` and `foo.ptr[10] = 1.5`. The first correctly throws an out of bounds error. The second gives `Segmentation fault (core dumped)`.
I think you can use data only because data contains data.ptr
Jun 12
parent reply bachmeier <no spam.net> writes:
On Wednesday, 12 June 2024 at 20:37:36 UTC, drug007 wrote:
 On 12.06.2024 21:57, bachmeier wrote:
 On Wednesday, 12 June 2024 at 18:36:26 UTC, Vinod K Chandran 
 wrote:
 On Wednesday, 12 June 2024 at 15:33:39 UTC, bachmeier wrote:
 A SafeRefCounted example with main marked  nogc:

 ```
 import std;
 import core.stdc.stdlib;

 struct Foo {
   double[] data;
   double * ptr;
   alias data this;

    nogc this(int n) {
     ptr = cast(double*) malloc(n*double.sizeof);
     data = ptr[0..n];
     printf("Data has been allocated\n");
   }
  }

 ```
Why not just use `ptr` ? Why did you `data` with `ptr` ?
Try `foo[10] = 1.5` and `foo.ptr[10] = 1.5`. The first correctly throws an out of bounds error. The second gives `Segmentation fault (core dumped)`.
I think you can use data only because data contains data.ptr
Yes, but you get all the benefits of `double[]` for free if you do it that way, including the more concise foo[10] syntax.
Jun 12
parent reply drug007 <drug2004 bk.ru> writes:
On 12.06.2024 23:56, bachmeier wrote:
 On Wednesday, 12 June 2024 at 20:37:36 UTC, drug007 wrote:
 On 12.06.2024 21:57, bachmeier wrote:
 On Wednesday, 12 June 2024 at 18:36:26 UTC, Vinod K Chandran wrote:
 On Wednesday, 12 June 2024 at 15:33:39 UTC, bachmeier wrote:
 A SafeRefCounted example with main marked  nogc:

 ```
 import std;
 import core.stdc.stdlib;

 struct Foo {
   double[] data;
   double * ptr;
   alias data this;

    nogc this(int n) {
     ptr = cast(double*) malloc(n*double.sizeof);
     data = ptr[0..n];
     printf("Data has been allocated\n");
   }
  }

 ```
Why not just use `ptr` ? Why did you `data` with `ptr` ?
Try `foo[10] = 1.5` and `foo.ptr[10] = 1.5`. The first correctly throws an out of bounds error. The second gives `Segmentation fault (core dumped)`.
I think you can use data only because data contains data.ptr
Yes, but you get all the benefits of `double[]` for free if you do it that way, including the more concise foo[10] syntax.
I meant you do not need to add `ptr` field at all ```D import std; import core.stdc.stdlib; struct Foo { nogc: double[] data; alias data this; this(int n) { auto ptr = cast(double*) malloc(n*double.sizeof); data = ptr[0..n]; } } nogc void main() { auto foo = SafeRefCounted!Foo(3); foo[0..3] = 1.5; printf("%f %f %f\n", foo[0], foo[1], foo[2]); foo.ptr[10] = 1.5; // no need for separate ptr field } ```
Jun 12
parent Lance Bachmeier <no spam.net> writes:
On Wednesday, 12 June 2024 at 21:59:54 UTC, drug007 wrote:

 Yes, but you get all the benefits of `double[]` for free if 
 you do it that way, including the more concise foo[10] syntax.
I meant you do not need to add `ptr` field at all
I see. You're right. I thought it would be easier for someone new to the language to read more explicit code rather than assuming knowledge about data.ptr. In practice it's better to not have a ptr field.
Jun 12
prev sibling next sibling parent Vinod K Chandran <kcvinu82 gmail.com> writes:
On Wednesday, 12 June 2024 at 15:21:22 UTC, bachmeier wrote:
 You're splitting things into GC-allocated memory and manually 
 managed memory. There's also SafeRefCounted, which handles the 
 malloc and free for you.
Thanks, I have read about the possibilities of "using malloc and free from D" in some other post. I think I should need to check that.
Jun 12
prev sibling parent reply Dukc <ajieskola gmail.com> writes:
bachmeier kirjoitti 12.6.2024 klo 18.21:
 You're splitting things into GC-allocated memory and manually managed 
 memory. There's also SafeRefCounted, which handles the malloc and free 
 for you.
I suspect `SafeRefCounted` (or `RefCounted`) is not the best fit for this scenario. The problem with it is it `malloc`s and `free`s individual objects, which doesn't sound efficient to me. Maybe it performs if the objects in question are big enough, or if they can be bundled to static arrays so there's no need to refcount individual objects. But even then, you can't just allocate and free dozens or hundreds of megabytes with one call, unlike with the GC or manual `malloc`/`free`. I honestly don't know if calling malloc/free for, say each 64KiB, would have performance implications over a single allocation.
Jun 12
parent reply Lance Bachmeier <no spam.net> writes:
On Wednesday, 12 June 2024 at 21:36:30 UTC, Dukc wrote:
 bachmeier kirjoitti 12.6.2024 klo 18.21:
 You're splitting things into GC-allocated memory and manually 
 managed memory. There's also SafeRefCounted, which handles the 
 malloc and free for you.
I suspect `SafeRefCounted` (or `RefCounted`) is not the best fit for this scenario. The problem with it is it `malloc`s and `free`s individual objects, which doesn't sound efficient to me. Maybe it performs if the objects in question are big enough, or if they can be bundled to static arrays so there's no need to refcount individual objects. But even then, you can't just allocate and free dozens or hundreds of megabytes with one call, unlike with the GC or manual `malloc`/`free`. I honestly don't know if calling malloc/free for, say each 64KiB, would have performance implications over a single allocation.
Why would it be different from calling malloc and free manually? I guess I'm not understanding, because you put the same calls to malloc and free that you'd otherwise be doing inside this and ~this.
Jun 12
parent reply Dukc <ajieskola gmail.com> writes:
Lance Bachmeier kirjoitti 13.6.2024 klo 1.32:
 
 Why would it be different from calling malloc and free manually? I guess 
 I'm not understanding, because you put the same calls to malloc and free 
 that you'd otherwise be doing inside this and ~this.
Because with `SafeRefCounted`, you have to decide the size of your allocations at compile time, meaning you need to do a varying number of `malloc`s and `free`s to vary the size of your allocation at runtime. Even if you were to use templates to vary the type of `SafeRefCounted` object based on size of your allocation, the spec puts an upper bound of 16MiB to size of a static array. So for example, if you have a program that sometimes needs 600Mib and sometimes needs 1100MiB, you can in any case allocate all that in one go with one `malloc` or one `new`, but you'll need at least 38/59 `SafeRefCounted` static arrays, and therefore `malloc`s, to accomplish the same.
Jun 13
next sibling parent Dukc <ajieskola gmail.com> writes:
Dukc kirjoitti 13.6.2024 klo 10.18:
 So for example, if you have a program that sometimes needs 600Mib and 
 sometimes needs 1100MiB, you can in any case allocate all that in one go 
 with one `malloc` or one `new`, but you'll need at least 38/59 
 `SafeRefCounted` static arrays, and therefore `malloc`s, to accomplish 
 the same.
Now granted, 16MiB (or even smaller amounts, like 256 KiB) sounds big enough that it probably isn't making a difference since it's a long way into multiples of page size anyway. But I'm not sure.
Jun 13
prev sibling parent reply Lance Bachmeier <no spam.net> writes:
On Thursday, 13 June 2024 at 07:18:48 UTC, Dukc wrote:
 Lance Bachmeier kirjoitti 13.6.2024 klo 1.32:
 
 Why would it be different from calling malloc and free 
 manually? I guess I'm not understanding, because you put the 
 same calls to malloc and free that you'd otherwise be doing 
 inside this and ~this.
Because with `SafeRefCounted`, you have to decide the size of your allocations at compile time, meaning you need to do a varying number of `malloc`s and `free`s to vary the size of your allocation at runtime. Even if you were to use templates to vary the type of `SafeRefCounted` object based on size of your allocation, the spec puts an upper bound of 16MiB to size of a static array.
We must be talking about different things. You could, for instance, call a function in a C library to allocate memory at runtime. That function returns a pointer and you pass it to SafeRefCounted to ensure it gets freed. Nothing is known about the allocation at compile time. This is in fact my primary use case - allocating an opaque struct allocated by a C library, and not wanting to concern myself with freeing it when I'm done with it.
Jun 13
parent reply Dukc <ajieskola gmail.com> writes:
Lance Bachmeier kirjoitti 14.6.2024 klo 4.23:
 We must be talking about different things. You could, for instance, call 
 a function in a C library to allocate memory at runtime. That function 
 returns a pointer and you pass it to SafeRefCounted to ensure it gets 
 freed. Nothing is known about the allocation at compile time. This is in 
 fact my primary use case - allocating an opaque struct allocated by a C 
 library, and not wanting to concern myself with freeing it when I'm done 
 with it.
Using a raw pointer as the `SafeRefCounted` type like that isn't going to work. `SafeRefCounted` will free only the pointer itself at the end, not the struct it's referring to. If you use some sort of RAII wrapper for the pointer that `free`s it at it's destructor, then it'll work - maybe that's what you meant.
Jun 14
parent reply bachmeier <no spam.net> writes:
On Friday, 14 June 2024 at 07:52:35 UTC, Dukc wrote:
 Lance Bachmeier kirjoitti 14.6.2024 klo 4.23:
 We must be talking about different things. You could, for 
 instance, call a function in a C library to allocate memory at 
 runtime. That function returns a pointer and you pass it to 
 SafeRefCounted to ensure it gets freed. Nothing is known about 
 the allocation at compile time. This is in fact my primary use 
 case - allocating an opaque struct allocated by a C library, 
 and not wanting to concern myself with freeing it when I'm 
 done with it.
Using a raw pointer as the `SafeRefCounted` type like that isn't going to work. `SafeRefCounted` will free only the pointer itself at the end, not the struct it's referring to. If you use some sort of RAII wrapper for the pointer that `free`s it at it's destructor, then it'll work - maybe that's what you meant.
See the example I posted elsewhere in this thread: https://forum.dlang.org/post/mwerxaolbkuxlgfepzwc forum.dlang.org I defined ``` nogc ~this() { free(ptr); printf("Data has been freed\n"); } ``` and that gets called when the reference count hits zero.
Jun 14
parent Dukc <ajieskola gmail.com> writes:
bachmeier kirjoitti 14.6.2024 klo 16.48:
 See the example I posted elsewhere in this thread: 
 https://forum.dlang.org/post/mwerxaolbkuxlgfepzwc forum.dlang.org
 
 I defined
 
 ```
  nogc ~this() {
    free(ptr);
    printf("Data has been freed\n");
 }
 ```
 
 and that gets called when the reference count hits zero.
Oh sorry, missed that.
Jun 14