There are a few common issues: 1) You measure compilation plus execution instead of just execution. 2) Your code relies on global values that limit optimizations. 3) Your code doesn’t allow the compiler to determine all the types. 4) Your code forces allocations for intermediate values.
Especially 4) makes a big difference when you’re coming from NumPy or MATLAB. If you have a chain of vectorized operations (e.g. “X = α * A + β * B + γ * C”), the performance is quite similar between the languages, but in Julia you can enforce that these happen in-place with a single fused loop and zero allocations (e.g. adding “@.” in front). This can often give you another ~2x–10x speedup and make the difference between “similar to NumPy/MATLAB” and “similar to C/Fortran”.