在没有运行时开销的情况下使用静态类型检查对C#中的索引进行Typedef

时间:2012-11-09 05:26:40

标签: c# typechecking static-typing

使用具有复杂索引的多维数组是很常见的情况。当所有索引都是整数时,它确实令人困惑且容易出错,因为您可以轻松地混合列和行(或者您拥有的任何内容),并且编译器无法识别问题。实际上应该有两种类型的索引:行和列,但它不在类型级别上表示。

以下是我想要的一个小例子:

var table = new int[RowsCount,ColumnsCount];
Row row = 5;
Column col = 10;
int value = table[row, col];

public void CalcSum(int[,] table, Column col)
{
    int sum = 0;
    for (Row r = 0; r < table.GetLength(0); r++)
    {
        sum += table[row, col];
    }
    return sum;
}

CalcSum(table, col); // OK
CalcSum(table, row); // Compile time error

总结:

  • 索引应该静态检查混合(类型检查的种类)
  • 很重要!它们应该运行时效率高,因为将性能包装到包含索引的自定义对象然后将其展开回来是不行的
  • 它们应该可以隐式转换为int,以便在本机多维数组中充当索引

有没有办法实现这个目标?完美的解决方案就像typedef一样,它只用作编译时平面整数的编译时检查。

1 个答案:

答案 0 :(得分:1)

x64抖动只会减慢2倍速度。它生成有趣的优化代码。使用struct的循环如下所示:

00000040  mov         ecx,1 
00000045  nop         word ptr [rax+rax+00000000h] 
00000050  lea         eax,[rcx-1] 
                s.Idx = j;
00000053  mov         dword ptr [rsp+30h],eax 
00000057  mov         dword ptr [rsp+30h],ecx 
0000005b  add         ecx,2 
            for (int j = 0; j < 100000000; j++) {
0000005e  cmp         ecx,5F5E101h 
00000064  jl          0000000000000050 

这需要一些注释,因为代码不常见。首先,偏移45处的奇怪NOP用于在循环开始时对齐指令。这使得偏移64处的分支更快。 53处的指令看起来完全没必要。你在这里看到的是循环展开,注意5b处的指令如何将循环计数器递增2.然而,优化器不够智能,因此也看不到存储是不必要的。

最重要的是,没有任何ADD指令可供使用。换句话说,代码实际上并不计算“sum”的值。这是因为你没有在循环之后的任何地方使用它,优化器可以看到计算是无用的并完全删除它。

在第二个循环中做得更好:

000000af  xor         eax,eax 
000000b1  add         eax,4 
            for (int j = 0; j < 100000000; j++) {
000000b4  cmp         eax,5F5E100h 
000000b9  jl          00000000000000B1 

现在它完全删除了“sum”计算和“i”变量赋值。它也可以删除整个for()循环但是抖动优化器永远不会这样做,它假定延迟是故意的。

希望现在的信息很明确:避免从人工基准测试中做出假设,并且只能编写真正的代码。您可以通过实际显示“sum”的值使其更真实,以便优化器不会丢弃计算。在循环之后添加以下代码行:

        Console.Write("Sum = {0} ", sum);

现在你会发现它已经没有区别了。