【问题标题】:Performance issue: Java vs C++性能问题:Java vs C++
【发布时间】:2014-09-14 21:01:25
【问题描述】:

我一直听说 C++ 比 Java 更高效(这就是为什么大多数游戏都是用 C++ 开发的)。

我写了一个小算法来解决 Java 和 C++ 中的“八皇后谜题”,使用完全相同的算法,然后开始提高数字或平方。 当达到 20*20 甚至 22*22 的棋盘格时,Java 似乎更有效(3 秒对 C++ 的 66 秒)。

我不知道为什么,但我刚开始使用 C++,所以我可能犯了一些严重的性能错误,所以我很乐意接受任何有助于我理解正在发生的事情的信息。

以下是我在 Java 中使用的代码:

import java.awt.Point;
import java.util.ArrayList;
import java.util.List;

public class HuitDames {

    /**
     * La liste des coordnnées des dames.
     */
    private static List<Point> positions = new ArrayList<>();

    /**
     * Largeur de la grille.
     */
    private static final int LARGEUR_GRILLE = 22;


    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        int i = 1;
        placerDame(i);
        for (Point point : positions) {
            System.out.println("(" + point.x + "; " + point.y + ")");
        }
    }

    /**
     * Place une dame et return true si la position est bonne.
     * @param i le numéro de la dame.
     * @return si la position est bonne.
     */
    private static boolean placerDame(int i) {

        boolean bonnePosition = false;
        for (int j = 1; j <= LARGEUR_GRILLE && bonnePosition == false; j++) {
            Point emplacement = new Point(i, j);
            positions.add(emplacement);
            if (verifierPrise(emplacement) && (i == LARGEUR_GRILLE || placerDame(i + 1))) {
                bonnePosition = true;
            }
            else {
                positions.remove(i - 1);
            }
        }

        return bonnePosition;
    }

    /**
     * Vérifie que la nouvelle position n'est pas en prise avec une position déjà présente.
     * @param position la position de la nouvelle dame.
     * @return Si la position convient par rapport aux positions des autres dames.
     */
    private static boolean verifierPrise(Point position) {
        boolean nonPrise = true;
        for (Point point : positions) {
            if (!point.equals(position)) {
                // Cas où sur la même colonne.
                if (position.y == point.y) {
                    nonPrise = false;
                }
                // Cas où sur même diagonale.
                if (Math.abs(position.y - point.y) == Math.abs(position.x - point.x)) {
                    nonPrise = false;
                }
            }
        }

        return nonPrise;
    }
}

下面是C++代码:

#include <iostream>
#include <list>
#include <math.h>
#include <stdlib.h>

using namespace std;


// Class to represent points.
class Point {

    private:
        double xval, yval;

    public:
        // Constructor uses default arguments to allow calling with zero, one,
        // or two values.
        Point(double x = 0.0, double y = 0.0) {
                xval = x;
                yval = y;
        }

        // Extractors.
        double x() { return xval; }
        double y() { return yval; }
};

#define LARGEUR_GRILLE 22
list<Point> positions;


bool verifierNonPrise(Point emplacement) {
    bool nonPrise = true;
    for (list<Point>::iterator it = positions.begin(); it!= positions.end(); it++) {
        if (it->x() != emplacement.x()) {
            if (it->y() == emplacement.y()) {
                nonPrise = false;
            }
            if (abs(it->y() - emplacement.y()) == abs(it->x() - emplacement.x())) {
                nonPrise = false;
            }
        }
    }

    return nonPrise;
}

bool placerDame(int i) {
    bool bonnePosition = false;
    for (int j = 1; j <= LARGEUR_GRILLE && !bonnePosition; j++) {
        Point emplacement(i,j);
        positions.push_back(emplacement);
        if (verifierNonPrise(emplacement) && (i == LARGEUR_GRILLE || placerDame(i + 1))) {
            bonnePosition = true;
        }
        else {
            positions.pop_back();
        }
    }

    return bonnePosition;
}

int main()
{
    int i = 1;
    placerDame(i);
    for (list<Point>::iterator it = positions.begin(); it!= positions.end(); it++) {
        cout << "(" << it->x() << "; " << it->y() << ")" << endl;
    }
    return 0;
}

