首先,除非在程序中创建了多个线程,否则该程序中只有一个执行线程。
看到 25% 的 CPU 资源用于该程序,这表明四个内核中的一个内核正在以 100% 的速度使用,但所有其他内核都没有被使用。如果使用了所有内核,那么理论上该进程可能会占用 100% 的 CPU 资源。
附带说明一下,Windows 任务管理器中显示的图表是当时运行的所有进程的 CPU 利用率,而不仅仅是一个进程。
其次,您提供的代码可以拆分为可以在两个单独的线程上执行的代码,以便在两个内核上执行。我猜你想证明这一点a
并且b
彼此独立,它们只依赖于i
. 在这种情况下,for
像下面这样分离循环内部可以允许多线程操作,从而提高性能:
// Process this in one thread:
for (int i = 0; i < 1000; i++) {
a = i * 2;
}
// Process this in another thread:
for (int i = 0; i < 1000; i++) {
b = i + 1;
}
但是,变得棘手的是,是否需要评估来自两个单独线程的结果的时间,例如if
后面的语句似乎暗示的那样:
for (i = 0; i < 1000; i++) {
// manipulate "a" and "b"
if (a == ... || b == ...) { ... }
}
这将需要查找位于不同线程(在不同处理器上执行)中的a
and值,这是一个非常令人头疼的问题。b
没有真正好的保证i
两个线程的值同时相同(毕竟,乘法和加法可能会花费不同的时间来执行),这意味着一个线程可能需要等待另一个线程在比较和对应于从属值i
之前使值同步。或者,我们是否要创建第三个线程来进行两个线程的值比较和同步?无论哪种情况,复杂性都开始迅速增加,所以我认为我们可以同意我们开始看到出现严重的混乱——线程之间共享状态可能非常棘手。a
b
i
因此,您提供的代码示例只是部分可并行化而不需要太多努力,但是,一旦需要比较两个变量,分离这两个操作很快就会变得非常困难。
并发编程的几个经验法则:
当有些任务可以分解为涉及完全独立于其他数据及其结果(状态)的数据处理的部分时,并行化可以非常容易。
例如,从输入计算值的两个函数(在伪代码中):
f(x) = { return 2x }
g(x) = { return x+1 }
这两个函数不相互依赖,因此它们可以并行执行而没有任何痛苦。此外,由于它们不是在计算之间共享或处理的状态,即使x
需要计算多个值,即使这些值也可以进一步拆分:
x = [1, 2, 3, 4]
foreach t in x:
runInThread(f(t))
foreach t in x:
runInThread(g(t))
现在,在这个例子中,我们可以有 8 个独立的线程来执行计算。对于并发编程来说,没有副作用可能是一件好事。
但是,一旦依赖于其他计算的数据和结果(这也意味着存在副作用),并行化就变得极其困难。在许多情况下,这些类型的问题必须连续执行,因为它们等待返回其他计算的结果。
也许问题归结为,为什么编译器不能找出可以自动并行化并执行这些优化的部分?我不是编译器方面的专家,所以我不能说,但维基百科上有一篇关于自动并行化的文章可能有一些信息。