将容器操作编译为高效的增量代码

时间:2016-04-21 15:19:12

标签: algorithm optimization collections compiler-optimization

现在大多数编程语言都提供了list,set,multiset,map等容器。对容器的所有元素的操作,例如copy_iftransform通常需要O(n)时间。如果你只需要结果的前几个元素,那么延迟评估可以使这个次线性,但如果你需要完整的结果,它会回到线性。

考虑例如DPLL算法的命题可满足性的以下实现。它本质上是将伪代码转换为C ++,因此针对可读性进行了优化。但是每个步骤在变量数量上都需要时间线性,因此即使它第一次能够正确地猜测所有分配,总时间和内存消耗在变量数量上也是二次的,这使得实现在实践中使用太慢。即使我们应用众所周知的优化,例如通过常量引用传递容器,并在任何可能避免不必要工作的地方使用延迟评估,也是如此。

DPLL的高效实现使用增量技术,其中不是扫描和复制整个容器,而是使用每步的O(1)时间和内存来计算进行小变化(如分配单个变量)的后果。

人类可以将伪代码或未优化的参考实现转换为有效的增量实现;这就是我们在编写实用的SAT求解器,定理证明器等时所做的工作。我们付出的代价是从此开始使用大量复杂的优化代码,这比使用简洁的数学逻辑描述困难得多。

我们理想的是一个更高级别的编译器,它可以将未优化的参考实现编译成有效的增量代码。

我的问题是:有没有做过这方面的工作?是否有任何现有的实施,部分实施或讨论?容器操作的更高级优化原则上并不完全是未知的,例如, SQL查询优化器,Haskell循环融合,但我不知道有什么试图在我这里寻找。

DPLL的未优化参考实现(伪代码的简单转换):

bool dpll(map<Var, bool> m, set<set<Literal>> clauses) {
  clauses = eval(m, clauses);

  // Solved
  if (isFalse(clauses))
    return false;
  if (isTrue(clauses))
    return true;

  // Unit clause
  auto unitClauses =
      copy_if(clauses, [](set<Literal> clause) { return clause.size() == 1; });
  if (unitClauses.size()) {
    auto x = front(front(unitClauses));
    return dpll(m + makeMap(x.var, x.pol), clauses);
  }

  // Pure literal
  auto pureVars =
      copy_if(vars(clauses), [=](Var x) { return pure(x, clauses); });
  if (pureVars.size()) {
    auto x = front(pureVars);
    return dpll(m + makeMap(x, pol(x, clauses)), clauses);
  }

  // Choice
  auto x = choose(vars(clauses));
  return dpll(m + makeMap(x, false), clauses) ||
         dpll(m + makeMap(x, true), clauses);
}

辅助函数的类似参考实现:https://github.com/russellw/ayane/blob/master/logic/dpll.cpp

0 个答案:

没有答案