【问题标题】:Algorithm implementation in C# seems to be catastrophic [closed]C# 中的算法实现似乎是灾难性的 [关闭]
【发布时间】:2015-07-23 09:17:43
【问题描述】:

我试图实现这篇研究论文中指定的算法: [这里 - 请忽略数学,因为它与问题无关] [2]。该算法在形式概念分析中非常基础。输入是一个矩阵NXM,在.txt 文件中存储为X.。根据论文中嵌入的伪代码,输入也必须表示为矩阵

【问题讨论】:

  • 假设您的实际测试测量忽略了 JIT 时间并且您在没有调试器的情况下运行发布版本...下一步将是用户分析器并查看花费最多的时间...通常这样的问题会引起人们的兴趣和您无需任何努力即可获得优化版本(这主要是机会游戏)。
  • 如果没有可靠地证明问题的a good, minimal, complete code example,您不太可能得到任何有帮助的具体答案。至于“C#是否与C++一样好”,当然是,但每种语言都有其优点和缺点。尽管如此,即使是一个天真的 C++ 算法实现的版本也应该在非托管代码的性能的 10-20% 范围内,并且通过努力(并且可能使用unsafe 代码)应该能够实现奇偶校验。对于正确完成的端口,35 倍的差异是闻所未闻的。
  • 如果没有将 C++ 源代码作为参考,很难回答这个问题。您的代码是否产生正确的结果?
  • 实际上,确实如此,只是做得很奇怪,如果您可以发布一个我们可以自己编译的完整示例,并具有预期的输出,那么可以将您的代码重构为更正常的 C#
  • generate_from中,新的int[] Ds在for (int jcompute_closure中重复分配。 D 始终为CT_WIDTH 大小,并且在每次j-iteration 之后不再使用。您可以通过在 for(int j 循环和 calculate_closure 调用之间重用 Ds 来相同的一些内存和 GC 时间。而不是 int[] D; /.../ D=compute_closure,预先创建 D 并将其传递给函数:int[] D = new int[xxx]; /.../ compute_closure(D, B, j) + 从 closure 中删除 D 的分配.. 我怀疑这就是全部,但有点确定。

标签: c# c++ algorithm performance optimization


【解决方案1】:

如果无法看到您正在比较的 C++ 实现的源代码,就无法确定它比 C# 代码快得多的原因,但是由于您的每个值都是 0 或 1您可以做的一种优化(以使您的代码更复杂为代价)是将值存储在某种位掩码数据结构中,或者就像 int 数组中的打包位值一样,并使用位操作操作来操作它们。 C++ 实现可能会这样做,但为了便于解释,已发布的伪代码中没有显示。

例如,由于 CT_WIDTH 为 126,因此您可以将单行存储为 4 x 32 位整数(128 位),而不是 126 位整数。

然后这样的操作:

match = true;
for (int j = 0; j < CT_WIDTH; j++)
{
    if (B[j] == 1 && FCAContext[rows[y][i] * CT_WIDTH + j] == 0)
    {
        match = false;
        break;
    }
}

可以这样重写,一次有效处理32个值:

// CT_WIDTH_SHIFTED = (CT_WIDTH + 31) / 32
match = true;
int index = rows[y][i] * CT_WIDTH_SHIFTED;
for (int j = 0; j < CT_WIDTH_SHIFTED; j++)
{
    if (B[j] & FCAContext[index + j] != B[j])
    {
        match = false;
        break;
    }
}

同样,这个:

for (int j = 0; j < CT_WIDTH; j++)
    if (FCAContext[rows[y][i] * CT_WIDTH + j] == 0)
         D[j] = 0;

可以改写为

for (int j = 0; j < CT_WIDTH_SHIFTED; j++)
    D[j] &= FCAContext[index + j];

FCAContext、D 和 B 中的值需要存储为打包位。

例如,在数组中设置一个位,而不是使用这样的代码来设置 int 数组的第 j 个元素:

B[j] = 1;

您首先通过将 j 除以 32 左移 5 (j >> 5) 来计算索引,然后计算元素内部的位,如下所示 1

B[j >> 5] |= 1 << (j & 31)

Here's 一个关于位掩码的在线教程。谷歌“位操作”、“位移”、“位掩码”、“位黑客”和“位操作”/“位操作”以获取更多信息。位操作可能会变得非常繁琐,但会使您的代码难以阅读。

还可以考虑使用 .NET BitArray 类。

【讨论】:

  • 您能否更新您的问题以解释有关位掩码的更多信息?
  • 我就是不明白这个// CT_WIDTH_SHIFTED = (CT_WIDTH + 31) &gt;&gt; 5
  • 它计算存储 126 位所需的 32 位整数的数量。加 31 是为了保证数值向上取整,因为这意味着结果会大一,除非 CT_WIDTH 能被 32 整除。 (126 + 31) / 32 = 4(整数除法)
  • 好的,但是 126 不是固定的,它取决于输入,有时它会像 4 或 60 或 200 ...等
  • 是的,这就是为什么它是一个公式而不是一个硬编码的值。例如(60 + 31) / 32 = 2。(200 + 31) / 32 = 7 等等。
猜你喜欢
  • 2016-02-08
  • 2010-10-04
  • 1970-01-01
  • 1970-01-01
  • 2023-03-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-26
相关资源
最近更新 更多