【问题讨论】:

  • 您是否在启用优化的情况下编译了您的 C++ 代码?对于 gcc 和 clang,在命令行中添加 -O3。
  • “我一直听说 C++ 比 Java 更高效”——因为通常是这样。
  • Java 击败 C++ 一点也不稀奇。与使用任何现成的 C++ 编译器/链接器相比,JITC 优化的 Java 程序可以得到更紧密的优化。例如,JITC 可以轻松地对 Point 进行内联操作,而 C++ 通常只有在函数声明为可内联时才能这样做。
  • @HotLicks 请注意,声明函数可内联实际上与它们是否内联无关。在这个例子中,Point 的方法是隐式内联的。但它们甚至不是必需的。数据应该是公开的。
  • @HotLicks 当然,但是 OP 的代码,一切都是内联的。性能损失很可能是使用std::list。至于 getter,如果内联,这些也会被优化,但使用它们可能会导致数据成员的额外副本被制作,因为从语义上它们返回值。无论如何,如果在这两种语言中正确编码,很难看出 Java 如何在如此简单的数字问题上胜过 C++。我也不认为 C++ 的性能会大大超过 Java。

标签: java c++ performance algorithm


【解决方案1】:

std::list 在 C++ 中是一个链表,而java.util.ArrayList 是一个数组。尝试将std::list 替换为std::vector。另外,请务必在打开优化的情况下进行编译。

【讨论】:

  • @realUser404 对我来说,在这种情况下通过引用传递速度较慢(我猜是因为Point 对象不是那么大)。另外,你用的是什么编译器?对我来说,在优化编译时,您的原始 C++ 代码已经比原始 Java 代码快 (g++-4.9 -O2)。
  • Point 结构中用 int 代替 double 可能会让编译器生成更好的代码。
  • @Jakub 好点。带有int 的Java 代码与带有double 的C++ 代码并不是一个公平的比较。
  • @Jakub 我的想法完全正确。错误的数据结构加上错误的数据类型。此外,为了帮助我们的朋友 Peter,我认为每种语言都有自己的 STL 怪癖。要变得流利,需要了解库和习语。
  • 您可能想详细说明std::list 结构对于该程序的效率如何低于std::vector。是否只是(双向链接)列表上的基本操作(例如迭代器的增量)需要比向量更多的周期,还是使用的东西从根本上来说对于列表来说更慢(例如通过索引访问元素) ?换句话说,这两种实现是否具有相同的渐近复杂度,它们之间只有一个常数因子?
【解决方案2】:

更新:

对 C++ 的更改

  • 如所写:
    编译失败
  • 替换 math.h => cmath
    27610 毫秒
  • 添加 -O3 标志
    4416 毫秒
  • 用 std::vector 替换 std::list
    2294 毫秒
  • 用 std::pair 替换 Point
    2384 毫秒
  • 使verifierNonPrise const 正确
    2351 毫秒
  • 将 verifierNonPrise 中的循环替换为 std::find_if
    929 毫秒
  • 将 double 替换为 int(使其等同于 Java)
    549 毫秒

对 Java 的更改

  • 书面
    3459 毫秒
  • 更改verifierNonPrise提前返回
    368 毫秒

Java 与 C++

> javac HuitDames.java
> time java HuitDames
real    0m0.368s
user    0m0.436s
sys     0m0.042s    
> g++ -O3 -std=c++11 HuitDames.cpp
> time ./a.out
real    0m0.541s
user    0m0.539s
sys     0m0.002s

最终代码:

#include <iostream>
#include <vector>
#include <cmath>
#include <stdlib.h>
#include <chrono>
#include <algorithm>

using namespace std;

typedef std::pair<int, int>   Point;

#define LARGEUR_GRILLE 22
vector<Point> positions;


bool verifierNonPrise(Point const& emplacement) {
    return std::find_if(positions.begin(), positions.end(), [&emplacement](Point const& val){
        if (val.first != emplacement.first) {
            if ((val.second == emplacement.second) || (abs(val.second - emplacement.second) == abs(val.first - emplacement.first))) {
                return true;
            }
        }
        return false;
    }) == positions.end();
}

