当前位置: 首页 > news >正文

照片变年轻在线制作网站门户网站制作

照片变年轻在线制作网站,门户网站制作,网站建设大横幅尺寸,赛盈分销平台官网本章概要 基准测试 微基准测试JMH 的引入 基准测试 我们应该忘掉微小的效率提升#xff0c;说的就是这些 97% 的时间做的事#xff1a;过早的优化是万恶之源。—— Donald Knuth 如果你发现自己正在过早优化的滑坡上#xff0c;你可能浪费了几个月的时间(如果你雄心勃勃的…本章概要 基准测试 微基准测试JMH 的引入 基准测试 我们应该忘掉微小的效率提升说的就是这些 97% 的时间做的事过早的优化是万恶之源。—— Donald Knuth 如果你发现自己正在过早优化的滑坡上你可能浪费了几个月的时间(如果你雄心勃勃的话)。通常一个简单直接的编码方法就足够好了。如果你进行了不必要的优化就会使你的代码变得无谓的复杂和难以理解。 基准测试意味着对代码或算法片段进行计时看哪个跑得更快与下一节的分析和优化截然相反分析优化是观察整个程序找到程序中最耗时的部分。 可以简单地对一个代码片段的执行计时吗在像 C 这样直接的编程语言中这个方法的确可行。在像 Java 这样拥有复杂的运行时系统的编程语言中基准测试变得更有挑战性。为了生成可靠的数据环境设置必须控制诸如 CPU 频率节能特性其他运行在相同机器上的进程优化器选项等等。 微基准测试 写一个计时工具类从而比较不同代码块的执行速度是具有吸引力的。看上去这会产生一些有用的数据。比如这里有一个简单的 Timer 类可以用以下两种方式使用它 创建一个 Timer 对象执行一些操作然后调用 Timer 的 duration() 方法产生以毫秒为单位的运行时间。向静态的 duration() 方法中传入 Runnable。任何符合 Runnable 接口的类都有一个函数式方法 run()该方法没有入参且没有返回。 import static java.util.concurrent.TimeUnit.*;public class Timer {private long start System.nanoTime();public long duration() {return NANOSECONDS.toMillis(System.nanoTime() - start);}public static long duration(Runnable test) {Timer timer new Timer();test.run();return timer.duration();} }这是一个很直接的计时方式。难道我们不能只运行一些代码然后看它的运行时长吗 有许多因素会影响你的结果即使是生成提示符也会造成计时的混乱。这里举一个看上去天真的例子它使用了 标准的 Java Arrays 库后面会详细介绍 import java.util.*;public class BadMicroBenchmark {static final int SIZE 250_000_000;public static void main(String[] args) {try { // For machines with insufficient memorylong[] la new long[SIZE];System.out.println(setAll: Timer.duration(() - Arrays.setAll(la, n - n)));System.out.println(parallelSetAll: Timer.duration(() - Arrays.parallelSetAll(la, n - n)));} catch (OutOfMemoryError e) {System.out.println(Insufficient memory);System.exit(0);}} }main() 方法的主体包含在 try 语句块中因为一台机器用光内存后会导致构建停止。 对于一个长度为 250,000,000 的 long 型仅仅差一点就会让大部分机器内存溢出数组我们比较了 Arrays.setAll() 和 Arrays.parallelSetAll() 的性能。这个并行的版本会尝试使用多个处理器加快完成任务尽管我在这一节谈到了一些并行的概念但是在 并发编程 章节我们才会详细讨论这些 。然而非并行的版本似乎运行得更快尽管在不同的机器上结果可能不同。 BadMicroBenchmark.java 中的每一步操作都是独立的但是如果你的操作依赖于同一资源那么并行版本运行的速度会骤降因为不同的进程会竞争相同的那个资源。 import java.util.*;public class BadMicroBenchmark2 {static final int SIZE 5_000_000;public static void main(String[] args) {long[] la new long[SIZE];Random r new Random();System.out.println(parallelSetAll: Timer.duration(() - Arrays.parallelSetAll(la, n - r.nextLong())));System.out.println(setAll: Timer.duration(() - Arrays.setAll(la, n - r.nextLong())));SplittableRandom sr new SplittableRandom();System.out.println(parallelSetAll: Timer.duration(() - Arrays.parallelSetAll(la, n - sr.nextLong())));System.out.println(setAll: Timer.duration(() - Arrays.setAll(la, n - sr.nextLong())));} }SplittableRandom 是为并行算法设计的它当然看起来比普通的 Random 在 parallelSetAll() 中运行得更快。 但是看上去还是比非并发的 setAll() 运行时间更长有点难以置信也许是真的但我们不能通过一个坏的微基准测试得到这个结论。 这只考虑了微基准测试的问题。Java 虚拟机 Hotspot 也非常影响性能。如果你在测试前没有通过运行代码给 JVM 预热那么你就会得到“冷”的结果不能反映出代码在 JVM 预热之后的运行速度假如你运行的应用没有在预热的 JVM 上运行你就可能得不到所预期的性能甚至可能减缓速度。 优化器有时可以检测出你创建了没有使用的东西或者是部分代码的运行结果对程序没有影响。如果它优化掉你的测试那么你可能得到不好的结果。 一个良好的微基准测试系统能自动地弥补像这样的问题和很多其他的问题从而产生合理的结果但是创建这么一套系统是非常棘手需要深入的知识。 JMH 的引入 截止目前为止唯一能产生像样结果的 Java 微基准测试系统就是 Java Microbenchmarking Harness简称 JMH。本书的 build.gradle 自动引入了 JMH 的设置所以你可以轻松地使用它。 你可以在命令行编写 JMH 代码并运行它但是推荐的方式是让 JMH 系统为你运行测试build.gradle 文件已经配置成只需要一条命令就能运行 JMH 测试。 JMH 尝试使基准测试变得尽可能简单。例如我们将使用 JMH 重新编写 BadMicroBenchmark.java。这里只有 **State ** 和 **Benchmark ** 这两个注解是必要的。其余的注解要么是为了产生更多易懂的输出要么是加快基准测试的运行速度JMH 基准测试通常需要运行很长时间 // validating/jmh/JMH1.java package validating.jmh; import java.util.*; import org.openjdk.jmh.annotations.*; import java.util.concurrent.TimeUnit;State(Scope.Thread) BenchmarkMode(Mode.AverageTime) OutputTimeUnit(TimeUnit.MICROSECONDS) // Increase these three for more accuracy: Warmup(iterations 5) Measurement(iterations 5) Fork(1) public class JMH1 {private long[] la;Setuppublic void setup() {la new long[250_000_000];}Benchmarkpublic void setAll() {Arrays.setAll(la, n - n);}public void parallelSetAll() {Arrays.parallelSetAll(la, n - n);} }“forks” 的默认值是 10意味着每个测试都运行 10 次。为了减少运行时间这里使用了 **Fork ** 注解来减少这个次数到 1。我还使用了 **Warmup ** 和 **Measurement ** 注解将它们默认的运行次数从 20 减少到 5 次。尽管这降低了整体的准确率但是结果几乎与使用默认值相同。可以尝试将 **Warmup 、Measurement ** 和 **Fork ** 都注释掉然后看使用它们的默认值结果会有多大显著的差异一般来说你应该只能看到长期运行的测试使错误因素减少而结果没有多大变化。 需要使用显式的 gradle 命令才能运行基准测试在示例代码的根目录处运行。这能防止耗时的基准测试运行其他的 gradlew 命令 gradlew validating:jmh 这会花费几分钟的时间取决于你的机器(如果没有注解上的调整可能需要几个小时)。控制台会显示 results.txt 文件的路径这个文件统计了运行结果。注意results.txt 包含这一章所有 jmh 测试的结果JMH1.javaJMH2.java 和 JMH3.java。 因为输出是绝对时间所以在不同的机器和操作系统上结果各不相同。重要的因素不是绝对时间我们真正观察的是一个算法和另一个算法的比较尤其是哪一个运行得更快快多少。如果你在自己的机器上运行测试你将看到不同的结果却有着相同的模式。 我在大量的机器上运行了这些测试尽管不同的机器上得到的绝对值结果不同但是相对值保持着合理的稳定性。我只列出了 results.txt 中适当的片段并加以编辑使输出更加易懂而且内容大小适合页面。所有测试中的 Mode 都以 avgt 展示代表 “平均时长”。Cnt测试的数目的值是 200尽管这里的一个例子中配置的 Cnt 值是 5。Units 是 us/op是 “Microseconds per operation” 的缩写因此这个值越小代表性能越高。 我同样也展示了使用 warmups、measurements 和 forks 默认值的输出。我删除了示例中相应的注解就是为了获取更加准确的测试结果这将花费数小时。结果中数字的模式应该仍然看起来相同不论你如何运行测试。 下面是 JMH1.java 的运行结果 Benchmark Score JMH1.setAll 196280.2 JMH1.parallelSetAll 195412.9 即使像 JMH 这么高级的基准测试工具基准测试的过程也不容易练习时需要倍加小心。这里测试产生了反直觉的结果并行的版本 parallelSetAll() 花费了与非并行版本的 setAll() 相同的时间两者似乎都运行了相当长的时间。 当创建这个示例时我假设如果我们要测试数组初始化的话那么使用非常大的数组是有意义的。所以我选择了尽可能大的数组如果你实验的话会发现一旦数组的大小超过 2亿5000万你就开始会得到内存溢出的异常。然而在这么大的数组上执行大量的操作从而震荡内存系统产生无法预料的结果是有可能的。不管这个假设是否正确看上去我们正在测试的并非是我们想测试的内容。 考虑其他的因素 C客户端执行操作的线程数量 P并行算法使用的并行数量 N数组的大小**10^(2_k)_通常来说k1…7 足够来练习不同的缓存占用。 Qsetter 的操作成本 这个 C/P/N/Q 模型在早期 JDK 8 的 Lambda 开发期间浮出水面大多数并行的 Stream 操作(parallelSetAll() 也基本相似)都满足这些结论**N_Q_(主要工作量)对于并发性能尤为重要。并行算法在工作量较少时可能实际运行得更慢。 在一些情况下操作竞争如此激烈使得并行毫无帮助而不管 **N_Q_ 有多大。当 C 很大时P 就变得不太相关内部并行在大量的外部并行面前显得多余。此外在一些情况下并行分解会让相同的 C 个客户端运行得比它们顺序运行代码更慢。 基于这些信息我们重新运行测试并在这些测试中使用不同大小的数组改变 N // validating/jmh/JMH2.java package validating.jmh; import java.util.*; import org.openjdk.jmh.annotations.*; import java.util.concurrent.TimeUnit;State(Scope.Thread) BenchmarkMode(Mode.AverageTime) OutputTimeUnit(TimeUnit.MICROSECONDS) Warmup(iterations 5) Measurement(iterations 5) Fork(1) public class JMH2 {private long[] la;Param({1,10,100,1000,10000,100000,1000000,10000000,100000000,250000000})int size;Setuppublic void setup() {la new long[size];}Benchmarkpublic void setAll() {Arrays.setAll(la, n - n);}Benchmarkpublic void parallelSetAll() {Arrays.parallelSetAll(la, n - n);} }**Param ** 会自动地将其自身的值注入到变量中。其自身的值必须是字符串类型并可以转化为适当的类型在这个例子中是 int 类型。 下面是已经编辑过的结果包含精确计算出的加速数值 JMH2 BenchmarkSizeScore %SpeedupsetAll10.001parallelSetAll10.0360.028setAll100.005parallelSetAll103.9650.001setAll1000.031parallelSetAll1003.1450.010setAll10000.302parallelSetAll10003.2850.092setAll100003.152parallelSetAll100009.6690.326setAll10000034.971parallelSetAll10000020.1531.735setAll1000000420.581parallelSetAll1000000165.3882.543setAll100000008160.054parallelSetAll100000007610.1901.072setAll10000000079128.752parallelSetAll10000000076734.6711.031setAll250000000199552.121parallelSetAll250000000191791.9271.040可以看到当数组大小达到 10 万左右时parallelSetAll() 开始反超而后趋于与非并行的运行速度相同。即使它运行速度上胜了看起来也不足以证明由于并行的存在而使速度变快。 setAll()/parallelSetAll() 中工作的计算量起很大影响吗在前面的例子中我们所做的只有对数组的赋值操作这可能是最简单的任务。所以即使 N 值变大**N_Q_ 也仍然没有达到巨大所以看起来像是我们没有为并行提供足够的机会JMH 提供了一种模拟变量 Q 的途径如果想了解更多的话可搜索 Blackhole.consumeCPU。 我们通过使方法 f() 中的任务变得更加复杂从而产生更多的并行机会 // validating/jmh/JMH3.java package validating.jmh; import java.util.*; import org.openjdk.jmh.annotations.*; import java.util.concurrent.TimeUnit;State(Scope.Thread) BenchmarkMode(Mode.AverageTime) OutputTimeUnit(TimeUnit.MICROSECONDS) Warmup(iterations 5) Measurement(iterations 5) Fork(1) public class JMH3 {private long[] la;Param({1,10,100,1000,10000,100000,1000000,10000000,100000000,250000000})int size;Setuppublic void setup() {la new long[size];}public static long f(long x) {long quadratic 42 * x * x 19 * x 47;return Long.divideUnsigned(quadratic, x 1);}Benchmarkpublic void setAll() {Arrays.setAll(la, n - f(n));}Benchmarkpublic void parallelSetAll() {Arrays.parallelSetAll(la, n - f(n));} }f() 方法提供了更加复杂且耗时的操作。现在除了简单的给数组赋值外setAll() 和 parallelSetAll() 都有更多的工作去做这肯定会影响结果。 JMH2 BenchmarkSizeScore %SpeedupsetAll10.012parallelSetAll10.0470.255setAll100.107parallelSetAll103.8940.027setAll1000.990parallelSetAll1003.7080.267setAll1000133.814parallelSetAll100011.74711.391setAll1000097.954parallelSetAll1000037.2592.629setAll100000988.475parallelSetAll100000276.2643.578setAll10000009203.103parallelSetAll10000002826.9743.255setAll1000000092144.951parallelSetAll1000000028126.2023.276setAll100000000921701.863parallelSetAll100000000266750.5433.455setAll2500000002299127.273parallelSetAll250000000538173.4254.272 可以看到当数组的大小达到 1000 左右时parallelSetAll() 的运行速度反超了 setAll()。看来 parallelSetAll() 严重依赖数组中计算的复杂度。这正是基准测试的价值所在因为我们已经得到了关于 setAll() 和 parallelSetAll() 间微妙的信息知道在何时使用它们。 这显然不是从阅读 Javadocs 就能得到的。 大多数时候JMH 的简单应用会产生好的结果正如你将在本书后面例子中所见但是我们从这里知道你不能一直假定 JMH 会产生好的结果。 JMH 网站上的范例可以帮助你开始。
http://www.zqtcl.cn/news/391808/

