har*_*ers 53 c c# c++ java for-loop
在整个网络上,代码示例都有如下所示的for
循环:
for(int i = 0; i < 5; i++)
Run Code Online (Sandbox Code Playgroud)
我使用以下格式:
for(int i = 0; i != 5; ++i)
Run Code Online (Sandbox Code Playgroud)
我这样做是因为我认为它更有效率,但这在大多数情况下真的很重要吗?
Luc*_*cas 86
每个人都喜欢他们的微观优化,但就我所见,这并没有什么不同.对于英特尔处理器,我使用g ++编译了两个版本,没有任何花哨的优化,结果是
Run Code Online (Sandbox Code Playgroud)for(int i = 0; i < 5; i++)
movl $0, -12(%ebp)
jmp L2
L3:
leal -12(%ebp), %eax
incl (%eax)
L2:
cmpl $4, -12(%ebp)
jle L3
Run Code Online (Sandbox Code Playgroud)
Run Code Online (Sandbox Code Playgroud)for(int i = 0; i != 5; ++i)
movl $0, -12(%ebp)
jmp L7
L8:
leal -12(%ebp), %eax
incl (%eax)
L7:
cmpl $5, -12(%ebp)
jne L8
Run Code Online (Sandbox Code Playgroud)
我认为jle
并且jne
应该转换为大多数架构上同样快速的指令.所以对于性能,你不应该区分这两者.总的来说,我同意第一个更安全,我也认为更常见.
编辑(2年后):由于这个帖子最近再次得到了很多关注,我想补充说,一般来说很难回答这个问题.C-Standard [PDF] 特别没有定义哪些版本的代码更有效(同样适用于C++,也可能适用于C#).
第5.1.2.3节程序执行
§1本国际标准中的语义描述描述了抽象机器的行为,其中优化问题无关紧要.
但是有理由认为现代编译器会生成同样有效的代码,我认为在非常罕见的情况下,循环测试和计数表达式将成为for循环的瓶颈.
至于味道,我写
for(int i = 0; i < 5; ++i)
Run Code Online (Sandbox Code Playgroud)
Yur*_*ich 69
如果由于某种原因i
在循环中跳到50,你的版本将永远循环.这i < 5
是一个健全检查.
Tom*_*icz 41
表格
for (int i = 0; i < 5; i++)
Run Code Online (Sandbox Code Playgroud)
是惯用的,所以对于有经验的C程序员来说,它更容易阅读.特别是当用于迭代数组时.您应尽可能编写惯用代码,因为它读取速度更快.
在循环中修改i或使用不同于1的增量时,它也会更安全一些.但这是一个小问题.最好仔细设计你的循环并添加一些断言以尽早捕捉破坏的假设.
use*_*019 13
这取决于语言.
C++文本经常建议第二种格式,因为它可以与迭代器一起使用,可以直接比较(!=),但不能与更大或更小的条件进行比较.预增量也可以比后增量更快,因为不需要变量的副本进行比较 - 但是优化者可以处理这个问题.
对于整数,要么形成作品.C的常见习语是第一个,而对于C++,它是第二个.
对于C#和Java的使用,我会循环遍历所有事情.
在C++中还有std :: for_each函数需要使用一个仿函数,对于简单的情况,它可能比这里的任何一个例子和Boost FOR_EACH更复杂,它们看起来像C#foreach但内部很复杂.
Jon*_*nna 10
你给的东西实际上有四种排列.对你们两个:
for(int i = 0; i < 5; i++)
for(int i = 0; i != 5; ++i)
Run Code Online (Sandbox Code Playgroud)
我们可以添加:
for(int i = 0; i < 5; ++i)
for(int i = 0; i != 5; i++)
Run Code Online (Sandbox Code Playgroud)
在大多数具有现代编译器的现代机器上,这些效率完全相同并不奇怪.可能有一天你可能会发现自己为一些小型处理器编程,而平等比较和低于比较之间存在差异.
在某些情况下,根据我们选择0和5的原因,某个特定情况的特定情况可能更有意义地考虑"小于"或"不等于",但即使这样,一个人看起来很明显编码员不得与他人合作.
更抽象的是,这些是以下形式:
for(someType i = start; i < end; i++)
for(someType i = start; i != end; ++i)
for(someType i = start; i < end; ++i)
for(someType i = start; i != end; i++)
Run Code Online (Sandbox Code Playgroud)
这里的一个明显区别是,在两种情况下someType
必须具有意义<
,对于其余情况,它必须具有意义!=
.!=
定义的类型<
并不常见,包括C++中的很多迭代器对象(可能在C#中,与STL迭代器相同的方法是可行的,有时也很有用,但既不是惯用的,也不是公共库直接支持的,也不是因为有竞争对手的习语有更直接的支持,所以常常很有用.值得注意的是,STL方法是专门设计的,以便在有效迭代器类型集中包含指针.如果您习惯使用STL,!=
即使应用于整数,您也会考虑使用更加惯用的表单.就个人而言,接触它的量非常小,足以让我成为我的直觉.
另一方面,虽然定义<
而不是!=
更少,但它适用于我们用增量i
值的不同增量替换增量或者在i
循环内可能改变的情况.
因此,双方都有明确的案例,其中一种或另一种是唯一的方法.
现在对于++i
VS i++
.再次使用整数并且直接调用而不是通过返回结果的函数(并且偶然的机会),实际结果将完全相同.
在一些C风格的语言(没有运算符过载的语言)中,整数和指针是唯一的情况.我们可以人为地发明一种情况,即通过函数调用增量只是为了改变它的运行方式,编译器仍然可能会将它们变成同样的东西.
C++和C#允许我们覆盖它们.通常,前缀的++
作用类似于以下功能:
val = OneMoreThan(val);//whatever OneMoreThan means in the context.
//note that we assigned something back to val here.
return val;
Run Code Online (Sandbox Code Playgroud)
postfix ++
的功能就像一个函数:
SomeType copy = Clone(val);
val = OneMoreThan(val);
return copy;
Run Code Online (Sandbox Code Playgroud)
C++和C#都不能完美地匹配上述内容(我故意不使我的伪代码匹配),但在任何一种情况下都可能有副本或者两个副本.这可能贵也可能不贵.它可能是可避免的,也可能是不可避免的(在C++中我们通常可以this
通过返回void 返回和后缀中的前缀形式完全避免它).它可能会也可能不会被优化为任何东西,但它仍然++i
比i++
在某些情况下更有效.
更具体地说,稍微提高一点性能的可能性很大++i
,而且一个大类甚至可能是相当大的,但是除非有人压倒C++以使两者具有完全不同的含义(一个非常糟糕的主意)它通常不可能这是另一种方式.因此,养成在postfix上使用前缀的习惯意味着你可能会在一千次中获得一次改进,但不会失败,所以这是C++编码人员经常遇到的习惯.
总之,在您的问题中给出的两种情况绝对没有区别,但可以有相同的变体.
我同意关于可读性的说法 - 重要的是让代码对于维护者来说很容易阅读,尽管你希望那些人能够理解增量前后的内容.
也就是说,我认为我会运行一个简单的测试,并获得一些关于四个循环中哪个循环运行最快的可靠数据.我是普通的规格电脑,用javac 1.7.0编译.
我的程序进行了一个for循环,在没有任何东西的情况下迭代了2,000,000次(以免在for循环中执行任何操作需要多长时间来淹没有趣的数据).它使用上面提出的所有四种类型,并对结果进行计时,重复1000次以获得平均值.
实际代码是:
public class EfficiencyTest
{
public static int iterations = 1000;
public static long postIncLessThan() {
long startTime = 0;
long endTime = 0;
startTime = System.nanoTime();
for (int i=0; i < 2000000; i++) {}
endTime = System.nanoTime();
return endTime - startTime;
}
public static long postIncNotEqual() {
long startTime = 0;
long endTime = 0;
startTime = System.nanoTime();
for (int i=0; i != 2000000; i++) {}
endTime = System.nanoTime();
return endTime - startTime;
}
public static long preIncLessThan() {
long startTime = 0;
long endTime = 0;
startTime = System.nanoTime();
for (int i=0; i < 2000000; ++i) {}
endTime = System.nanoTime();
return endTime - startTime;
}
public static long preIncNotEqual() {
long startTime = 0;
long endTime = 0;
startTime = System.nanoTime();
for (int i=0; i != 2000000; ++i) {}
endTime = System.nanoTime();
return endTime - startTime;
}
public static void analyseResults(long[] data) {
long max = 0;
long min = Long.MAX_VALUE;
long total = 0;
for (int i=0; i<iterations; i++) {
max = (max > data[i]) ? max : data[i];
min = (data[i] > min) ? min : data[i];
total += data[i];
}
long average = total/iterations;
System.out.print("max: " + (max) + "ns, min: " + (min) + "ns");
System.out.println("\tAverage: " + (average) + "ns");
}
public static void main(String[] args) {
long[] postIncLessThanResults = new long [iterations];
long[] postIncNotEqualResults = new long [iterations];
long[] preIncLessThanResults = new long [iterations];
long[] preIncNotEqualResults = new long [iterations];
for (int i=0; i<iterations; i++) {
postIncLessThanResults[i] = postIncLessThan();
postIncNotEqualResults[i] = postIncNotEqual();
preIncLessThanResults[i] = preIncLessThan();
preIncNotEqualResults[i] = preIncNotEqual();
}
System.out.println("Post increment, less than test");
analyseResults(postIncLessThanResults);
System.out.println("Post increment, inequality test");
analyseResults(postIncNotEqualResults);
System.out.println("Pre increment, less than test");
analyseResults(preIncLessThanResults);
System.out.println("Pre increment, inequality test");
analyseResults(preIncNotEqualResults);
}
}
Run Code Online (Sandbox Code Playgroud)
对不起,如果我复制错了!
结果让我觉得 - 测试 i < maxValue
每个循环大约需要1.39ms,无论是使用前增量还是后增量,但i != maxValue
需要1.05ms.这是一个节省24.5%或32.5%的时间,取决于你如何看待它.
当然,运行for循环所需的时间可能不是你的瓶颈,但这是一种有用的优化,对于你需要它的罕见情况.
不过我认为我仍然坚持不到测试!
编辑
我测试过递减我为好,并发现这并没有真正有需要个时间的影响- for (int i = 2000000; i != 0; i--)
以及for (int i = 0; i != 2000000; i++)
两者采取相同的时间长度,因为这样做for (int i = 2000000; i > 0; i--)
和for (int i = 0; i < 2000000; i++)
.
我决定列出最有信息性的答案,因为这个问题变得有点拥挤。
DenverCoder8的基准测试以及Lucas的循环编译版本显然值得一些认可。Tim Gee展示了前后增量之间的差异,而User377178则强调了 < 和 != 的一些优缺点。Tenacious Techhunter撰写了有关循环优化的一般性文章,值得一提。
这就是我的 5 个最佳答案。