Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit cd7e104

Browse files
[feat16](截止 微基准测试)
1 parent 5a884f8 commit cd7e104

1 file changed

Lines changed: 95 additions & 1 deletion

File tree

‎docs/book/16-Validating-Your-Code.md‎

Lines changed: 95 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1469,14 +1469,108 @@ SimpleDebugging.main(SimpleDebugging.java:20)
14691469

14701470
## 基准测试
14711471

1472-
> 我们应该忘掉微小的效率,说的就是这些 97% 的时间:过早的优化是万恶之源。
1472+
> 我们应该忘掉微小的效率提升,说的就是这些 97% 的时间做的事:过早的优化是万恶之源。
14731473
>
14741474
> ​ —— Donald Knuth
14751475

14761476
如果你发现自己正在过早优化的滑坡上,你可能浪费了几个月的时间(如果你雄心勃勃的话)。通常,一个简单直接的编码方法就足够好了。如果你进行了不必要的优化,就会使你的代码变得无谓的复杂和难以理解。
14771477

14781478
基准测试意味着对代码或算法片段进行计时看哪个跑得更快,与下一节的分析和优化截然相反,分析优化是观察整个程序,找到程序中最耗时的部分。
14791479

1480+
可以简单地对一个代码片段的执行计时吗?在像 C 这样直接的编程语言中,这个方法的确可行。在像 Java 这样拥有复杂的运行时系统的编程语言中,基准测试变得更有挑战性。为了生成可靠的数据,环境设置必须控制诸如 CPU 频率,节能特性,其他运行在相同机器上的进程,优化器选项等等。
1481+
1482+
### 微基准测试
1483+
1484+
写一个计时工具类从而比较不同代码块的执行速度是具有吸引力的。看上去这会产生一些有用的数据。比如,这里有一个简单的 **Timer** 类,可以用以下两种方式使用它:
1485+
1486+
1. 创建一个 **Timer** 对象,执行一些操作然后调用 **Timer****duration()** 方法产生以毫秒为单位的运行时间。
1487+
2. 向静态的 **duration()** 方法中传入 **Runnable**。任何符合 **Runnable** 接口的类都有一个函数式方法 **run()**,该方法没有入参,且没有返回。
1488+
1489+
```java
1490+
// onjava/Timer.java
1491+
package onjava;
1492+
import static java.util.concurrent.TimeUnit.*;
1493+
1494+
public class Timer {
1495+
private long start = System.nanoTime();
1496+
1497+
public long duration() {
1498+
return NANOSECONDS.toMillis(System.nanoTime() - start);
1499+
}
1500+
1501+
public static long duration(Runnable test) {
1502+
Timer timer = new Timer();
1503+
test.run();
1504+
return timer.duration();
1505+
}
1506+
}
1507+
```
1508+
1509+
这是一个很直接的计时方式。难道我们不能只运行一些代码然后看它的运行时长吗?
1510+
1511+
有许多因素会影响你的结果,即使是生成提示符也会造成计时的混乱。这里举一个看上去天真的例子,它使用了 标准的 Java **Arrays** 库(后面会详细介绍):
1512+
1513+
```java
1514+
// validating/BadMicroBenchmark.java
1515+
// {ExcludeFromTravisCI}
1516+
import java.util.*;
1517+
import onjava.Timer;
1518+
1519+
public class BadMicroBenchmark {
1520+
static final int SIZE = 250_000_000;
1521+
1522+
public static void main(String[] args) {
1523+
try { // For machines with insufficient memory
1524+
long[] la = new long[SIZE];
1525+
System.out.println("setAll: " + Timer.duration(() -> Arrays.setAll(la, n -> n)));
1526+
System.out.println("parallelSetAll: " + Timer.duration(() -> Arrays.parallelSetAll(la, n -> n)));
1527+
} catch (OutOfMemoryError e) {
1528+
System.out.println("Insufficient memory");
1529+
System.exit(0);
1530+
}
1531+
}
1532+
1533+
}
1534+
/* Output
1535+
setAll: 272
1536+
parallelSetAll: 301
1537+
```
1538+
1539+
**main()** 方法的主体包含在 **try** 语句块中,因为一台机器用光内存后会导致构建停止。
1540+
1541+
对于一个长度为 250,000,000 的 **long** 型(仅仅差一点就会让大部分机器内存溢出)数组,我们比较了 **Arrays.setAll()** 和 **Arrays.parallelSetAll()** 的性能。这个并行的版本会尝试使用多个处理器加快完成任务(尽管我在这一节谈到了一些并行的概念,但是在 [并发编程](./24-Concurrent-Programming.md) 章节我们才会详细讨论这些 )。然而非并行的版本似乎运行得更快,尽管在不同的机器上结果可能不同。
1542+
1543+
**BadMicroBenchmark.java** 中的每一步操作都是独立的,但是如果你的操作依赖于同一资源,那么并行版本运行的速度会骤降,因为不同的进程会竞争相同的那个资源。
1544+
1545+
```java
1546+
// validating/BadMicroBenchmark2.java
1547+
// Relying on a common resource
1548+
1549+
import java.util.*;
1550+
import onjava.Timer;
1551+
1552+
public class BadMicroBenchmark2 {
1553+
static final int SIZE = 5_000_000;
1554+
1555+
public static void main(String[] args) {
1556+
long[] la = new long[SIZE];
1557+
Random r = new Random();
1558+
System.out.println("parallelSetAll: " + Timer.duration(() -> Arrays.parallelSetAll(la, n -> r.nextLong())));
1559+
System.out.println("setAll: " + Timer.duration(() -> Arrays.setAll(la, n -> r.nextLong())));
1560+
SplittableRandom sr = new SplittableRandom();
1561+
System.out.println("parallelSetAll: " + Timer.duration(() -> Arrays.parallelSetAll(la, n -> sr.nextLong())));
1562+
System.out.println("setAll: " + Timer.duration(() -> Arrays.setAll(la, n -> sr.nextLong())));
1563+
}
1564+
}
1565+
/* Output
1566+
parallelSetAll: 1147
1567+
setAll: 174
1568+
parallelSetAll: 86
1569+
setAll: 39
1570+
```
1571+
1572+
1573+
14801574
<!-- Profiling and Optimizing -->
14811575
14821576
## 分析和优化

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /