I used LinearAlgebra.dot for small to medium sized vectors but discovered recently that it is much slower for short vectors than an simple for loop. The following code
using Printf
using LinearAlgebra
using BenchmarkTools
function mydot(x, y)
s = zero(eltype(x))
axes(x) === axes(y) || error("axes must match")
@inbounds @simd for i in eachindex(x, y)
s += x[i]*y[i]
end
return s
end
lens = [3, 11, 21, 51, 501, 2501]
mtdot = similar(lens, Float64)
mtmydot = similar(lens, Float64)
for i in eachindex(lens)
global mt
l = lens[i]
x = randn(l)
y = randn(l)
#x = @view(randn(l+2)[begin+1:end-1])
#y = @view(randn(l+2)[begin+1:end-1])
#x = SVector{length(x)}(x)
#y = SVector{length(x)}(y)
bm = @benchmark dot($x, $y)
mtdot[i] = minimum(bm.times)
bm = @benchmark mydot($x, $y)
mtmydot[i] = minimum(bm.times)
end
println("length dot/ns mydot/ns")
for i in eachindex(lens)
@printf("%4d %1.2f ns %1.2f ns\n", lens[i], mtdot[i]/lens[i], mtmydot[i]/lens[i])
end
So for small, βgeometricalβ vectors dot is nearly three times slower than the loop. It needs more than 50 bins until they run equally fast. The picture is the same if I use views (see code in comments). If I use static vectors both are equally fast. So is it recommended to use dot only for long vectors? Actually it would be nice to have a function that is equally fast for all sizes.
My versionfo() output is:
Julia Version 1.12.7
Commit 6d172b025e (2026-08-15 08:05 UTC)
Build Info:
Official https://julialang.org release
Platform Info:
OS: Windows (x86_64-w64-mingw32)
CPU: 16 Γ AMD Ryzen AI 7 PRO 350 w/ Radeon 860M
WORD_SIZE: 64
LLVM: libLLVM-18.1.7 (ORCJIT, znver5)
GC: Built with stock GC
Threads: 7 default, 1 interactive, 7 GC (on 16 virtual cores)
Environment:
JULIA_NUM_THREADS = 7,1
JULIA_EDITOR = code
JULIA_VSCODE_REPL = 1
LinearAlgebra.dot uses BLAS. This is a library of foreign functions. This means that there is more call overhead than something native. The overhead is small in absolute terms but can be significant in relative terms.
There is a small, constant overhead in the regular dot function. If you look at how the function is implemented, itβs because it dispatches to a ccall, which basically means thereβs a layer of indirection here.
This could be fixed by making a julia native version of this function. Iβm actually rather surprised we use BLAS for this, I thought usually we only used BLAS for things like matmul.
Edit: lol @mikmoore beat me to it by a few seconds. Sorry for the double post
Small followup, but perhaps this makes more sense @mwlidar when you account for the fact that BLAS will start multithreading these calls beyond a certain length cutoff, so a performance gap opens up again:
If you are dealing with βgeometricalβ vectors, typically 2- and 3-component vectors, where the dimensionality is effectively known ahead of time (statically), then you are usually much better off using StaticArrays.jl.
Not only will operations like dot be much faster than loop-based code (the loop is completely unrolled to 3 multiplications and 2 additions, and no branches or indexing), but many other operations will be faster, and you donβt need to worry about allocating temporary vectors so the code becomes much cleaner (e.g. you can just do things like vec1 = 2vec2 + vec3 * (vec4'vec5) in the most natural way, without messing around with pre-allocation and in-place operations like you would for larger arrays).