Memory usage of compiled function: C vs WVM

18

4

Reported to WRI [CASE:3406803]


Why does generating an array in "C"-compiled function uses 50% more memory than doing same thing in "WVM"-compiled function?

Does this depend on OS or used C compiler?

Simple example:

ClearAll[zeroArrayWVM, zeroArrayC]
zeroArrayWVM =
    Compile[{{i, _Integer}}, Table[0, {i}], CompilationTarget -> "WVM"];
zeroArrayC =
    Compile[{{i, _Integer}}, Table[0, {i}], CompilationTarget -> "C"];

TableForm[
    Table[
        {#1, #2, N[#2/#1]} &[
            zeroArrayWVM[i] // MaxMemoryUsed, 
            zeroArrayC[i] // MaxMemoryUsed
        ],
        {i, 10^Range[8]}
    ], 
    TableHeadings -> {None, {"WVM", "C", "C/WVM"}}
]

\begin{array}{lll} \text{WVM} & \text{C} & \text{C/WVM} \\ 320 & 512 & 1.6 \\ 1824 & 2768 & 1.51754 \\ 16544 & 24848 & 1.50193 \\ 160240 & 240392 & 1.5002 \\ 1600240 & 2400392 & 1.50002 \\ 16000240 & 24000392 & 1.5 \\ 160000240 & 240000392 & 1.5 \\ 1600000240 & 2400000392 & 1.5 \\ \end{array}

This results were obtained on Linux x86_64 with gcc 4.8.3, if that is relevant. They are the same in Mathematica versions 9.0, 10.0, 10.1 and 10.2.

jkuczm

Posted 2015-08-23T14:58:27.420

Reputation: 14 388

2The actual storage requirement for those tables is 8 bytes per element, so it looks like there is one additional copy present during evaluation of the C compiled function. My guess would be that the extra copy is the one created in the underlying LibraryFunction which must be copied to the kernel before it can be deleted. – Simon Woods – 2015-08-23T17:26:51.413

@SimonWoods thanks, do you think there's anything we can do to avoid those extra copies? – jkuczm – 2015-08-23T18:04:58.957

2

Player 3 has joined the game: Creating a C-library from a compiled function. Here is the comparison and you can see the whole notebook by evaluating Import["http://goo.gl/NaH6rM"]["http://i.stack.imgur.com/GTpLl.png"] If we only could trust that MaxMemoryUsed is working correctly..

– halirutan – 2015-08-23T19:59:48.670

@halirutan My system monitor agrees with MaxMemoryUsed, but I don't know why memory was not freed after using zeroArrayCGen. I had $HistoryLength = 0; and memory was automatically freed after zeroArrayWVM and zeroArrayC. Do library functions use some additional caching?

– jkuczm – 2015-08-23T22:41:55.563

@jkuczm I recommend self-answering this question, i.e. move the second section to an answer. – Mr.Wizard – 2017-01-20T08:44:24.857

Answers

9

As suggested in comment of Simon Woods and comment of halirutan, returning an array from "WVM"-CompiledFunction and from LibraryFunction creates one additional copy of result, while returning from "C"-CompiledFunction creates two additional copies of result.

We can test it by comparing total memory usage of calling functions generating array of zeros with size of this array. We create "C"-CompiledFunction: zeroArrayC, directly. We get "WVM"-CompiledFunction: zeroArrayWVM, by taking all elements of "C"-CompiledFunction except last one. We get LibraryFunction: zeroArrayCLib by taking last element of "C"-CompiledFunction.

zeroArrayC = Compile[{{i, _Integer}}, Table[0, {i}], CompilationTarget -> "C"];
zeroArrayWVM = Most@zeroArrayC;
zeroArrayCLib = Last@zeroArrayC;

TableForm[
    Table[
        Prepend[
            N[(MaxMemoryUsed@#@i& /@ {zeroArrayWVM, zeroArrayC, zeroArrayCLib})/#],
            #
        ]&@ByteCount@ConstantArray[0, i]
        ,
        {i, 2^(5 Range@5)}
    ],
    TableHeadings -> {
        None,
        {"ByteCount", "WVM/ByteCount", "C/ByteCount", "CLib/ByteCount"}
    }
 ]

\begin{array}{rlll} \text{ByteCount} & \text{WVM/ByteCount} & \text{C/ByteCount} & \text{CLib/ByteCount} \\ 360 & 1.86667 & 2.88889 & 0.844444 \\ 8304 & 2.23892 & 3.36224 & 2.20713 \\ 262288 & 1.99982 & 2.99985 & 1.96465 \\ 8388752 & 1.99999 & 3. & 1.96874 \\ 268435600 & 2. & 3. & 1.96875 \\ \end{array}

If array is created while evaluating function, but is not returned, then no additional copies of array are created:

zeroArrayFirstC = Compile[{{i, _Integer}}, First@Table[0, {i}], CompilationTarget -> "C"];
zeroArrayFirstWVM = Most@zeroArrayFirstC;
zeroArrayFirstCLib = Last@zeroArrayFirstC;

TableForm[
    Table[
        Prepend[
            N[(MaxMemoryUsed@#@i & /@ {zeroArrayFirstWVM, zeroArrayFirstC, zeroArrayFirstCLib})/#],
            #
        ]&@ByteCount@ConstantArray[0, i]
        ,
        {i, 2^(5 Range@5)}
    ],
    TableHeadings -> {
        None,
        {"ByteCount", "WVM/ByteCount", "C/ByteCount", "CLib/ByteCount"}
    }
 ]

\begin{array}{rlll} \text{ByteCount} & \text{WVM/ByteCount} & \text{C/ByteCount} & \text{CLib/ByteCount} \\ 360 & 0.866667 & 1.15556 & 0.844444 \\ 8304 & 1.11657 & 1.12909 & 1.11561 \\ 262288 & 0.999817 & 1.00021 & 0.999786 \\ 8388752 & 0.999994 & 1.00001 & 0.999993 \\ 268435600 & 1. & 1. & 1. \\ \end{array}

jkuczm

Posted 2015-08-23T14:58:27.420

Reputation: 14 388