【问题标题】:C# pointers vs. C++ pointersC# 指针与 C++ 指针
【发布时间】:2014-06-26 18:04:23
【问题描述】:

我一直在学习编程,我选择了 C++ 和 C# 编程作为第一语言。更具体地说,我有一本旧的 C 书,有人好心让我借,我正在用它来学习 C#。我使用 Visual Studio Express 并用 C++ 和 C# 编写。我感兴趣的一个领域是进行直接内存管理的能力。我正在尝试学习使用它来优化我的代码。但是,我正在努力正确地做到这一点,并且实际上看到了任何真正的性能改进。例如,下面是 C# 中的以下代码:

unsafe static void Main(string[] args)
{
    int size = 300000;
    char[] numbers = new char[size];

    for (int i = 0; i < size; i++)
    {
        numbers[i] = '7';
    }

    DateTime start = DateTime.Now;

    fixed (char* c = &numbers[0])
    {
        for (int i = 0; i < 10000000; i++)
        {
            int number = myFunction(c, 100000);
        }
    }

    /*char[] c = numbers;  // commented out C# non-pointer version same 
          speed as C# pointer version
    {
        for (int i = 0; i < 10000000; i++)
        {
            int number = myFunction(c, 100000);
        }
    }*/

    TimeSpan timeSpan = DateTime.Now - start;
    Console.WriteLine(timeSpan.TotalMilliseconds.ToString());
    Console.ReadLine();
}

static int myFunction(ref char[] numbers, int size)
{
    return size * 100;
}

static int myFunction(char[] numbers, int size)
{
    return size * 100;
}

unsafe static int myFunction(char* numbers, int size)
{
    return size * 100;
}

无论我调用三种方法中的哪一种,我都获得了相同的执行速度。我还在尝试理解使用 ref 和使用指针之间的区别,除非这可能需要时间和练习。

然而,我不明白的是,我能够在 C++ 中产生非常显着的性能差异。以下是我尝试在 C++ 中近似相同代码时的想法:

/*int myFunction(std::string* numbers, int size)  // C++ pointer version commented 
     out is much faster than C++ non-pointer version
{
    return size * 100;
}*/

int myFunction(std::string numbers, int size) // value version
{
    return size * 100;
}

int _tmain(int argc, _TCHAR* argv[])
{
int size = 100000;
std::string numbers = "";
for (int i = 0; i < size; i++)
{
    numbers += "777";
}

clock_t start = clock();

for (int i = 0; i < 10000; i++)
{
    int number = myFunction(numbers, 100000);
}

clock_t timeSpan = clock() - start;

std::cout << timeSpan;
char c;
std::cin >> c;

return 0;
}

谁能告诉我为什么我的 C# 代码没有从我使用引用或指针中受益?我一直在网上看东西等等,但我被卡住了。

【问题讨论】:

  • 只是小费;使用/**/ 来注释它们之间的所有内容,而不必在每一行都使用//
  • 在实际的 C# 中,您可能永远不必使用不安全的代码,除了某些平台互操作情况和可能的图像操作。 C# 并非旨在直接用于管理内存,因此提供了开箱即用的垃圾收集等功能。因此,在大多数情况下,它的性能不会受益于直接内存操作。仅仅是因为它不会编译为机器代码,并且仍然在 CLR 之上执行。另一方面,C++ 直接编译为机器码并几乎完全按原样执行您的代码,因此内存优化起作用。
  • C# 对象默认是通过引用传递的,所以即使它看起来是通过使用昂贵的大副本按值传递的,它不是并且只是引用,并且仅在必要时复制对象(修改)
  • 我认为在一个非常紧凑的循环中进行简单乘法的 100000 次迭代甚至不足以在执行时间方面注册任何内容。你甚至没有对数组/指针做任何事情,那么你到底在计时什么?
  • 不要使用 C 书来学习 C#——这只会让人感到困惑。这些语言的语法有些相似,但有很多不同。

标签: c# c++ c performance pointers


【解决方案1】:

C# 已经生成了指针,而无需您显式声明它们。每个引用类型引用,就像您的 numbers 变量一样,实际上是运行时的指针。您使用 refout 关键字传递的每个参数实际上都是运行时的指针。数组参数的确切 C 等效项是 C++ 中的 char**、char*&。 C# 没有区别。

所以你看不到任何速度差异,因为实际执行的代码是相同的。

这也不完全是它停止的地方,你实际上从未对数组做任何事情。您调用的方法会在运行时消失,就像在 C 或 C++ 编译器中一样,它会被优化器内联。而且由于您不使用数组参数,因此您也不会得到任何代码。

当您使用指针来实际寻址内存时,指针对加速程序很有用。您可以索引数组并确保您永远不会为数组边界检查付费。在许多情况下,您也不会在正常使用中为其付费,如果抖动优化器知道索引始终是安全的,那么它会非常聪明地删除检查。这是指针的不安全用法,您可以轻松地在不属于数组的内存部分中乱写,并以这种方式破坏 GC 堆。用于对象引用或 ref 参数的指针永远不会不安全。

查看其中任何内容的唯一真正方法是查看生成的机器代码。调试 + Windows + 反汇编窗口。重要的是,即使您调试代码或您看不到优化,仍允许代码被优化。确保运行发布版本并使用工具+选项、调试、常规,取消选中“在模块加载时抑制 JIT 优化”选项。需要对机器代码有一定的了解才能理解所看到的内容。

【讨论】:

  • 不太一样。 ref char[]是指针指向的,另外两个是单指针。
  • 还值得指出的是,在 C++ 示例中,std::string按值 传递到函数中。它与传递数据的指针或引用不同,而是传递整个数据内容。
  • 我需要一些时间来思考你提出的观点。我不知道的一个问题是当你说“你调用的方法在运行时消失,就像它在 C 或 C++ 编译器中一样”。如果方法在运行时消失,为什么当我通过指针传递它时,如果方法在运行时消失,当我通过值传递它时,我会看到 C++ 程序执行速度的差异?只是为了确保我没有歪曲我的问题,我没有将 C# 执行速度与 C++ 执行速度进行比较。我正在将 C# 值与指针的差异与 C++ 中的差异进行比较。
  • 在 C++ 中按值传递对象是昂贵的,它必须制作对象的副本。 C++ 中的所有非指针类型都是值类型。在 C# 中是不可能的,像数组或字符串这样的引用类型总是通过引用传递。
【解决方案2】:

问题在于您没有衡量您认为自己正在衡量的内容。我可以阅读您的代码并立即了解您为什么会得到这个结果或那个结果,这不仅仅是因为指针或不是指针。还有很多其他因素在起作用,或者可能在起作用。各种 cmets 反映了这一点。

对于它的价值,一个 C++ 调用比另一个慢得多的主要原因是因为慢的一个复制了 std::string 而快速的没有。 C# 示例之间没有类似的顺序差异。

我的建议是,作为一个聪明但处于早期阶段的程序员,你首先要专注于成为一个更好的程序员。在您知道自己要达到什么目标之前,不要担心“优化”。

当您准备好真正理解这个问题时,您将不得不研究生成的代码。在 C# 的情况下,它是 MSIL,以及它在特定平台上 JIT 的任何内容。在 C++ 的情况下,这是适用于任何处理器的英特尔操作码。在您知道 MSIL 和 JIT 以及操作码是什么之前,很难解释清楚为什么会得到您所做的结果。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-02
    • 2021-05-06
    • 1970-01-01
    • 2021-11-26
    • 1970-01-01
    • 2021-12-31
    相关资源
    最近更新 更多