我正在使用C++、C#和Java实现Floyd-Warshall算法。在每种语言中,我都进行了顺序和并行实现。测试结果如下:
(流逝时间仅用于主功能和读取文件,变量初始化等不计入测量范围)
从这里下载源代码SourceCodes
C++
- IDE:Netbeans
- 编译器:MinGW-4.8.1
- 顺序时间:9.333000
- 并行时间:2.539000
- 使用
OpenMP
实现并行
如果
NumOfThreads
=1,则为顺序,否则为并行
变量
#define n 1000 /* Then number of nodes */
double dist[n][n];
void floyd_warshall(int NumOfThreads) {
int i, j, k;
omp_set_num_threads(NumOfThreads);
for (k = 0; k < n; ++k)
#pragma omp parallel for private(i,j)
for (i = 0; i < n; ++i)
for (j = 0; j < n; ++j)
if ((dist[i][k] * dist[k][j] != 0) && (i != j))
if ((dist[i][k] + dist[k][j] < dist[i][j]) || (dist[i][j] == 0))
dist[i][j] = dist[i][k] + dist[k][j]; }
Java
- IDE: Netbeans
- 编译器: Netbeans默认
- 顺序时间: 11.632
- 并行时间: 3.089
- -Xms512m -Xmx1024m
- 导入java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
Java变量
public final int numNodes =1000;
public final double[][] costs= new double[numNodes][numNodes] ;
我没有在这里放置Java代码,因为它正常工作(我想)
c#
- IDE:Visual Studio 2012
- 编译器:Visual Studio 2012默认
- 顺序时间:31.312
- 并行时间:8.920
- 使用System.Threading.Tasks;
变量
const int n = 1000;
static double[,] dist = new double[n, n];
并行代码
static void floyd_warshall(ParallelOptions pOp)
{
int k;
for (k = 0; k < n; ++k)
Parallel.For<int>(0, n, pOp, () => 0, (i, loop, j) =>
{
for (j = 0; j < n; ++j)
if ((dist[i, k] * dist[k, j] != 0) && (i != j))
if ((dist[i, k] + dist[k, j] < dist[i, j]) || (dist[i, j] == 0))
dist[i, j] = dist[i, k] + dist[k, j];
return 0;
}, (j) => { });
单引号
static void floyd_warshallSingle()
{
int i, j, k;
for (k = 0; k < n; ++k)
for (i = 0; i < n; ++i)
for (j = 0; j < n; ++j)
if ((dist[i,k] * dist[k,j] != 0) && (i != j))
if ((dist[i,k] + dist[k,j] < dist[i,j]) || (dist[i,j] == 0))
dist[i,j] = dist[i,k] + dist[k,j];
}
我的 C# 实现有什么问题?
所有使用同一个文件
现在我的问题是,为什么用 C# 解决这个算法需要更长的时间?Java 和 C++ 的运行时间几乎相同,但我认为我的 C# 实现有问题,因为 C# 和 C++ 之间的差异很奇怪!
请帮助我改进我的 C# 实现或指出一些原因。谢谢!
编辑1
我把数组改成了交错数组,结果如下:
- 顺序时间:19.22
- 并行时间:4.903
仍然存在 C# 与 C++ 或 Java 之间的巨大差异!有什么想法吗?
新变量
const int n = 1000;
static double[][] dist = new double[n][];
新代码:
static void floyd_warshallSingle()
{
int i, j, k;
for (k = 0; k < n; ++k)
for (i = 0; i < n; ++i)
for (j = 0; j < n; ++j)
if ((dist[i][k] * dist[k][j] != 0) && (i != j))
if ((dist[i][k] + dist[k][j] < dist[i][j]) || (dist[i][j] == 0))
dist[i][j] = dist[i][k] + dist[k][j];
}
static void floyd_warshall(ParallelOptions pOp)
{
int k;
for (k = 0; k < n; ++k)
Parallel.For<int>(0, n, pOp, () => 0, (i, loop, j) =>
{
for (j = 0; j < n; ++j)
if ((dist[i][k] * dist[k][j] != 0) && (i != j))
if ((dist[i][ k] + dist[k][j] < dist[i][ j]) || (dist[i][j] == 0))
dist[i][ j] = dist[i][k] + dist[k][j];
return 0;
}, (j) => { });
}
int[][]
(一个int[]
数组)和int[,]
(一个平坦的内存分配) - 前者通常被称为不规则数组。int[][]
在C#中显然比int[,]
更快只是意味着编译器/CLR在某个地方做得很糟糕。在线性访问的情况下,两者的速度应该大致相同,但如果您在不规则数组上进行随机访问,则会出现第二个缓存未命中,这可以避免。但是,不规则数组永远不可能比平坦数组更快。 - Vooif ((dist[i,k] * dist[k,j] != 0) && (i != j))
中,乘法总是会被执行。C++ 编译器(我不知道 Java)可能会生成代码来首先进行i != j
测试,这将更快。下面的if
语句也是如此:将(dist[i,j] == 0)
测试放在第一位。 - Jim Mischel