在C ++中更快地执行两个程序的可能解释(与Python比较)?

时间:2019-03-31 16:42:23

标签: python c++ algorithm performance execution-time

更新:C ++程序(如下所示)在编译时没有附加标志,即g++ program.cpp。但是,提高优化级别并不会改变蛮力运行速度比记忆技术快的事实(我的计算机上为0.1秒VS 1秒)。

上下文

我尝试计算最长Collatz sequence的数字(<100万)。我编写了一个蛮力算法,并将其与建议的优化程序(基本上使用记忆)进行了比较。

我的问题是:蛮力执行比C ++中假定的优化(记忆化)版本执行得更快的原因可能是什么?

在我的机器(Macbook Air)上进行比较之后;时间在注释中程序代码的开头。

C ++(强力)

/**
 * runs in 1 second
 */

#include <iostream>
#include <vector>

unsigned long long nextSequence(unsigned long long n)
{
  if (n % 2 == 0)
    return n / 2;
  else
  {
    return 3 * n + 1;
  }
}

int main()
{
  int max_counter = 0;
  unsigned long long result;
  for (size_t i = 1; i < 1000000; i++)
  {
    int counter = 1;
    unsigned long long n = i;
    while (n != 1)
    {
      n = nextSequence(n);
      counter++;
    }
    if (counter > max_counter)
    {
      max_counter = counter;
      result = i;
    }
  }

  std::cout << result << " has " << max_counter << " sequences." << std::endl;

  return 0;
}

C ++(记忆)

/**
 * runs in 2-3 seconds 
 */

#include <iostream>
#include <unordered_map>

int countSequence(uint64_t n, std::unordered_map<uint64_t, uint64_t> &cache)
{
  if (cache.count(n) == 1)
    return cache[n];

  if (n % 2 == 0)
    cache[n] = 1 + countSequence(n / 2, cache);
  else
    cache[n] = 2 + countSequence((3 * n + 1) / 2, cache);

  return cache[n];
}

int main()
{
  uint64_t max_counter = 0;
  uint64_t result;
  std::unordered_map<uint64_t, uint64_t> cache;
  cache[1] = 1;
  for (uint64_t i = 500000; i < 1000000; i++)
  {
    if (countSequence(i, cache) > max_counter)
    {
      max_counter = countSequence(i, cache);
      result = i;
    }
  }

  std::cout << result << std::endl;

  return 0;
}

在Python中,记忆技术实际上运行得更快。

Python(记忆)

# runs in 1.5 seconds

def countChain(n):
    if n in values:
        return values[n]
    if n % 2 == 0:
        values[n] = 1 + countChain(n / 2)
    else:
        values[n] = 2 + countChain((3 * n + 1) / 2)
    return values[n]


values = {1: 1}
longest_chain = 0
answer = -1

for number in range(500000, 1000000):
    if countChain(number) > longest_chain:
        longest_chain = countChain(number)
        answer = number

print(answer)

Python(强力)

# runs in 30 seconds


def countChain(n):
    if n == 1:
        return 1
    if n % 2 == 0:
        return 1 + countChain(n / 2)
    return 2 + countChain((3 * n + 1) / 2)


longest_chain = 0
answer = -1

for number in range(1, 1000000):
    temp = countChain(number)
    if temp > longest_chain:
        longest_chain = temp
        answer = number

print(answer)

2 个答案:

答案 0 :(得分:4)

我了解您的问题是关于两个C ++变体之间的差异,而不是有关已编译的C ++与解释的python之间的差异。果断地回答它,将需要在打开优化的情况下编译代码并对其执行进行性能分析。明确说明编译器目标是64位还是32位。

但是考虑到两个版本的C ++代码之间的数量级,快速检查已经显示出您的备忘录消耗的资源多于获得的资源。

这里的一个重要性能瓶颈是无序映射的内存管理。 unordered_mapbuckets of items一起使用。该映射会在必要时调整存储桶的数量,但这需要分配内存(以及可能移动的内存块,具体取决于存储桶的实现方式)。

现在,如果您在缓存初始化之后并在显示结果之前添加以下语句,您将发现number of buckets allocated中的变化很大:

std::cout << "Bucket count: "<<cache.bucket_count()<<"/"<<cache.max_bucket_count()<<std::endl; 

为避免与此相关的开销,您可以在构造时预先分配存储桶数:

std::unordered_map<uint64_t, uint64_t> cache(3000000);

在ideone上进行一次小型的非正式测试可以节省近50%的性能。

但是没什么……在unordered_map中存储和查找对象需要计算由许多算术运算组成的哈希码。因此,我想这些操作比进行蛮力计算要简单得多。

答案 1 :(得分:2)

主存储器访问比计算慢得多,如此之多,以至于在需要照顾时,您应该处理从I检索到的很少(取决于cpu模型)的meg中的任何事情。 / O或网络设备。

与整数运算相比,即使从L1进行获取也很昂贵。

很久很久以前,这不是事实。至少几十年来,计算和内存访问至少在同一领域,因为晶体管预算中根本没有足够的空间来容纳足够大的快速缓存以支付费用。

因此人们数了CPU操作,只是假设内存可以或多或少地保持不变。

如今,它……无法。 CPU缓存未命中的惩罚是整数操作的数百,您的百万个16字节条目的哈希映射可以保证不仅烧毁了CPU的内存缓存,而且还破坏了TLB,从痛苦到毁灭的延迟惩罚。