c# - 编译器是否优化了循环内的 if 语句
问题描述
我正在查看另一个程序员的代码并看到以下函数(以及几个变体,在一个巨大的循环和关键路径中被调用),我想知道 c# 编译器是否足够聪明来优化它:
public double SomeFunction(double p_lower, double p_upper) {
double result = 0.0;
int low = (int)Math.Ceiling(p_lower);
int high = (int)Math.Ceiling(p_upper);
for( int i = low; i <= high; ++i){
double dbl_low;
double dbl_high;
if (i == low && i == high) {
dbl_low = p_lower; // corrected from low in original post
dbl_high = p_upper; // corrected from high original post
} else if (i == low) {
dbl_low = p_lower;
dbl_high = i;
} else if (i == high) {
dbl_low = i - 1;
dbl_high = p_upper;
} else {
dbl_low = i - 1;
dbl_high = i;
}
if (dbl_low != dbl_high) {
result += f(dbl_low,dbl_high);
}
}
return result;
}
这个函数的作用很清楚,从 p_lower 到 p_upper 的范围分为三部分:分数到第一个整数,步长 1 到最后一个整数,从最后一个整数到 p_upper 的分数,并在这些间隔上调用函数。
第一个条件是上下都在同一单位区间内的边缘情况(从原始修正)
我的直觉(从我学会编程开始,编译器很糟糕)就是这样重写代码:
public double SomeFunction2(double p_lower, double p_upper) {
if(p_upper < p_lower){
return 0.0;
}
double result = 0.0;
double low = Math.Ceiling(p_lower);
double high = Math.Ceiling(p_upper);
/// edge case
if (Math.Abs(low - high) < 0.00001) {
return Math.Abs(p_upper-p_lower)< 0.00001? 0.0 : f(p_lower, p_upper);
}
/// first fraction
result += Math.Abs(low - p_lower)< 0.00001? 0.0 : f(p_lower, low);
/// whole intervals
for( double i = low + 1.0; i < high; ++i){ // < instead of <=
result += f(i-1.0, i);
}
/// add last fraction and return
return result + f(high - 1.0, p_upper);
}
这样,每个循环都不会评估整个级联的条件语句,第一个在第一个之后总是为假,第二个总是为真,除了最后一个。事实上,循环中没有条件,因为最后一个条件已包含在循环范围内。
循环计数器是一个双精度数,这不应该成为问题,因为低和高的范围是 0.0 ... 120.0,所有这些都与双精度数完全相同。
我是否在浪费时间,编译器是否处理了所有这些,我是否获得了一些可读性?
解决方案
我稍微更改了您的第二个功能以提高可读性:
using System;
using System.Diagnostics;
class Program
{
static void Main()
{
Stopwatch sw = new Stopwatch();
const int COUNT = 10000000;
double[] lowers = new double[COUNT];
double[] uppers = new double[COUNT];
double[] result = new double[COUNT];
double[] result2 = new double[COUNT];
Random random = new Random();
for (int i = 0; i < COUNT; i++)
{
lowers[i] = Math.Round(random.NextDouble() * 60.0, 2);
uppers[i] = lowers[i] + Math.Round(random.NextDouble() * 40.0,2);
}
sw.Start();
for (int i = 0; i < COUNT; i++)
{
result[i] = SomeFunction(lowers[i], uppers[i]);
}
sw.Stop();
Console.WriteLine("Elapsed Time for SomeFunction is {0} ms", sw.ElapsedMilliseconds);
sw.Reset();
sw.Start();
for (int i = 0; i < COUNT; i++)
{
result2[i] = SomeFunction2(lowers[i], uppers[i]);
}
sw.Stop();
Console.WriteLine("Elapsed Time for SomeFunction2 is {0} ms", sw.ElapsedMilliseconds);
for (int i = 0; i < COUNT; i++)
{
if (result[i] != result2[i])
{
Console.WriteLine("i: {0}",i);
}
}
}
public static double SomeFunction(double p_lower, double p_upper) {
double result = 0.0;
int low = (int)Math.Ceiling(p_lower);
int high = (int)Math.Ceiling(p_upper);
for(int i = low; i <= high; ++i){
double dbl_low;
double dbl_high;
if (i == low && i == high) {
dbl_low = p_lower;
dbl_high = p_upper;
} else if (i == low) {
dbl_low = p_lower;
dbl_high = i;
} else if (i == high) {
dbl_low = i - 1;
dbl_high = p_upper;
} else {
dbl_low = i - 1;
dbl_high = i;
}
if (dbl_low != dbl_high) {
result += f(dbl_low,dbl_high);
}
}
return result;
}
public static double SomeFunction2(double p_lower, double p_upper) {
double result = 0.0;
if (p_upper <= p_lower) {
return result;
}
double low = Math.Ceiling(p_lower);
double high = Math.Ceiling(p_upper);
/// edge case
if (high == low) {
return f(p_lower, p_upper);
}
/// first fraction
if (low > p_lower) {
result += f(p_lower, low);
}
/// whole intervals
for (int i = (int)low + 1; i < high; ++i){
result += f(i-1.0, i);
}
/// add last fraction and return
return result + f(high - 1.0, p_upper);
}
// Simple function f(a,b) for test purpose
public static double f(double a, double b)
{
return a + b;
}
}
运行这个几次给了我:
3680 毫秒 / 1863 毫秒 -> 49%
2362 毫秒 / 1441 毫秒 -> 39%
3175 毫秒 / 2030 毫秒 -> 36%
2956 毫秒 / 1531 毫秒 -> 48%
所以它在性能方面保持相当接近
推荐阅读
- jxbrowser - JxBrowser - 渲染通道已关闭 - ipc 日志:OnRenderViewGoneMessage
- c - 将数组转换为结构的宏
- c++ - 如何将 char 与 TCHAR 数组连接起来?
- excel - Excel - VBA - 连接 - 列中的多个单元格
- postgresql - Postgres 使用 %wildcard% 过滤索引和性能
- javascript - 为什么在 server.js 以外的其他文件上发布请求在 node.js 中不起作用?
- excel - 如何在 Excel VBA 中截断某些字符?
- go - 我想在 golang 中使用劫持,而在客户端获得无效响应
- security - 在客户端-服务器架构中使用英特尔 SGX 远程认证
- python - RuntimeError: 期望一个 Torch.FloatTensor 类型的张量,但为序列元素找到了一个类型 torch.IntTensor