Octave中pararrayfun/parcellfun疑似Bug及线程并行替代方案咨询
Octave parcellfun with Handle Objects: Process-Based Parallelism Gotchas & Workarounds
1. Is this behavior a bug?
Quick answer: No, this is a design limitation rather than a bug—but it’s a major gotcha that’s under-documented for users who expect handle objects’ reference semantics to carry over to parallel operations.
Here’s the breakdown:
- Octave’s
parcellfun(and its cousinpararrayfun) use process-based parallelism (forking child processes) instead of threads. When a child process is created, it gets a copy of the parent’s entire memory space (via copy-on-write, or COW). - While handle objects are passed by reference within a single process, each child process gets its own isolated copy of the handle and its underlying data. Any changes you make to the handle’s
.dataproperty in a child process only affect that child’s local copy—these updates never make their way back to the parent process’s original object. - The lack of a warning about this limitation is definitely an oversight, but the core behavior is consistent with how process-based parallelism works in Unix-like systems (which Octave relies on for this functionality).
2. Are there thread-based parallel alternatives?
This depends on which Octave version you’re using:
For Octave 4.2.2 (your version)
Octave 4.2.2 has very limited native thread support for parallel operations—there’s no direct thread-based drop-in replacement for parcellfun here. Your best options are:
- Stick with single-threaded
cellfunif the performance impact is manageable. - Refactor your code to avoid handle objects in parallel: Instead of passing handles to child processes, split your input data into chunks, process each chunk in parallel with
parcellfun, then collect the results and merge them back into your handle object in the parent process. This works becauseparcellfundoes return output values from child processes to the parent.
Here’s a refactored version of your code to demonstrate this approach:
classdef data_class < handle properties data end end % Modified to return processed chunks instead of modifying handles directly function chunk = fill_arr(start_idx, num_elems, in_data) chunk = zeros(num_elems, size(in_data, 2)); for i=1:num_elems chunk(i,:) = fancy_func(in_data(start_idx+i-1,:)); end end elem_per_process=3; num_processes=4; start_indexes={1,4,7,10}; input_arr = data_class(); input_arr.data = rand(12, 5); % Example input data arr = data_class(); arr.data = zeros(12,5); % Process chunks in parallel filler = @(start_idx) fill_arr(start_idx, elem_per_process, input_arr.data); chunks = parcellfun(num_processes, filler, start_indexes, "UniformOutput", false); % Merge results back into the handle object in the parent process for idx = 1:length(start_indexes) start_pos = start_indexes{idx}; arr.data(start_pos:start_pos+elem_per_process-1,:) = chunks{idx}; end
For newer Octave versions (5.0+)
Later Octave versions introduced much better thread support. You have two solid options here:
pararrayfunwith the"threads"backend: If you can adapt your code to usepararrayfuninstead ofparcellfun, specifying the thread-based backend will let you modify handle objects directly—since threads share the same memory space as the parent process, changes to the handle’s properties will propagate as expected:pararrayfun("threads", num_processes, filler, start_indexes);thread_poolclass: For more control over thread-based tasks, you can create a thread pool and submit work manually. This gives you full access to handle objects across threads, as they all share the parent’s memory space.
内容的提问来源于stack exchange,提问作者Ash
相关产品推荐
相关产品推荐

