在对Solaris SPARC盒子上的一些Java代码进行基准测试时,我注意到我第一次调用基准测试函数时,它运行得很慢(10倍差异):
为什么是这样?我怀疑是JIT编译器,有没有办法验证这个?
编辑:根据一些答案,我想澄清一下,这段代码是最简单的测试案例,我可以找到展示这种行为.所以我的目标不是让它快速运行,而是要了解发生了什么,所以我可以在我真正的基准测试中避免它.
解决: Tom Hawtin正确地指出我的"慢"时间实际上是合理的.根据这一观察,我将调试器附加到Java进程.在第一个中,内循环看起来像这样:
0xf9037218: cmp %l0, 100
0xf903721c: bge,pn %icc,0xf90371f4 ! 0xf90371f4
0xf9037220: nop
0xf9037224: ld [%l3 + 92], %l2
0xf9037228: ld [%l2 + 8], %l6
0xf903722c: add %l6, 1, %l5
0xf9037230: st %l5, [%l2 + 8]
0xf9037234: inc %l0
0xf9037238: ld [%l1], %g0
0xf903723c: ba,pt %icc,0xf9037218 ! 0xf9037218
Run Code Online (Sandbox Code Playgroud)
在接下来的迭代中,循环如下所示:
0xf90377d4: sub %l2, %l0, %l3
0xf90377d8: add %l3, %l0, %l2
0xf90377dc: add %l2, …Run Code Online (Sandbox Code Playgroud) 直观地说,后者应该比前者更快.但是,当我看到基准测试结果时,我感到非常惊讶:
require 'benchmark/ips'
b = (0..20).to_a;
y = 21;
Benchmark.ips do |x|
x.report('<<') { a = b.dup; a << y }
x.report('+=') { a = b.dup; a += [y] }
x.report('push') { a = b.dup; a.push(y) }
x.report('[]=') { a = b.dup; a[a.size]=y }
x.compare!
end
Run Code Online (Sandbox Code Playgroud)
结果是:
Calculating -------------------------------------
<< 24.978k i/100ms
+= 30.389k i/100ms
push 24.858k i/100ms
[]= 22.306k i/100ms
-------------------------------------------------
<< 493.125k (± 3.2%) i/s - 2.473M
+= 599.830k (± 2.3%) i/s - 3.009M
push 476.374k (± 3.3%) …Run Code Online (Sandbox Code Playgroud) 我没有看到为什么python的timeit模块使用the来测量时间的原因best of 3.这是我的控制台的一个例子:
~ python -m timeit 'sum(range(10000))'
10000 loops, best of 3: 119 usec per loop
Run Code Online (Sandbox Code Playgroud)
直觉上,我会将整个时间放在一起然后除以循环次数.在所有循环中获得最佳3的直觉是什么?这似乎有点不公平.
我正在研究一个maven项目.我正在尝试将jmh基准测试集成到我的项目中.我的maven项目的pom.xml ......
<parent>
<groupId>platform</groupId>
<artifactId>platform-root</artifactId>
<version>3.0-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<artifactId>platform-migration</artifactId>
<packaging>jar</packaging>
<name>Platform Migration</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compile.source>1.7</maven.compile.source>
<maven.compile.target>1.7</maven.compile.target>
<jmh.version>1.1.1</jmh.version>
<jersey-version>2.22.1</jersey-version>
<uberjar.name>rest-benchmarks</uberjar.name>
</properties>
<dependencies>
<dependency>
<groupId>platform</groupId>
<artifactId>platform-commons</artifactId>
<version>${platform.version}</version>
</dependency>
<dependency>
<groupId>platform</groupId>
<artifactId>platform-persistence</artifactId>
<version>${platform.version}</version>
</dependency>
<dependency>
<groupId>platform</groupId>
<artifactId>platform-testing</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>
<version>${jmh.version}</version>
</dependency>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<version>${jmh.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-client</artifactId>
<version>${jersey-version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>org.openjdk.jmh.Main</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
Run Code Online (Sandbox Code Playgroud)
当我使用"mvn clean …
我注意到使用一个NSDateFormatter可能非常昂贵.我发现分配和初始化对象已经消耗了很多时间.
此外,似乎NSDateFormatter在多线程中使用会增加成本.在线程必须互相等待的情况下是否存在阻塞?
我创建了一个小测试应用程序来说明问题.请检查一下.
这种成本的原因是什么?如何改善使用?
17,12. - 更新我的观察结果:我不理解为什么线程处理时线程运行时间比串行运行时更长.仅在使用NSDateFormatter时才会出现时差.
cocoa multithreading memory-management nsdateformatter microbenchmark
由于缺乏准确性,我总是被一点点跟踪,因为我看到了台架标记system.time和rbenchmark(因为时间的精确度可能不足)并且看到Hadley microbenchmark最近参考了这个包.所以我决定给它一个旋转,如下所示.我mean反对f <- function(x) {sum(x)/length(x)}并期望mean做得更好,f但结果,正如我理解的那样,并不表明这是真的.
我在win 7机器上运行R2.15(因为microbenchmark根据你的操作系统做不同的时间).
结果
Unit: microseconds
expr min lq median uq max
1 f(x) 19.130 20.529 20.529 20.996 286.00
2 mean(x) 28.927 29.860 30.327 30.327 672.31
Run Code Online (Sandbox Code Playgroud)
代码
library(microbenchmark)
x <- 1:10000
f <- function(x) {sum(x)/length(x)}
mean(x)
res <- microbenchmark(
mean(x),
f(x),
times=1000L)
print(res)
boxplot(res)
Run Code Online (Sandbox Code Playgroud) 计算机科学领域的任何人都知道HeapSort O(n log n)在理论上是O(n^2)最糟糕的,而QuickSort是最糟糕的情况.但是,实际上,一个实现良好的QuickSort(具有良好的启发式)将在每个数据集上优于HeapSort.一方面,我们几乎没有观察到最坏的情况,另一方面,例如CPU缓存线,预取等在许多简单的任务中产生巨大的差异.虽然例如QuickSort可以处理预分类数据(具有良好的启发式)O(n),但HeapSort将始终重新组织数据O(n log n),因为它不利用现有结构.
对于我的玩具项目卡尺分析,我最近一直在研究从基准测试结果中估算算法的实际平均复杂度的方法.特别是,我尝试过Lawson和Hanson的NNLS拟合不同的多项式.
但是,它还不能很好地工作.有时候我会得到有用的结果,有时我却没有.我认为只做更大的基准测试可能有所帮助,特别是尝试更多参数.
以下结果用于以具有10%随机性的SAW模式对Double对象进行排序.对于此次运行,n最多只能达到500,因此对于实际使用来说它并不具有代表性...数字是估计的运行时对大小的依赖性.输出是手工编辑和手动排序的,因此它不能反映当前工具提供的内容!
BubbleSortTextbook LINEAR: 67.59 NLOG2N: 1.89 QUADRATIC: 2.51
BubbleSort LINEAR: 54.84 QUADRATIC: 1.68
BidirectionalBubbleSort LINEAR: 52.20 QUADRATIC: 1.36
InsertionSort LINEAR: 17.13 NLOG2N: 2.97 QUADRATIC: 0.86
QuickSortTextbook NLOG2N: 18.15
QuickSortBo3 LINEAR: 59.74 QUADRATIC: 0.12
Java LINEAR: 6.81 NLOG2N: 12.33
DualPivotQuickSortBo5 NLOG2N: 11.28
QuickSortBo5 LINEAR: 3.35 NLOG2N: 9.67
Run Code Online (Sandbox Code Playgroud)
你可以看出,虽然在这个特定的设置中(通常它完全没有令人满意)但结果在很大程度上与已知的行为一致:冒泡排序真的很昂贵,而且QuickSort上的一个好的启发式方法要好得多.但是,例如,具有三个中值启发式的QuickSort最终会进行O(n + n^2)估算,而其他QuickSort估计为O(n + n log n)
那么现在我的实际问题: …
在研究分代垃圾收集器对应用程序性能的微妙后果时,我已经在一个非常基本的操作 - 一个简单的写入堆位置 - 的性能方面遇到了相当惊人的差异 - 关于所写的值是原始值还是引用.
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@BenchmarkMode(Mode.AverageTime)
@Warmup(iterations = 1, time = 1)
@Measurement(iterations = 3, time = 1)
@State(Scope.Thread)
@Threads(1)
@Fork(2)
public class Writing
{
static final int TARGET_SIZE = 1024;
static final int[] primitiveArray = new int[TARGET_SIZE];
static final Object[] referenceArray = new Object[TARGET_SIZE];
int val = 1;
@GenerateMicroBenchmark
public void fillPrimitiveArray() {
final int primitiveValue = val++;
for (int i = 0; i < TARGET_SIZE; i++)
primitiveArray[i] = primitiveValue;
}
@GenerateMicroBenchmark
public void fillReferenceArray() …Run Code Online (Sandbox Code Playgroud) void DoNotOptimize我对Google Benchmark Framework 的功能实现有点困惑(定义来自这里):
template <class Tp>
inline BENCHMARK_ALWAYS_INLINE void DoNotOptimize(Tp const& value) {
asm volatile("" : : "r,m"(value) : "memory");
}
template <class Tp>
inline BENCHMARK_ALWAYS_INLINE void DoNotOptimize(Tp& value) {
#if defined(__clang__)
asm volatile("" : "+r,m"(value) : : "memory");
#else
asm volatile("" : "+m,r"(value) : : "memory");
#endif
}
Run Code Online (Sandbox Code Playgroud)
因此,它具体化了变量,如果变量非常量,也会告诉编译器忘记有关其先前值的任何信息。("+r"是 RMW 操作数)。
并且总是使用"memory"clobber,这是编译器对重新排序加载/存储的障碍,即确保所有全局可访问的对象的内存与 C++ 抽象机同步,并假设它们也可能已被修改。
我距离成为低级代码专家还很远,但据我了解其实现,该函数充当读/写屏障。因此,基本上,它确保传入的值位于寄存器或内存中。
虽然如果我想保留函数的结果(应该进行基准测试),这似乎是完全合理的,但我对编译器留下的自由度感到有点惊讶。
我对给定代码的理解是,编译器可能会在DoNotOptimize调用时插入物化点,这意味着重复执行时(例如,在循环中)会产生大量开销。当不应优化的值只是单个标量值时,如果编译器确保该值驻留在寄存器中似乎就足够了。
区分指针和非指针不是一个好主意吗?例如:
template< class T >
inline __attribute__((always_inline))
void do_not_optimize( …Run Code Online (Sandbox Code Playgroud) c++ assembly inline-assembly microbenchmark google-benchmark
背景:
这是 R 的“微基准测试”包:https :
//cran.r-project.org/web/packages/microbenchmark/index.html
参考手册中的第一行说它是为“精确计时功能”而构建的。
与此有关的一个问题是固有的计算机时间与计算机内存的权衡。一些解决方案是内存密集型的,但 CPU 速度很快。有些是 CPU 密集型的,但内存占用非常小。
问题:
我如何以良好的分辨率同时对基准/微基准进行基准测试/微基准测试,不仅是执行时间,还包括在 R 中执行期间的内存使用?
microbenchmark ×10
benchmarking ×4
java ×3
jmh ×2
performance ×2
r ×2
algorithm ×1
arrays ×1
assembly ×1
c++ ×1
caliper ×1
cocoa ×1
jvm-hotspot ×1
maven ×1
memory ×1
python ×1
ruby ×1
timeit ×1