# What about an \`undefs\` function in \`Base\`?

**URL:** <https://discourse.julialang.org/t/what-about-an-undefs-function-in-base/92690>\
**Category:** General Usage\
**Tags:** feature-request\
**Created:** [January 9, 2023, 2:18am UTC](https://discourse.julialang.org/t/what-about-an-undefs-function-in-base/92690 "2023-01-09T02:18:50Z")\
**Posts on this page:** 1\
**Showing post:** 51

<div class="post-metadata">

**Author:** ![Sukera](https://avatars.discourse-cdn.com/v4/letter/s/ce7236/32.png) [@Sukera](https://discourse.julialang.org/u/Sukera)\
**Post date:** [January 15, 2023, 12:27pm UTC](https://discourse.julialang.org/t/what-about-an-undefs-function-in-base/92690/51 "2023-01-15T12:27:18Z")

</div>

> [@mkitti](#):
>
> As Carsten Bauer mentioned, there is a better solution than `undefs` for the novice user where they can obtain nearly the same performance. This is [`calloc`](https://en.cppreference.com/w/c/memory/calloc), a standard C function. On operating systems, there also more specialized methods in the same vein.

“Better” is a very subjective term here, as has already been explained in the very thread you linked. There is more to the story there than “just” dropping in `calloc` as a replacement to `malloc` and be done with it.

> [@mkitti](#):
>
> I have since packaged this into ArrayAllocators.jl. Let me offer a brief demonstration.

This benchmark is misleading because it _does not zero the memory until `sum` is called_. Please don’t sweep the big caveat of using `calloc` under the rug and present it as a pure win in performance - it just isn’t. I’ve already benchmarked this in the other thread extensively.

> [@DNF](#):
>
> Would that work for all kinds of data types? `zeros` really only makes sense for numerical data. What about allocations of unitialized data in general?

No - this only “works” for types where a zero bitpattern also happens to coincide with the `zero` of that type, as I mentioned in the linked thread:

> [@Faster zeros with calloc](https://discourse.julialang.org/t/faster-zeros-with-calloc/69860/13):
>
> In fact, for user defined types `zeros` _has to use `malloc` with initialization_ because a zero-filled block of memory may not be valid for `zero` of a user defined type! As you note, it may be boxed or a number of other things, like not being `isbits` or simply not having a block of zeros as its `zero` representation.

In which case, using `calloc` by default means the memory is initialized twice, once by the kernel when it gives you the memory that’s filled with 0, and once when julia has to inevitably call `zero` to initialize the data properly. This **will** lead to users being confused about why `zeros` is slower than it needs to be and should thus be avoided.

> [@mkitti](#):
>
> The knowledge I’m using above is:
> 
> 1. The underlying memory representation of `Vector{Vector{Int}}` is an array of pointers.
> 2. `#undef` actually means those pointers are `C_NULL` (in other words, `0`)
> 3. When set, the pointers point to a `jl_array` structure which has 48 bytes of meta data before the array contents.

All of these are internal implementation details you cannot rely on. Do not assume these to be true.

* * *

The interface for allocation I would prefer to all options presented here looks like this:

```julia
"""
Allocate a single object of type `T`, using memory managed by `Allocator`.
Return a `T`, throws an OutOfMemory exception when it fails to allocate memory.
"""
allocate(::Allocator, ::T)

"""
Allocate `n` objects of type `T`, using memory managed by `Allocator`.
Return a collection of `T`, throws an OutOfMemory exception when it fails to allocate memory.
"""
allocate(::Allocator, ::T, n)

```

This is how Zig [does it](https://ziglearn.org/chapter-2/), and for good reason - you don’t need more. [Rust](https://doc.rust-lang.org/alloc/alloc/index.html) has some more fanciness for deallocation. Both of these can be used in the compiler to hoist allocations to the stack if need be. The second one semantically returns an `Array` (or a fixed-size equivalent) when passed a default GC.

> [@jar1](#):
>
> In this API there’s no room to say where the memory comes from, we just pull it out of the sky. If I have a block of memory, I might want to split it up and make arrays and dictionaries out of it, e.g. using `allocate!(block, Array, Float64, (2,2))`.

That’s because “splitting the memory up” to create other objects out of is not a safe operation - if you allocate a large array and use that as the “backing memory” for multiple objects, you’re well on the way of reimplementing a memory manager yourself. For the regular julia GC, it would have to keep that big chunk around as long as even a single other object (that may use only a tiny portion of that memory) is still accessible. You really need the allocator passed in the API to handle that part of the operation for you.

---

_[View the full topic](https://discourse.julialang.org/t/what-about-an-undefs-function-in-base/92690)._
