Assuming that your kernel is somewhat smooth, use low-rank approximation.
Here's a naive example:
import numpy as np
A = np.exp(1j*2*np.pi*X*Y)
output = np.dot(A, input)
U,S,V = np.linalg.svd(A)
# find truncation rank for given tolerance
k = ...
Effectively, what you are asking, is how to take advantage of a multicore architecture without parallelizing code yourself. There are no ideal solutions for that, most likely, you will have to parallelize the code yourself manually; otherwise, you are bound to use a single core.
Nevertheless, there are a couple of things one can take advantage of:
If your ...
IPS and IPC are generally specified "per core". That's because processor makers often vary how many cores of a particular kind they pack on the same processor, so it doesn't really make sense to specify these per-processor, whereas the core is always the same in those cases -- the type of core is generally described by the "generation" of the processor.
Nothing stops you from decomposing the problem up yourself and feeding the relevant partitioned data into MKL sequentially, or even in parallel. It will work as long as you avoid data races, but you may experience performance penalties unless you are very careful about how you do it.
The reason it's discouraged to combine OpenMP code with MKL is that OpenMP ...
I am by no means an expert but I will share some challenges that I have heard of. One challenge is (and this may depend on how you chose your c-points) in order to avoid extensive communication between nodes you want your matrix to have a nice structure. For example lets say you are solving a finite element problem. Then you want your nodes to be ordered in ...
Beyond hwloc there are a few tools that can report on a HPC cluster's memory environment and which can be used to set a variety of NUMA configurations.
I would recommend LIKWID as one such tool as it avoids a code based approach allowing you for instance to pin a process to a core. This approach of tooling to address machine specific memory configuration ...
Matrix-vector multiplication is RAM-bound. You make two arithmetic operations for each float that you read. So the major cost is going to be storing the whole matrix in slow, non-cached core memory and reading it back.
You should consider evaluating your "complicated kernel" on the fly instead of saving it to memory:
output = np.zeros(n)
for j in range(n):