Using multiple cores requires explicitly exposing thread-level parallelism to the OS, which usually requires the programmer to write a multi-threaded program. (Or to run a single-threaded program multiple times on different inputs, like compiling with make -j4
)
Compilers for some languages support auto-parallelization, though. For example, C or C++ with OpenMP can compile an ordinary for()
loop into a program that starts multiple threads.
#pragma omp parallel for
for(int i = 0; i < 1000000; ++i)
{
A[i] = B[i] * constant + C[i];
}
But still, this has to happen when you wrote or compiled the program. There is no way for current hardware and OSes to use multiple cores to speed up a single-threaded program.
Related: How does a single thread run on multiple cores?: answer: they don't. But there are other kinds of parallelism, like Instruction-level parallelism that a single CPU core finds and exploits to run a single thread faster than one instruction at a time.
My answer on that question goes into some of the details of how modern CPUs find and exploit fine-grained instruction-level parallelism. (Mostly focusing on x86). That's just part of how normal CPUs work, by having multiple instructions in flight at once, and isn't something you need to enable specially. (There are performance counters that can let you see how many instructions per clock your CPU managed to run while executing a program, though, or other measures.)
Note that RPi3 uses in-order ARM Cortex-A53 CPU cores. Each core is 2-wide superscalar (2 instructions per clock as ILP allows), but can't reorder instructions to find more instruction-level parallelism and hide latency.
Still, the CPU is pipelined, so the total number of instructions in flight (from fetch and decode all the way to the write-back stage at the end of the pipeline) is significant. When data dependencies don't limit things, there can be 2 instructions in each pipeline stage that the CPU is working on, with a throughput of 2 instructions per clock. (That's what 2-wide means.)
It can't execute instructions out of order, but with careful instruction ordering (usually by a compiler) it can still hide the latency of an instruction that takes multiple cycles for its output to be ready. (e.g. a load even if it hits in cache or a multiply will take multiple cycles, vs. an add being ready the next cycle). The trick is to order the asm instructions so there are multiple independent instructions between the one that produces a result and the one that uses it.
Having software (a compiler) statically schedule instructions is more brittle than having hardware that can reorder internally while preserving the illusion of running in program order. It's very hard for compilers to do as good a job as even a small out-of-order window for reordering instructions because cache-misses are unpredictable, and it's hard to analyze dependency chains across function calls at compile time. And the number of registers is limited without hardware register-renaming.
All of this is small comfort when your code runs slower than you'd like. Sure there's a lot of cool stuff under the hood in a Cortex-A53, but there's more cool stuff under the hood in a Cortex-A57 (like out-of-order execution of up to 3 instructions per clock), and even more in a big x86 CPU like Skylake (not to mention the clock-speed differences).
Cortex-A53 is pretty fantastic compared to a https://en.wikipedia.org/wiki/Classic_RISC_pipeline like original MIPS that you'd learn about in computer-architecture class, but by modern standards it's pretty low-end.