You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何带JIT的矩阵余弦相似度计算比循环向量计算慢?

余弦相似度计算效率优化问题分析

我正在优化余弦相似度的计算效率,最初是把向量存入矩阵后逐行调用计算;后来改成移除内层循环,直接对整个矩阵做计算——无JIT时处理时间确实减少了,但带JIT的向量循环计算速度还是更快。给矩阵计算函数加上JIT后,处理时间反而大幅飙升,效率变成了最低。

我曾怀疑问题出在matrix ** 2,试过改用np.linalg.norm,但当前Numba版本不支持axis参数。我已经找到一种高效的Numba矩阵余弦计算实现,但还是想弄明白我的代码为什么会出现这种反向优化的问题。


实验代码

模块导入

import numpy as np
from numba import jit
from timeit import default_timer as timer

计算函数

# @jit(nopython=True)
def cosine_sim_matrix(matrix, vector):
    dot_product = np.dot(matrix, vector)
    magnitude_a = np.sqrt((matrix ** 2).sum(axis=1))
    magnitude_b = np.sqrt((vector ** 2).sum())
    similarity_score = dot_product / (magnitude_a * magnitude_b)
    similarity_score[similarity_score > 1] = 1
    similarity_score[similarity_score < -1] = -1
    return similarity_score

# @jit(nopython=True)
def cosine_sim_vector(vector_1, vector_2):
    dot_product = np.dot(vector_1, vector_2)
    magnitude_a = np.sqrt((vector_1** 2).sum())
    magnitude_b = np.sqrt((vector_2 ** 2).sum())
    similarity_score = dot_product / (magnitude_a * magnitude_b)
    if similarity_score > 1:
        similarity_score = 1.0
    elif similarity_score < -1:
        similarity_score = -1.0 
    return similarity_score

测试代码

mymatrix = np.random.rand(10000,1000)

# 计时矩阵计算
start = timer()
for idx in range(mymatrix.shape[0]):
    vec = mymatrix[idx,:]
    cosine_sim_matrix(mymatrix, vec)
end = timer()
print(f"process time, matrix={end-start}")

# 计时向量计算
start = timer()
for idx in range(mymatrix.shape[0]):
    vec = mymatrix[idx,:]
    for idx2 in range(mymatrix.shape[0]):
        vec2 = mymatrix[idx2,:]
        cosine_sim_vector(vec, vec2)
end = timer()
print(f"process time, vector={end-start}")

实验结果

# 无JIT
process time, matrix=348.5313815
process time, vector=839.2151422999999

# 带JIT
process time, matrix=883.4681065999998
process time, vector=275.0485957000001

补充:高效实现的测试结果

process time, no parallel, matrix=109.96504489999916
process time, w/ parallel, matrix=21.85965199999964 

问题原因分析

  • Numba对Numpy高阶操作的优化局限性:cosine_sim_matrix依赖大量Numpy广播(matrix ** 2)和按轴聚合(sum(axis=1))操作,Numba的JIT编译器无法像优化纯循环代码那样生成高效机器码,反而会因兼容Numpy API语义引入额外开销,比如临时内存对象创建、不必要的数组拷贝,抵消了原本的性能优势。
  • 向量循环的JIT优化更彻底:cosine_sim_vector是纯向量级计算,逻辑简单,Numba能直接将其编译为高度优化的机器码,没有Numpy高阶操作的负担;同时循环逻辑在JIT下可充分优化(如寄存器重用、循环展开),所以带JIT的向量循环反而更快。
  • 无JIT时矩阵计算的优势来自Numpy底层:未加JIT时,cosine_sim_matrix的Numpy操作调用的是底层优化过的C实现,比纯Python循环的向量计算快,但加上JIT后,Numba未能有效优化这些Numpy操作,反而导致性能下降。

内容的提问来源于stack exchange,提问作者linkey apiacess

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 18:44:56