考虑以下代码:
using System;
using System.Diagnostics;
namespace Demo
{
class Program
{
static void Main(string[] args)
{
Stopwatch sw = new Stopwatch();
for (int trial = 0; trial < 4; ++trial)
{
sw.Restart();
loop1();
Console.WriteLine("loop1() took " + sw.Elapsed);
sw.Restart();
loop2();
Console.WriteLine("loop2() took " + sw.Elapsed);
sw.Restart();
loop3();
Console.WriteLine("loop3() took " + sw.Elapsed);
// Console.WriteLine(); // <-- Uncomment this and the timings change a LOT!
}
}
static void loop1()
{
bool done = false;
for (int i = 0; i < 100000 && !done; ++i)
{
for (int j = 0; j < 100000 && !done; ++j)
{
for (int k = 0; k < 2; ++k)
{
if (i == 9900)
{
done = true;
break;
}
}
}
}
}
static void loop2()
{
for (int i = 0; i < 100000; ++i)
{
for (int j = 0; j < 100000; ++j)
{
for (int k = 0; k < 2; ++k)
{
if (i == 9900)
{
goto exit;
}
}
}
}
exit: return;
}
static void loop3()
{
for (int i = 0; i < 100000; ++i)
{
for (int j = 0; j < 100000; ++j)
{
for (int k = 0; k < 2; ++k)
{
if (i == 9900)
{
k = 2;
j = 100000;
i = 100000;
}
}
}
}
}
}
}
当我使用 Visual Studio 2010 在 Windows 7 x64 上编译并运行此版本的 RELEASE x86 版本时,我得到以下计时(在 Intel Core i7 上运行)
loop1() took 00:00:01.7935267
loop2() took 00:00:01.4747297
loop3() took 00:00:05.6677592
loop1() took 00:00:01.7654008
loop2() took 00:00:01.4818888
loop3() took 00:00:05.7656440
loop1() took 00:00:01.7990239
loop2() took 00:00:01.5019258
loop3() took 00:00:05.7979425
loop1() took 00:00:01.8356245
loop2() took 00:00:01.5688070
loop3() took 00:00:05.7238753
这本身就很奇怪——为什么 loop3() 会比其他循环慢那么多? 无论如何,然后我取消注释指示的行(Console.WriteLine()),我的时间变为:
loop1() took 00:00:01.8229538
loop2() took 00:00:07.8174210
loop3() took 00:00:01.4879274
loop1() took 00:00:01.7691919
loop2() took 00:00:07.4781999
loop3() took 00:00:01.4810248
loop1() took 00:00:01.7749845
loop2() took 00:00:07.5304738
loop3() took 00:00:01.4634904
loop1() took 00:00:01.7521282
loop2() took 00:00:07.6325186
loop3() took 00:00:01.4663219
现在 loop2() 慢得多,而 loop3() 快得多。我觉得这很好奇......
所以我有两个问题:
[编辑] 我应该补充一点,我可以用秒表验证这些计时,并且我正在从命令行运行测试程序(因此我们可以排除 Visual Studio 对其进行干扰)。
附录:
我对程序进行了如下修改,以排除 JITTER 正在优化循环的可能性:
using System;
using System.Diagnostics;
using System.Text;
namespace Demo
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine(test());
}
static string test()
{
Stopwatch sw = new Stopwatch();
int total = 0;
StringBuilder builder = new StringBuilder();
for (int trial = 0; trial < 2; ++trial)
{
sw.Restart();
total += loop1();
builder.AppendLine("loop1() took " + sw.Elapsed);
sw.Restart();
total += loop2();
builder.AppendLine("loop2() took " + sw.Elapsed);
sw.Restart();
total += loop3();
builder.AppendLine("loop3() took " + sw.Elapsed);
//builder.AppendLine(); // Uncommenting this line makes a big difference!
}
builder.AppendLine(total.ToString());
return builder.ToString();
}
static int loop1()
{
bool done = false;
int total = 0;
for (int i = 0; i < 100000 && !done; ++i)
{
for (int j = 0; j < 100000 && !done; ++j)
{
for (int k = 0; k < 2; ++k)
{
if (i == 9900)
{
done = true;
break;
}
++total;
}
}
}
return total;
}
static int loop2()
{
int total = 0;
for (int i = 0; i < 100000; ++i)
{
for (int j = 0; j < 100000; ++j)
{
for (int k = 0; k < 2; ++k)
{
if (i == 9900)
{
goto exit;
}
++total;
}
}
}
exit: return total;
}
static int loop3()
{
int total = 0;
for (int i = 0; i < 100000; ++i)
{
for (int j = 0; j < 100000; ++j)
{
for (int k = 0; k < 2; ++k)
{
if (i == 9900)
{
k = 2;
j = 100000;
i = 100000;
}
else
{
++total;
}
}
}
}
return total;
}
}
}
现在我的结果如下:
builder.AppendLine() 注释掉了:
loop1() took 00:00:06.6509471
loop2() took 00:00:06.7322771
loop3() took 00:00:01.5361389
loop1() took 00:00:06.5746730
loop2() took 00:00:06.7051531
loop3() took 00:00:01.5027345
-1004901888
builder.AppendLine() 未注释掉:
loop1() took 00:00:06.9444200
loop2() took 00:00:02.8960563
loop3() took 00:00:01.4759535
loop1() took 00:00:06.9036553
loop2() took 00:00:03.1514154
loop3() took 00:00:01.4764172
-1004901888
请注意我这样做时 loop2() 计时的差异。不计算!
最佳答案
我可以准确重现。此外,我还可以像这样消除方差:
private static void Main(string[] args)
{
Stopwatch sw = new Stopwatch();
for (int trial = 0; trial < 4; ++trial)
{
sw.Restart();
Interlocked.MemoryBarrier();
loop1();
Interlocked.MemoryBarrier();
Console.WriteLine("loop1() took " + sw.Elapsed);
sw.Restart();
Interlocked.MemoryBarrier();
loop2();
Interlocked.MemoryBarrier();
Console.WriteLine("loop2() took " + sw.Elapsed);
sw.Restart();
Interlocked.MemoryBarrier();
loop3();
Interlocked.MemoryBarrier();
Console.WriteLine("loop3() took " + sw.Elapsed);
// Console.WriteLine(); // <-- Uncomment this and the timings don't change now.
}
}
当我使用 MemoryBarriers 运行时,无论我以何种方式运行测试,我都会得到第二种模式:
loop1() took ~1 sec
loop2() took ~7 secs
loop3() took ~1 sec
MemoryBarrier 定义:
Synchronizes memory access as follows: The processor executing the current thread cannot reorder instructions in such a way that memory accesses prior to the call to MemoryBarrier execute after memory accesses that follow the call to MemoryBarrier.
由于循环的 IL 在两个版本之间是相同的,并且 MemoryBarrier 使方差消失,我认为我们可以得出结论,方差肯定是第一个模式优化的结果......也许是 JITer.. .也许是 CPU 的问题……不确定。
除了我使用的是 VS2012 和 .NET 4.5 RTM,我的环境是一样的。
关于c# - 在循环外添加一个 Console.WriteLine() 会改变循环的计时 - 为什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11984176/
类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc
当我使用Bundler时,是否需要在我的Gemfile中将其列为依赖项?毕竟,我的代码中有些地方需要它。例如,当我进行Bundler设置时:require"bundler/setup" 最佳答案 没有。您可以尝试,但首先您必须用鞋带将自己抬离地面。 关于ruby-我需要将Bundler本身添加到Gemfile中吗?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/4758609/
我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co
我脑子里浮现出一些关于一种新编程语言的想法,所以我想我会尝试实现它。一位friend建议我尝试使用Treetop(Rubygem)来创建一个解析器。Treetop的文档很少,我以前从未做过这种事情。我的解析器表现得好像有一个无限循环,但没有堆栈跟踪;事实证明很难追踪到。有人可以指出入门级解析/AST指南的方向吗?我真的需要一些列出规则、常见用法等的东西来使用像Treetop这样的工具。我的语法分析器在GitHub上,以防有人希望帮助我改进它。class{initialize=lambda(name){receiver.name=name}greet=lambda{IO.puts("He
我有多个ActiveRecord子类Item的实例数组,我需要根据最早的事件循环打印。在这种情况下,我需要打印付款和维护日期,如下所示:ItemAmaintenancerequiredin5daysItemBpaymentrequiredin6daysItemApaymentrequiredin7daysItemBmaintenancerequiredin8days我目前有两个查询,用于查找maintenance和payment项目(非排他性查询),并输出如下内容:paymentrequiredin...maintenancerequiredin...有什么方法可以改善上述(丑陋的)代
我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%
我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i
使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta
我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何
我想要做的是有2个不同的Controller,client和test_client。客户端Controller已经构建,我想创建一个test_clientController,我可以使用它来玩弄客户端的UI并根据需要进行调整。我主要是想绕过我在客户端中内置的验证及其对加载数据的管理Controller的依赖。所以我希望test_clientController加载示例数据集,然后呈现客户端Controller的索引View,以便我可以调整客户端UI。就是这样。我在test_clients索引方法中试过这个:classTestClientdefindexrender:template=>