这里是“高效”的代码(对不起,如果我有一点语法错误,我现在不在计算机上使用代码)
long startNum = 0;
long stopNum = 1000000000L;
for (long i = startNum; i <= stopNum; i++){
if (i % 50000 == 0) {
System.out.println(i);
}
}
这里是“低效代码”
long startNum = 0;
long stopNum = 1000000000L;
long progressCheck = 50000;
for (long i = startNum; i <= stopNum; i++){
if (i % progressCheck == 0) {
System.out.println(i);
}
}
请注意,我有一个日期变量来测量差异,当它变得足够长时,第一个用了50ms,而另一个用了12秒或类似的时间。如果您的PC比我的效率高,您可能必须增加stopnum
或减少progresscheck
。
我在网上找这个问题,但我找不到答案,也许我只是问错了。
编辑:我没想到我的问题这么受欢迎,我很感激所有的答案。我确实在每一半的时间上执行了一个基准测试,而低效的代码花费了相当长的时间,1/4秒相对于10秒。当然,他们使用的是println,但他们都做了同样的量,所以我不认为这会有太大的偏差,特别是因为差异是可重复的。至于答案,因为我是Java的新手,所以我现在将让投票来决定哪个答案是最好的。我会试着在星期三之前挑一个。
Edit2:今晚我要做另一个测试,它不是modulus,而是增加一个变量,当它达到progressCheck时,它将执行一个,然后将该变量重置为0。第三种选择。
编辑3.5:
我使用了这个代码,下面我将显示我的结果…谢谢大家的帮助!我还尝试将长的短值与0进行比较,所以我的所有新检查都发生“65536”次,使它在重复中相等。
public class Main {
public static void main(String[] args) {
long startNum = 0;
long stopNum = 1000000000L;
long progressCheck = 65536;
final long finalProgressCheck = 50000;
long date;
// using a fixed value
date = System.currentTimeMillis();
for (long i = startNum; i <= stopNum; i++) {
if (i % 65536 == 0) {
System.out.println(i);
}
}
long final1 = System.currentTimeMillis() - date;
date = System.currentTimeMillis();
//using a variable
for (long i = startNum; i <= stopNum; i++) {
if (i % progressCheck == 0) {
System.out.println(i);
}
}
long final2 = System.currentTimeMillis() - date;
date = System.currentTimeMillis();
// using a final declared variable
for (long i = startNum; i <= stopNum; i++) {
if (i % finalProgressCheck == 0) {
System.out.println(i);
}
}
long final3 = System.currentTimeMillis() - date;
date = System.currentTimeMillis();
// using increments to determine progressCheck
int increment = 0;
for (long i = startNum; i <= stopNum; i++) {
if (increment == 65536) {
System.out.println(i);
increment = 0;
}
increment++;
}
//using a short conversion
long final4 = System.currentTimeMillis() - date;
date = System.currentTimeMillis();
for (long i = startNum; i <= stopNum; i++) {
if ((short)i == 0) {
System.out.println(i);
}
}
long final5 = System.currentTimeMillis() - date;
System.out.println(
"\nfixed = " + final1 + " ms " + "\nvariable = " + final2 + " ms " + "\nfinal variable = " + final3 + " ms " + "\nincrement = " + final4 + " ms" + "\nShort Conversion = " + final5 + " ms");
}
}
结果:
不足为奇,由于缺乏除法,短转换比“快速”方式快23%。这一点值得注意。如果您需要每256次(或大约256次)显示或比较某件事,您可以这样做,并使用
if ((byte)integer == 0) {'Perform progress check code here'}
最后一个有趣的注意事项是,在“FINAL声明变量”上使用模数65536(不是一个漂亮的数字)的速度是固定值的一半(慢)。在此之前,它的基准测试速度接近相同。
您正在测量OSR(堆栈上替换)存根。
OSR存根是编译方法的一个特殊版本,专门用于在方法运行时将执行从解释模式转换为编译代码。
OSR存根不如常规方法优化,因为它们需要一个与解释帧兼容的帧布局。我已经在下面的答案中显示了这一点:1、2、3。
类似的事情也发生在这里。当“低效代码”运行一个长循环时,该方法是专门为循环内部的堆栈上替换而编译的。状态从解释的帧转移到OSR编译的方法,这个状态包括progresscheck
局部变量。在这一点上,JIT不能用常量替换变量,因此不能应用某些优化,如强度降低。
特别是,这意味着JIT不会用乘法代替整数除法。(请参见为什么GCC在实现整数除法时使用一个奇怪的数字的乘法?对于来自提前编译器的asm技巧,当值是内联/常数传播后的编译时常数时,如果启用了这些优化。%
表达式中的整数文字也会通过gcc-o0
进行优化,类似于这里,即使在OSR存根中也会通过JITer进行优化。)
但是,如果您多次运行相同的方法,第二次和随后的运行将执行常规的(非OSR)代码,这是完全优化的。这里有一个证明理论的基准(使用JMH进行基准):
@State(Scope.Benchmark)
public class Div {
@Benchmark
public void divConst(Blackhole blackhole) {
long startNum = 0;
long stopNum = 100000000L;
for (long i = startNum; i <= stopNum; i++) {
if (i % 50000 == 0) {
blackhole.consume(i);
}
}
}
@Benchmark
public void divVar(Blackhole blackhole) {
long startNum = 0;
long stopNum = 100000000L;
long progressCheck = 50000;
for (long i = startNum; i <= stopNum; i++) {
if (i % progressCheck == 0) {
blackhole.consume(i);
}
}
}
}
和结果:
# Benchmark: bench.Div.divConst
# Run progress: 0,00% complete, ETA 00:00:16
# Fork: 1 of 1
# Warmup Iteration 1: 126,967 ms/op
# Warmup Iteration 2: 105,660 ms/op
# Warmup Iteration 3: 106,205 ms/op
Iteration 1: 105,620 ms/op
Iteration 2: 105,789 ms/op
Iteration 3: 105,915 ms/op
Iteration 4: 105,629 ms/op
Iteration 5: 105,632 ms/op
# Benchmark: bench.Div.divVar
# Run progress: 50,00% complete, ETA 00:00:09
# Fork: 1 of 1
# Warmup Iteration 1: 844,708 ms/op <-- much slower!
# Warmup Iteration 2: 105,893 ms/op <-- as fast as divConst
# Warmup Iteration 3: 105,601 ms/op
Iteration 1: 105,570 ms/op
Iteration 2: 105,475 ms/op
Iteration 3: 105,702 ms/op
Iteration 4: 105,535 ms/op
Iteration 5: 105,766 ms/op
divvar
的第一次迭代确实要慢得多,因为编译的OSR存根效率不高。但是只要方法从一开始就重新运行,就会执行新的无约束版本,它利用了所有可用的编译器优化。
问题内容: 我必须编写一个例程,如果变量的类型为,则将变量的值加1,否则将变量的值分配为0,其中变量的初始值为或。 第一个实现是因为我认为没有数字会使算术表达式为假,但是由于计算为真,所以这是错误的。然后,我得知行为类似于0,并且以下表达式均被评估为true。 当然不是0。被评估为false。这使看似重言式的表达成为错误。 为什么实际上不是0,却像0? 问题答案: 您真正的问题似乎是: 为什么:
今天我决定测试一下,结果我惊讶地发现(至少在C#正则表达式引擎中)似乎比其他两个没有太大区别的代码效率要低。下面是我测试输出的10000个由1000个随机字符组成的字符串,其中5077个实际包含一个数字: 这对我来说是一个惊喜,有两个原因,如果有人能给我一些启示,我会很感兴趣: 我本以为范围的实现会比集合的实现效率高得多。 我不能理解为什么比差。除了的简写之外,还有其他内容吗? 下面是测试代码:
问题内容: 我有以下代码。应该返回表的最后一行的mysqli_insert_id()(在本例中为“ $ last_row”)始终返回0。为什么会这样呢? 问题答案: 并 没有 返回表的最后一排的ID。从文档中,它: …返回由查询产生的ID,该查询是对具有具有AUTO_INCREMENT属性的列的表进行的。如果最后一个查询不是or 语句,或者如果修改后的表没有带有属性的列,则此函数 将返回零 。 (
问题内容: 什么是高/低算法? 我已经在NHibernate文档中找到了这一点(这是生成唯一密钥的一种方法,第5.1.4.2节),但是我没有找到有关其工作原理的很好的解释。 我知道Nhibernate可以处理它,并且我不需要了解内部,但是我很好奇。 问题答案: 基本思想是,您有两个数字组成主键-“高”数字和“低”数字。客户端可以从本质上增加“高”序列,知道它随后可以安全地从先前的“高”值的整个范围
为什么在Python中比慢?难道不应该比快吗? 我试图学习模块。从基础开始,我尝试了这些: null 注意:我运行三次,取结果的平均值,然后将时间和代码一起张贴在这里。 这个问题与如何做微基准测试无关(我在这个例子中做了,但我也明白它太基础了),而是为什么检查一个‘真’变量比一个常量慢。
问题内容: 我一直在获取要使用mysqli返回的行数方面遇到麻烦。即使确实有一些结果,我每次都会得到0。 为什么没有显示正确的数字? 问题答案: 您需要先调用num_rows查找: 请参阅文档,该文档显示在页面顶部附近(在主要说明区域中)…