bool placerDame(int i) {
    bool bonnePosition = false;

    for (int j = 1; j <= LARGEUR_GRILLE && !bonnePosition; j++) {
        Point emplacement(i,j);
        positions.push_back(emplacement);
        if (verifierNonPrise(emplacement) && (i == LARGEUR_GRILLE || placerDame(i + 1))) {
            bonnePosition = true;
        }
        else {
            positions.pop_back();
        }
    }

    return bonnePosition;
}


int main()
{
    using std::chrono::system_clock;

    system_clock::time_point begin_time = system_clock::now();

    int i = 1;
    placerDame(i);
    for (vector<Point>::iterator it = positions.begin(); it!= positions.end(); it++) {
        cout << "(" << it->first << "; " << it->second << ")" << endl;
    }

    system_clock::time_point end_time = system_clock::now();

    long long elapsed_milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - begin_time).count();
    cout << "Duration (milliseconds): "
         << elapsed_milliseconds
         << std::endl;    
}

【讨论】:

  • #include &lt;algorithm&gt; 和 #include &lt;chrono&gt; 丢失(没有它们,我的编译器无法编译)。此外, -O3 和 -Ofast 不会改变时间。有什么想法吗?
  • Replace std::list with std::vector、Replace Point with std::pair 和 Made verifierNonPrise const correct 的时间真的不同还是只是测量不精确?
  • @FlorianRichoux:-Ofast 启用允许非标准行为的优化,但在大多数情况下仍然表现合理(例如优化 aaaa 到 (aa)*(a*a)。这里根本没有任何情况可以带来可衡量的改进。
  • @Deduplicator:两者之间的速度差异(大部分)是一个神话。关于这个的大量文章。当您看到差异时,是因为您忘记致电 sync_with_stdio
  • @Deduplicator:输出量太小了,我不希望在速度上有任何可测量的差异。刚试了一下(没区别)。
【解决方案3】:

测试这个版本,使用 C++11 特性更新。在 GCC 4.9.0 中使用-std=c++11 进行了测试。在 Celeron 1.6 GHz、512 MB RAM 上测试。

我的电脑中的时间:
原始:持续时间(毫秒):12658
第一个版本:持续时间(毫秒):3616
优化版:持续时间(毫秒):1745

变化是:

  • 使用vector 代替list Benchmark 和Words from Stroustrup。
  • 尽我们所能使用 const,如果编译器知道值不会改变,它就能够进行更多优化。
  • 使用 std::pair 代替 Point。
  • 使用带有常量迭代器的新 for 循环。

来源:

#include <iostream>
#include <vector>
#include <chrono>
#include <iomanip>

using namespace std;

typedef std::pair<int, int> Point;

#define LARGEUR_GRILLE 22
vector<Point> positions;

bool verifierNonPrise(const Point& emplacement) {
    bool nonPrise = true;
    for (const auto& p : positions) {
        if (p.first != emplacement.first) {
            if (p.second == emplacement.second) {
                nonPrise = false;
            }
            if (abs(p.second - emplacement.second) ==
                abs(p.first - emplacement.first)) {
                nonPrise = false;
            }
        }
    }

    return nonPrise;
}

bool placerDame(int i) {
    bool bonnePosition = false;
    for (int j = 1; j <= LARGEUR_GRILLE && !bonnePosition; j++) {
        Point emplacement(i, j);
        positions.emplace_back(emplacement);
        if (verifierNonPrise(emplacement) &&
            (i == LARGEUR_GRILLE || placerDame(i + 1))) {
            bonnePosition = true;
        } else {
            positions.pop_back();
        }
    }

    return bonnePosition;
}

int main(int argc, char* argv[]) {
    std::chrono::system_clock::time_point begin_time =
        std::chrono::system_clock::now();

    positions.reserve(LARGEUR_GRILLE);

    placerDame(1);
    for (const auto& p : positions) {
        cout << "(" << p.first << "; " << p.second << ")" << endl;
    }

    std::chrono::system_clock::time_point end_time =
        std::chrono::system_clock::now();
    long long elapsed_milliseconds =
        std::chrono::duration_cast<std::chrono::milliseconds>(
            end_time - begin_time).count();
    std::cout << "Duration (milliseconds): " << elapsed_milliseconds
              << std::endl;

    return 0;
}

一些更深刻的变化。

变化包括:

  • 尽早返回。一旦女王不能放置。
  • 回到更简单的 Point 类。
  • 使用 find_if 算法搜索女王位置。

来源(更新了一些建议):

#include <algorithm>
#include <iostream>
#include <vector>
#include <chrono>
#include <iomanip>

using namespace std;

struct Point {
    int x, y;
};

#define LARGEUR_GRILLE 22
vector<Point> positions;

bool verifierNonPrise(const Point& emplacement) {
    return find_if(positions.cbegin(), positions.cend(), [&emplacement](const Point& p) {
               return (p.x != emplacement.x &&
                       (p.y == emplacement.y ||
                        abs(p.y - emplacement.y) == abs(p.x - emplacement.x)));
           }) == positions.cend();
}

bool placerDame(int i) {
    for (int j = 1; j <= LARGEUR_GRILLE; j++) {
        Point emplacement{i, j};
        positions.push_back(emplacement);
        if (verifierNonPrise(emplacement) &&
            (i == LARGEUR_GRILLE || placerDame(i + 1))) {
            return true;
        } else {
            positions.pop_back();
        }
    }
    return false;
}

int main(int argc, char* argv[]) {
    std::chrono::system_clock::time_point begin_time =
        std::chrono::system_clock::now();

    positions.reserve(LARGEUR_GRILLE);

    placerDame(1);
    for (const auto& p : positions) {
        cout << "(" << p.x << "; " << p.y << ")" << endl;
    }

    std::chrono::system_clock::time_point end_time =
        std::chrono::system_clock::now();
    long long elapsed_milliseconds =
        std::chrono::duration_cast<std::chrono::milliseconds>(
            end_time - begin_time).count();
    std::cout << "Duration (milliseconds): " << elapsed_milliseconds
              << std::endl;

    return 0;
}

【讨论】:

  • 然后呢?与原版相比表现如何?
  • 不知道为什么std::pair?从设计的角度来看,这绝对是您想要避免的事情。我也怀疑使用emplace_back 是否会产生重大影响;这是更精细的调整。 (使用std::vector 并通过引用传递无疑是最大的胜利。)
  • 在我的机器上原始 4296 毫秒。 NetVipeC 版本 682。可能需要进行更多改进,但已经有了很大的改进
  • @JamesKanze 是的,在这种情况下emplace_back 什么也得不到,我改变了因为认为方法placerDame 中的emplacement 变量仅用于插入vector 并撤消时看到对verifierNonPrise 的调用,但忘记了emplace_back。 std::pair 的原因是没有理由为 STD 覆盖的东西创建一个类,只是需要维护更多代码,需要测试更多代码,即使如此微不足道,以及 std::make_pair(在某些编译器中只2 种类型的一个分配),在这种情况下并不多,但创建 100 万对会有所不同。
  • @NetVipeC 没有编译器应该对std::vector 之外的任何内容进行任何分配。使用类会更干净,也不会更慢。或者因为你使用的是 C++11,一个带有列表初始化的结构。当然std::pair在这里是混淆的,因为成员不是第一和第二,而是x和y(语义明确); std::pair 便于快速实验,但在严肃的代码中使用它是一种反模式。
【解决方案4】:

将 Java 等托管的动态编译语言与 C++ 等静态编译语言进行比较非常困难。

在某种意义上,您总是会将苹果与橙子进行比较,因为它们在概念上非常不同。它首先使用标准库(ArrayList 与 std::list/vector),它们可能具有截然不同的性能特征,即使您的代码在两种语言中看起来都相似。

Java 中的微基准测试存在固有问题(Java 中的短测试总是较慢,因为JIT 会在决定编译什么以及如何编译之前观察程序流程)。 C++ 的编译器选项也是如此,即使是源代码的结构(独立编译和链接的类与单个文件源)也可以产生显着差异(因为它改变了 C++ 的“洞察”量编译器已进入其他类)。

接下来是内存管理、垃圾回收与手动内存管理(智能指针等仍被视为手动内存管理)的一般区别。

更不用说一般的语言差异,比如你需要在 C++ 中显式声明一个虚拟方法,而在 Java 中,每个成员方法默认都是虚拟的(在运行时确定它是否真的是虚拟的就剩下了到虚拟机)。

由于存在所有这些差异,总会有一种语言比另一种语言具有巨大优势的情况。一个范围非常有限的简单测试(如您在此处的测试)对每种语言的整体描述很少。

人们往往容易忽视的另一点是:您使用一门语言的效率如何 - 速度并不是一切(看看脚本语言在某些领域是多么成功,尽管仅从外观来看几乎没有竞争力

em> 以执行速度)。缺乏性能可能会造成严重后果,但生产力也会低下。

【讨论】:

  • 我认为问题的关键在于 OP 会(正确地)期望 C++ 在这种特殊情况下更快,并且惊讶地发现它要慢得多。这里没有苹果或橘子。
  • @juanchopanza 那么任务的标题应该是“如何改进这个 C++ 代码”,最好放在 codereview 上。虽然其他答案很好地涵盖了可能的优化,但它们都忽略了本质上无法比较的方面 - 并且建议的一些优化使结果更加无与伦比。一件正确的事情是将 C++ 版本更改为矢量,因为它更类似于 ArrayList。但其他一切都只是增加了两种实现之间的差异,它 将苹果与橙子进行比较。
  • @Durandal - 将点坐标从 double 更改为 int 使其更类似于 Java 示例。并且可以有很大的性能差异。
  • @Jakub 虽然我完全同意你的相同数据类型使其相似,但我一般的“苹果到橘子”是为了优化尝试,如“尽早返回。一旦女王不能放置。”甚至从根本上改变了逻辑。我一开始没有仔细检查 OP 的原始代码是否相似,所以我什至从未注意到原始 C++ 版本中存在这个错误。当他说完全相同的算法时,我只是相信没有这种根本的(和无动机的)差异。
  • @Durandal 我的意思是,您的“正确的一件事是将 C++ 版本更改为矢量,因为它更类似于 ArrayList。但其他一切都只是增加了两种实现之间的差异把苹果比作橘子。”并不完全正确。由于整数数据类型,Java 优化器可能会生成更好/更快的代码。答案/建议的解决方案之一包括此更改。我无意与您对一般优化和基准测试的评论进行争论。
【解决方案5】:

我可能在这里打了一匹死马,但只是将 Java 逐行转换为 C++,甚至不使用 const 引用参数或任何类似的东西,你可以看到 C++ 的速度几乎是 Java 的两倍。 如果有的话,所有的“语法优化”等等都没有什么效果......

rep ~/Documents $ g++ -O3 Queen.cpp
rep ~/Documents $ javac Queen.java 
rep ~/Documents $ time java Queen 
(1; 1)
(2; 3)
(3; 5)
(4; 2)
(5; 4)
(6; 10)
(7; 14)
(8; 17)
(9; 20)
(10; 13)
(11; 19)
(12; 22)
(13; 18)
(14; 8)
(15; 21)
(16; 12)
(17; 9)
(18; 6)
(19; 16)
(20; 7)
(21; 11)
(22; 15)

real    0m4.806s
user    0m4.857s
sys     0m0.067s
rep ~/Documents $ time ./a.out
(1; 1)
(2; 3)
(3; 5)
(4; 2)
(5; 4)
(6; 10)
(7; 14)
(8; 17)
(9; 20)
(10; 13)
(11; 19)
(12; 22)
(13; 18)
(14; 8)
(15; 21)
(16; 12)
(17; 9)
(18; 6)
(19; 16)
(20; 7)
(21; 11)
(22; 15)

real    0m2.131s
user    0m2.113s
sys     0m0.000s
rep ~/Documents $

Queen.java(翻译成英文)

import java.awt.Point;
import java.util.ArrayList;
import java.util.List;

public class Queen {

    private static List<Point> positions = new ArrayList<>();
    private static final int GRID_SIZE = 22;

    public static void main(String[] args) 
    {
        int i = 1;
        placeQueen(i);
        for (Point point : positions) 
        {
            System.out.println("(" + point.x + "; " + point.y + ")");
        }
    }

    private static boolean placeQueen(int i) 
    {
        boolean bIsGoodPos = false;
        for (int j = 1; j <= GRID_SIZE && bIsGoodPos == false; j++) 
        {
            Point emplacement = new Point(i, j);
            positions.add(emplacement);
            if (verifyPos(emplacement) && (i == GRID_SIZE || placeQueen(i + 1))) 
            {
                bIsGoodPos = true;
            }
            else 
            {
                positions.remove(i - 1);
            }
        }

        return bIsGoodPos;
    }

    private static boolean verifyPos(Point position) 
    {
        boolean bIsSafe = true;
        for (Point point : positions) 
        {
            if (!point.equals(position)) 
            {
                if (position.y == point.y) 
                {
                    bIsSafe = false;
                }
                if (Math.abs(position.y - point.y) == Math.abs(position.x - point.x)) 
                {
                    bIsSafe = false;
                }
            }
        }

        return bIsSafe;
    }
}

女王.cpp

#include <cmath>
#include <vector>
#include <iostream>

using namespace std;

struct Point
{
    int x, y;
    Point(int ii, int jj):x(ii), y(jj){}
};

vector<Point> positions;
int GRID_SIZE = 22;


bool verifyPos(Point position) 
{
    bool bIsSafe = true;
    for(int i = 0; i < positions.size(); ++i) 
    {
        Point point = positions[i];
        if(point.x != position.x || point.y != position.y) 
        {
            if(position.y == point.y) 
            {
                bIsSafe = false;
            }
            if(abs(position.y - point.y) == abs(position.x - point.x)) 
            {
                bIsSafe = false;
            }
        }
    }

    return bIsSafe;
}

bool placeQueen(int i) 
{
    bool bIsGoodPos = false;
    for(int j = 1; j <= GRID_SIZE && bIsGoodPos == false; j++) 
    {
        Point p(i, j);
        positions.push_back(p);
        if(verifyPos(p) && (i == GRID_SIZE || placeQueen(i + 1))) 
        {
            bIsGoodPos = true;
        }
        else 
        {
            positions.pop_back();
        }
    }
    return bIsGoodPos;
}


int main(void) 
{
    int i = 1;
    placeQueen(i);
    for(int i = 0; i < positions.size(); ++i) 
    {
        Point p = positions[i];
        cout << "(" << p.x << "; " << p.y << ")" << endl;
    }

    return 0;
}

【讨论】:

    【解决方案6】:

    如果您使用位图,C++ 可以在 21 毫秒内完成(在旧内核 i7-860 上)。对于计时运行,我注释掉了 showSoln() 调用,因为棋盘的图形显示所需的时间是找到解决方案的两倍。

    #include <iostream>
    #include <iomanip>
    #include <fstream>
    #include <omp.h>                        //omp_get_wtime() is my favorite time function
    using namespace std;
    
    static const unsigned n(22);            //size of board
    static_assert(n<32,"use long unsigned for bit masks if n > 32");
    static const unsigned mask((1U<<n)-1);  //n wide bitmask with every bit set
    
    void showSoln(unsigned* selCol, unsigned numSoln) {     //show a solution
        cout << "\nsolution " << numSoln << '\n';
        for (unsigned row=0; row<n; ++row) {
            for (unsigned col=0; col<n; ++col)
                cout << (col==selCol[row]? " Q": " .");
            cout << '\n';
        }
    }
    
    void main() {
        //for each row bitmasks that show what columns are attacked, 1 bit means attacked
        unsigned ulAttack[n];           //cols attacked from upper left, shift right for next row
        unsigned upAttack[n];           //cols attacked from straight up, same for next row
        unsigned urAttack[n];           //cols attacked from upper right, shift left for next row
        unsigned allAttack[n];          //OR of all attacks on given row
        allAttack[0]= ulAttack[0]= upAttack[0]= urAttack[0]= 0; //no attacks on row 0
        unsigned row= 0;                //the row where now placing a queen 
        unsigned selCol[n];             //for each row the selected column
        unsigned numSoln= 0;            //count of soutions found
        double wtime= omp_get_wtime();
        for (;;) {                                          //loop until find 1st (or all) solutions
            if (allAttack[row]!=mask) {                     //if 'row' has a column not attacked
                unsigned long bit;
                _BitScanForward(&bit,~allAttack[row]);      //find lowest column not attacked
                //note - your compiler may have a different intrinsic for find lowest set bit
                selCol[row]= bit;                           //remember selected column for this row
                unsigned move= 1U<<bit;                     //convert selected column to bitmask
                allAttack[row]|= move;                      //mark column attacked to prevent re-use
                if (row==n-1) {                             //if move in last row have a soln
                    ++numSoln;
                    showSoln(selCol,numSoln);
                    break;                                  //remove this break if want all solutions
                } else {                                    //no solution yet, fill in rows below
                    unsigned nrow= row+1;                   //next row
                    //from attacks on this row plus 'move' decide attacks on row below
                    ulAttack[nrow]= (ulAttack[row] | move) >> 1;
                    upAttack[nrow]= (upAttack[row] | move);
                    urAttack[nrow]= ((urAttack[row] | move) << 1) & mask;
                    allAttack[nrow]= ulAttack[nrow] | upAttack[nrow] | urAttack[nrow];
                    row= nrow;                              //go to next row
                }
            } else {                //else move on 'row' is impossible so backtrack
                if (!row)           //if backtrack from row 0 have found all solutions
                    break;
                --row;              //try next move in prior row
            }
        }
        wtime= omp_get_wtime() - wtime;
        cout << "numSoln= " << numSoln << '\n';
        cout << "time= " << wtime*1000 << " msec\n";
    }
    

    【讨论】:

      【解决方案7】:

      此外,没有理由对坐标使用浮点/双精度类型。

      如果您不在 C++ 中强制调用浮点 abs 库调用,您应该会获得性能

      Java 将 Point 坐标存储为整数。 get 函数返回双精度值,不过这在 Java 中可能更容易优化,然后在 c++ 中。

      【讨论】:

        【解决方案8】:

        对于不需要太多内存访问和密集计算的代码,Java 和 C++ 之间的差异似乎很小。但在相反的情况下,差异看起来很惊人:

        test.java

        import java.lang.String;
        import java.util.ArrayList;
        import java.util.List;
        import java.util.Random;
        import java.util.Scanner;
        
        public class test{
            private static Random gna=new Random();
        
            public static double new_value(double value){
                if (value<0.5) value=0;
                return value*value;
            }
        
            public static void main(String[] args) {
                long start_time = System.currentTimeMillis();   
                List<Double> ze_list=new ArrayList();
                for (int i=0;i<1e8;i++){
                    double temp=new_value(gna.nextDouble());
                    ze_list.add(temp);
                }
                long end_time = System.currentTimeMillis();
                System.out.println("Time (s) :"+ ((end_time-start_time)/1000));
                Scanner input = new Scanner(System.in);
                String inputval = input.next();
            }
        }
        

        并将其与 test.cpp 进行比较:

        #include <iostream>
        #include <vector>
        #include <ctime>
        #include <random>
        using namespace std;
        
        static default_random_engine dre1(time(0));
        static uniform_real_distribution <double> urd1(0, 1);
        
        static double new_value(double value){
            if (value<0.5) value=0;
            return value*value;
        }
        
        int main(void){
                time_t tbegin,tend;
                double texec=0;
                tbegin=time(NULL);
                vector<double> ze_list;
                for (int i=0;i<1e8;i++){
                    double temp=new_value(urd1(dre1));
                    ze_list.push_back(temp);
                }
                tend=time(NULL);
                texec=difftime(tend,tbegin);
                cout << "\nTime (s) " << texec << " s\n";
                int val=0;
                cin >> val;
            return 0;
        }
        

        我刚刚在我的 Mac 上测试了它:

        • Java 版本用了 90 年代,需要 3.77 Go
        • C++ 程序耗时 2 秒,仅需 770 个月

        也许有可能提高 Java 性能,但我不知道如何。

        【讨论】:

          【解决方案9】:

          Java 将对象作为引用传递给方法,这些引用是按值传递的,而 C++ 是按值传递对象的。

          您应该更改 C++ 代码以使其与 Java 相同(在 C++ 中传递指针而不是传递对象):

          bool verifierNonPrise(Point* emplacement) // The correct way for passing arguments.
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2010-12-04
            • 2011-04-16
            • 1970-01-01
            • 2016-04-09
            相关资源
            最近更新 更多