Launching cuda streams from the different threads would help a little (remember, you cannot fruitfully do it from python because of GIL, so you’d have to do it in a cpp extension), but the effect will still be very limited, because some of the work necessary to launch the kernels will be serialized by the driver.
The general ballpark is still that having a single kernel is much more efficient than launching kernels to the streams (that’s how e.g. all batched matrix multiplies are implemented, launching single matrix multiplies to the different streams does not even come close to the performance of a single kernel). Once the work performed by a single stream is large enough to fully occupy the GPU, there’s also no benefit from streams because they are essentially serialized, the next one will start only after the first one completes and frees the GPU.
I’d vote for Alban’s suggestion, if you can write your function to be applied to a 1D tensor that’s the way to go.