序言:我正在做一个恢复truecrypt容器的项目。它以最可能的随机顺序被切割成超过 3M 的小文件,目标是找到包含加密密钥的容器的开头或结尾。
为此,我编写了一个小型 ruby 脚本,该脚本启动许多 truecrypt 进程,同时尝试挂载主文件或恢复备份文件头。通过派生的 PTY 与 truecrypt 进行交互:
PTY.spawn(@cmd) do |stdout, stdin, pid|
@spawn = {stdout: stdout, stdin: stdin, pid: pid}
if test_type == :forward
process_truecrypt_forward
else
process_truecrypt_backward
end
stdin.puts
pty_expect('Incorrect password')
Process.kill('INT', pid)
stdin.close
stdout.close
Process.wait(pid)
end
这一切都很好,并成功地找到了测试容器所需的部分。为了加快速度(我需要处理超过 3M 块),我首先使用了 Ruby MRI 多线程,并在阅读了有关它的问题后切换到concurent-ruby。
我的实现非常简单:
log 'Starting DB test'
concurrent_db = Concurrent::Array.new(@db)
futures = []
progress_bar = initialize_progress_bar('Running DB test', concurrent_db.size)
MAXIMUM_FUTURES.times do
log "Started new future, total #{futures.size} futures"
futures << Concurrent::Future.execute do
my_piece = nil
run = 1
until concurrent_db.empty?
my_piece = concurrent_db.slice!(0, SLICE_PER_FUTURE)
break unless my_piece
log "Run #{run}, sliced #{my_piece.size} pieces, #{concurrent_db.size} left"
my_piece.each {|a| run_single_test(a)}
progress_bar.progress += my_piece.size
run += 1
end
log 'Future finished'
end
end
然后我租用了一个具有 74 个 CPU 内核的大型 AWS 实例,然后想:“现在我要快速处理它”。但问题是,无论我同时启动多少个期货/线程(我的意思是 20 或 1000 个),我都不会超过每秒 50 次检查。
当我启动 1000 个线程时,CPU 负载仅在 20-30 分钟内保持 100%,然后下降,直到达到 15% 并保持不变。此类运行中的典型 CPU 负载图。磁盘负载不是问题,我使用 Amazon EBS 存储时最高达到 3MiB/s。
我错过了什么?为什么我不能利用 100% cpu 并获得更好的性能?