相关文章:

  • 海外网站seo优化wordpress的代码逻辑
  • 怎样帮别人做网站哪有网站给光头强做面
  • 聊城营销网站建设价格网站设计论文框架
  • 成都哪家网站建设做得好介绍自己的家乡遵义网站建设
  • 阳春新农村建设网站欣赏网站
  • 永久免费企业网站建设杭州个人做网站
  • 博罗中山网站建设做网站的软件 知乎
  • 广州网站开发广州亦客网络解答wordpress换空间要改
  • 丽水企业网站开发企业erp系统是什么软件
  • 好看的网站设计个人发布信息的免费平台
  • 电商网站业务流程linux上传中文wordpress
  • 广州网站定制商家外贸seo网站推广
  • 许昌大成建设集团网站wordpress自动博客插件
  • wordpress网站地图插件中国来料加工网
  • 黑龙江做网站的公司上海企业网站建设公
  • 做公众号时图片的网站安徽建设工程造价信息网站
  • 网站开发的在淘宝上是什么类目深圳做网站的大公司
  • 手机网站 html5信阳哪里做网站
  • 网站服务器多少钱一月wordpress 博客宠物
  • 怎么制作网站游戏辽宁建设工程网
  • 网站开发好还要空间吗网站支付链接怎么做的
  • 网站制作报价图片欣赏杭州做网站价格
  • 帮人家做家务的网站host绑定网站
  • 地方门户网站盈利模式这样做微信网站
  • 企业网站要怎么做wordpress w3
  • 网站备案帐号找回密码seo优化工作有哪些
  • 美橙网站建设教程网站建站系统
  • 湖北网站建设公司哪家好重庆建站模板平台
  • 青岛企业建站最新上线的手游
  • 织梦网站wap精品下载