volk icon indicating copy to clipboard operation
volk copied to clipboard

Memory leak in volk_load_preferences

Open dernasherbrezon opened this issue 3 years ago • 10 comments

Looks like preferences are loaded on the first access, but never destroyed. Can't find any helped method to properly cleanup.

Got this while running valgrind:

==29410== 11,904 bytes in 1 blocks are still reachable in loss record 1 of 1
==29410==    at 0x4849F4C: realloc (vg_replace_malloc.c:785)
==29410==    by 0x497CD5F: volk_load_preferences (in /usr/local/lib/libvolk.so.2.0)

dernasherbrezon avatar Jan 17 '21 22:01 dernasherbrezon

This is not a memory leak to worry about and is only shown by Valgrind if explicitly instructed to do so. volk_rank_archs() which calls volk_load_preferences() makes sure that the memory is allocated only once:


    if (!prefs_loaded) {
        n_arch_prefs = volk_load_preferences(&volk_arch_prefs);
        prefs_loaded = 1;
    }

The memory will be freed by the operating system when the application using volk exists. It would be a “real” memory leak – “Definitely lost” in Valgrind – if the if guard wasn’t in place (because then a memory allocation would be performed every time a kernel is called).

This “leak” cannot be fixed unless a kind of deinit_volk() functions is added (and explicitly called).

See [1] for the different leak types in Valgrind (search for “Still reachable”).

[1] https://www.valgrind.org/docs/manual/mc-manual.html#mc-manual.leaks

rear1019 avatar Jan 18 '21 18:01 rear1019

I thought "memory leak" is a memory that leaked from the control and cannot be freed. Yes, something like deinit_volk() might be required for this. Or even better: explicit lifecycle management init_volk and deinit_volk.

That would bring several benefits:

  • make application performance predictable. It won't load and parse file on a critical path.
  • fail-fast if volk is unable to load preferences or if preferences corrupted &etc.

dernasherbrezon avatar Jan 18 '21 19:01 dernasherbrezon

Currently, you can just add you volk kernel calls to your code and the first time such a function is called, init_volk is run. With explicit calls to init_volk and in the end deinit_volk, we'd add some user complexity. e.g. when an where should GNU Radio call this functions? We can discuss how to add an additional interface to handle this explicitly on demand but I'd like the default to be as is. Currently, if preferences are not available VOLK defaults to a "use the latest SIMD version available". i.e. prefer AVX over SSE and AVX512 over AVX2, etc.

If VOLK could spit out a warning if it couldn't find a preferences file that would be beneficial.

In terms of predictable performance: The whole init code is called on the very first VOLK kernel call. At that stage, your application is unpredictable in terms of performance because we have no control over CPU internals. Only after a couple of iterations we can start to rely on performance metrics. So, we add a bit more unpredictability into a situation that is already unpredictable.

I know it's less than ideal and we might want to think of a more accessible way but: https://github.com/gnuradio/volk/blob/cb87a41101fc0a754298919c610402512ea3299d/lib/volk_rank_archs.c#L56-L62

I guess we can move this such that we have reliable access to this data structure. And then continue from there. Do you have any suggestions on what to change?

jdemel avatar Jan 19 '21 10:01 jdemel

we'd add some user complexity

Agree. This should be carefully introduced. Just wanted to highlight this weird behaviour.

when an where should GNU Radio call this functions?

I'm not writing GNU radio-related application :) But I would assume any application has some kind of lifecycle:

  • initialise, allocate memory, start
  • do some useful work
  • de-initialize, free memory, stop

When the code provided as a library it is hard to control how it is going to be used. For example, volk can be used only once briefly and then never executed during application lifecycle.

dernasherbrezon avatar Jan 25 '21 00:01 dernasherbrezon

So yeah. It would be great to modify VOLK such that we can optionally call init and deinit. This should be optional though. Do you have a suggestion how we could achieve this?

Regarding the "volk can be used only once briefly". I agree that this might happen. But it is not the intended use case. VOLK is optimized towards speed for operations that happen frequently.

jdemel avatar Jan 25 '21 08:01 jdemel

I thought "memory leak" is a memory that leaked from the control and cannot be freed.

The pointer to the allocated memory is not lost at any time point. If we wanted to, we could release it. However, there is no point doing do so because the memory is used throughout the lifetime of an application. See the definition of “Still reachable” in Valgrind’s documentation [1].

  • make application performance predictable. It won't load and parse file on a critical path.

The file is loaded only once. If this is really an issue – I doubt it is in any DSP heavy application – call any kernel once before starting main data processing. If the “unpredictability” of the dispatchers is an issue – once again, I doubt it is – call every used kernel once. Or don’t use unpredictable systems (GNU Radio :wink:) in the first place.

It would be great to modify VOLK such that we can optionally call init and deinit. This should be optional though.

What’s the point if it’s optional and doesn’t provide any benefit? The reported “leak” is not a problem. Even though I suggested how it could be fixed, I don’t think (and never considered) we should. The increased complexity and burden for users of volk is not worth it.

[1] https://www.valgrind.org/docs/manual/mc-manual.html#mc-manual.leaks

rear1019 avatar Jan 25 '21 18:01 rear1019

The file is loaded only once. [...] Or don’t use unpredictable systems (GNU Radio wink) in the first place.

Are you sure? What about multi-threaded applications. Like GNU Radio. I only had a brief look, but I didn't find any locks.

bastibl avatar Jan 26 '21 06:01 bastibl

What’s the point if it’s optional and doesn’t provide any benefit? The reported “leak” is not a problem. Even though I suggested how it could be fixed, I don’t think (and never considered) we should. The increased complexity and burden for users of volk is not worth it.

I agree that we should not require any of this. I just imagined a mechanism where a user may call init and deinit. If a user does not, the automatic route is preferred and I'd argue the automatic mechanism is the preferred one. Mostly because:

If this is really an issue – I doubt it is in any DSP heavy application – call any kernel once before starting main data processing.

jdemel avatar Jan 27 '21 08:01 jdemel

The file is loaded only once. [...] Or don’t use unpredictable systems (GNU Radio wink) in the first place.

Are you sure? What about multi-threaded applications. Like GNU Radio. I only had a brief look, but I didn't find any locks.

There are no locks. https://github.com/gnuradio/volk/blob/cb87a41101fc0a754298919c610402512ea3299d/lib/volk_rank_archs.c#L55-L62

And the called function doesn't have one either

https://github.com/gnuradio/volk/blob/cb87a41101fc0a754298919c610402512ea3299d/lib/volk_prefs.c#L66-L85

So far I haven't heard of any issues with this mechanism. And to conclude this:

https://github.com/gnuradio/volk/blob/cb87a41101fc0a754298919c610402512ea3299d/tmpl/volk.tmpl.c#L144-L160

Here, we call volk_rank_archs.

jdemel avatar Jan 27 '21 09:01 jdemel

If we provide a deinit function, we should also spend some thought on how to make initialization thread safe. Unless, we decide that this is a user problem and VOLK does not make any guarantees to be thread safe. Though, I'd prefer to make sure initialization is thread-safe.

I assume a viable solution would rely on something with C atomic and C threads. This needs further investigation though.

jdemel avatar Jan 29 '21 17:01 jdemel