Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does Swift's UnsafeMutablePointer<Float>.allocate(...) actually allocate memory?

I'm trying to understand Swift's unsafe pointer API for the purpose of manipulating audio samples.

The non-mutable pointer variants (UnsafePointer, UnsafeRawPointer, UnsafeBufferPointer) make sense to me, they are all used to reference previously allocated regions of memory on a read-only basis. There is no type method "allocate" for these variants

The mutable variants (UnsafeMutablePointer, UnsafeMutableRawPointer), however, are documented as actually allocating the underlying memory. Example from the documentation for UnsafeMutablePointer (here):

static func allocate(capacity: Int)

Allocates uninitialized memory for the specified number of instances of type Pointee

However, there is no mention that the UnsafeMutablePointer.allocate(size) can fail so it cannot be actually allocating memory. Conversely, if it does allocate actual memory, how can you tell if it failed?

Any insights would be appreciated.

like image 351
Andrew Coad Avatar asked Oct 17 '25 10:10

Andrew Coad


2 Answers

I decided to test this. I ran this program in CodeRunner:

import Foundation

sleep(10)

While the sleep function was executing, CodeRunner reported that this was taking 5.6 MB of RAM on my machine, making our baseline.

I then tried this program:

import Foundation

for _ in 0..<1000000 {
    let ptr = UnsafeMutablePointer<Float>.allocate(capacity: 1)
}

sleep(10)

Now, CodeRunner reports 5.8 MB of RAM usage. A little more than before, but certainly not the extra 4 MB that this should have taken up.

Finally, I assigned something to the pointer:

import Foundation

for _ in 0..<1000000 {
    let ptr = UnsafeMutablePointer<Float>.allocate(capacity: 1)
    ptr.pointee = 0
}

sleep(10)

Suddenly, the program is taking up 21.5 MB of RAM, finally giving us our expected RAM usage increase, although by a larger amount than what I was expecting.

Making a profile in CodeRunner to compile with the optimizations turned on did not seem to make a difference in the behavior I was seeing.

So, surprisingly enough, it does appear that the call to UnsafeMutablePointer.allocate actually does not immediately allocate memory.

like image 199
Charles Srstka Avatar answered Oct 19 '25 08:10

Charles Srstka


Operating systems can cheat a lot when it comes to memory allocations. If you request a block of memory of size N and don't actually put anything in it, the operating system can very well go "sure you can have a block of memory, here you go" and not really do anything with it. It's really more a promise that the memory will be available when used by the program.

Even with a very simple C program like the one below, the macOS's Activity Monitor will report 945 kB first, then 961 kB after calling malloc (which allocates the memory), and finally 257.1 MB after filling the allocated memory with zeroes.

From the point of view of the program, all 256 MB needed for the array of integers is available immediately after calling malloc, but that's actually a lie.

#include <stdlib.h>
#include <stdio.h>

int main(int argc, char const *argv[])
{
    int count = 64*1024*1024;
    printf("Step 1: No memory allocated yet. Check memory usage for baseline, then press enter to continue (1/3)");
    getchar();


    /* Allocate big block of memory */
    int *p = malloc(count*sizeof(int));
    if (p == NULL)  return 1; // failed to allocate
    printf("Step 2: Memory allocated. Check memory usage, then press any key to continue (2/3)");
    getchar();

    /* Fill with zeroes */
    for (int i=0; i < count; i++) {
        p[i] = 0;
    }
    printf("Step 3: Memory filled with zeroes. Check memory usage, then press any key to continue (3/3)");
    getchar();

    return 0;
}
like image 27
Simon Lundberg Avatar answered Oct 19 '25 10:10

Simon Lundberg