循环内重复成员测试性能优化求助:Julia代码`t in test_vector`提速方案
代码性能优化问题
请看以下函数:
using Distributions using StatsBase test_vector = sample([1,2,3], 100000000) function test_1(test_vector) rand_vector = randn(3333) sum = 0.0 for t in 1:1000 if t in test_vector sum = sum + rand_vector[t] else sum = sum - rand_vector[t] end end end
使用@profview分析性能后发现,大部分时间消耗在if t in test_vector这一行。请问有没有办法加速这部分代码?曾考虑将test_vector与1:1000拆分后运行两个循环,但这会产生内存分配。能否提供优化思路?
补充说明:需要支持用户传入任意test_vector,示例中使用sample创建test_vector仅为演示用途。
优化思路
核心问题是数组的in操作是线性扫描,每次判断t in test_vector都要遍历整个1亿元素的数组,1000次循环就是1000亿次无效操作,这是性能瓶颈的根源。下面是几种针对性的优化方案:
方案1:用BitVector标记存在的元素
因为循环仅涉及t ∈ 1:1000,可以用一个长度为1000的BitVector(仅占125字节)标记哪些数出现在test_vector中,后续查询直接通过索引访问,时间复杂度O(1):
function test_optimized_bit(test_vector) # 初始化全false的BitVector,标记1-1000中存在的元素 present = falses(1000) for x in test_vector if 1 ≤ x ≤ 1000 present[x] = true end end rand_vector = randn(3333) sum = 0.0 for t in 1:1000 sum += present[t] ? rand_vector[t] : -rand_vector[t] end return sum end
优势:
- 内存占用极小,构建成本低;
- 查询操作是直接索引,比Set更快;
- 仅需遍历一次
test_vector,总操作量为O(m + 1000)(m为test_vector长度),远低于原代码的O(1000*m)。
方案2:用Set存储有效元素
如果t的范围不固定(比如后续可能扩展到更大区间),可以先过滤出test_vector中属于1:1000的元素,存入Set(in操作O(1)):
function test_optimized_set(test_vector) # 过滤出1-1000范围内的元素,转成Set present = Set{Int}(x for x in test_vector if 1 ≤ x ≤ 1000) rand_vector = randn(3333) sum = 0.0 for t in 1:1000 sum += t in present ? rand_vector[t] : -rand_vector[t] end return sum end
优势:
- 适用于
t范围不固定的场景; - 避免存储无关元素,减少Set的内存占用。
关于内存分配的说明
你提到的拆分循环会产生内存分配,但上述方案的内存分配是一次性的,且量级极小:
- BitVector方案仅分配1000位(约125字节);
- Set方案仅存储
test_vector中属于1:1000的元素,对于示例中的test_vector(仅含1、2、3),Set仅存3个元素,内存可以忽略。
这些分配带来的性能提升远大于分配本身的开销,完全可以接受。
内容的提问来源于stack exchange,提问作者user1691278
相关产品推荐
相关产品推荐

