分而治之是否总能获得更好的表现?
Does Divide an Conquer always get better performance?
我目前正在测试一些分而治之的算法与它们的正常实现。我对此很陌生,我不确定在使用分而治之时是否应该始终获得更好的性能。例如,我已经实现了一种算法来按常规转置矩阵并使用分而治之,但我仍然使用第一个版本获得更好的性能。有可能还是我遗漏了一些重要的东西?
这是分而治之的代码
void trasponer_DyV(Matriz &matriz)
{
if (matriz.size() >= 2)
{
trasponer_DyV(matriz, 0, matriz.size(), 0, matriz.size());
}
}
void trasponer_DyV(Matriz &matriz, int fil_inicio, int fil_fin, int col_inicio, int col_fin)
{
int tam = fil_fin - fil_inicio;
if (tam == 1)
return;
trasponer_DyV(matriz,fil_inicio, fil_inicio + tam / 2,col_inicio, col_inicio + tam / 2);
trasponer_DyV(matriz, fil_inicio, fil_inicio + tam / 2, col_inicio + tam / 2, col_inicio + tam);
trasponer_DyV(matriz, fil_inicio + tam / 2, fil_inicio + tam, col_inicio, col_inicio + tam / 2);
trasponer_DyV(matriz, fil_inicio + tam / 2, fil_inicio + tam, col_inicio + tam / 2, col_inicio + tam);
for (int i = 0; i < tam / 2; i++)
{
for (int j = 0; j < tam / 2; j++)
swap(matriz[fil_inicio + i][col_inicio + tam / 2 + j], matriz[fil_inicio + tam / 2 + i][col_inicio + j]);
}
}
这是暴力破解:
Matriz trasponer_fuerzabruta(const Matriz &matriz)
{
Matriz ret;
ret.resize(matriz.size());
for (int i = 0; i < matriz.size(); ++i)
{
ret[i].resize(matriz.size());
}
// Todo lo que hacemos es sustituir filas por columnas.
for (int fila = 0; fila < matriz.size(); ++fila)
{
for (int columna = 0; columna < matriz.size(); ++columna)
{
ret[columna][fila] = matriz[fila][columna];
}
}
return ret;
}
提前致谢!
第一个函数预计性能更高,因为它不会进行任何额外的函数调用,这不是免费的。
恕我直言,如果出现以下情况,您将使用分而治之:
您可以并行使用多个处理器——使用线程或类似 MPI 的环境,或者
提高了函数的可读性(从而增强了可维护性),或者
更高级别的算法在概念上可以分为更小的、可能可重用的函数。
第一个版本做了更多工作 - 它就地转置片段,然后将它们交换到正确的位置。
第二个版本一次转置一个元素,但已经转置到最后位置。
此外,在顺序过程中,分而治之仅在工作集不适合 L3 缓存(8MB 或更多)时才有用,这相当于大小 >1000*1000 的矩阵。
尽管将其并行化(在 CPU 级别)也无益,因为矩阵转置是完全 DRAM 绑定的操作。
我目前正在测试一些分而治之的算法与它们的正常实现。我对此很陌生,我不确定在使用分而治之时是否应该始终获得更好的性能。例如,我已经实现了一种算法来按常规转置矩阵并使用分而治之,但我仍然使用第一个版本获得更好的性能。有可能还是我遗漏了一些重要的东西?
这是分而治之的代码
void trasponer_DyV(Matriz &matriz)
{
if (matriz.size() >= 2)
{
trasponer_DyV(matriz, 0, matriz.size(), 0, matriz.size());
}
}
void trasponer_DyV(Matriz &matriz, int fil_inicio, int fil_fin, int col_inicio, int col_fin)
{
int tam = fil_fin - fil_inicio;
if (tam == 1)
return;
trasponer_DyV(matriz,fil_inicio, fil_inicio + tam / 2,col_inicio, col_inicio + tam / 2);
trasponer_DyV(matriz, fil_inicio, fil_inicio + tam / 2, col_inicio + tam / 2, col_inicio + tam);
trasponer_DyV(matriz, fil_inicio + tam / 2, fil_inicio + tam, col_inicio, col_inicio + tam / 2);
trasponer_DyV(matriz, fil_inicio + tam / 2, fil_inicio + tam, col_inicio + tam / 2, col_inicio + tam);
for (int i = 0; i < tam / 2; i++)
{
for (int j = 0; j < tam / 2; j++)
swap(matriz[fil_inicio + i][col_inicio + tam / 2 + j], matriz[fil_inicio + tam / 2 + i][col_inicio + j]);
}
}
这是暴力破解:
Matriz trasponer_fuerzabruta(const Matriz &matriz)
{
Matriz ret;
ret.resize(matriz.size());
for (int i = 0; i < matriz.size(); ++i)
{
ret[i].resize(matriz.size());
}
// Todo lo que hacemos es sustituir filas por columnas.
for (int fila = 0; fila < matriz.size(); ++fila)
{
for (int columna = 0; columna < matriz.size(); ++columna)
{
ret[columna][fila] = matriz[fila][columna];
}
}
return ret;
}
提前致谢!
第一个函数预计性能更高,因为它不会进行任何额外的函数调用,这不是免费的。
恕我直言,如果出现以下情况,您将使用分而治之:
您可以并行使用多个处理器——使用线程或类似 MPI 的环境,或者
提高了函数的可读性(从而增强了可维护性),或者
更高级别的算法在概念上可以分为更小的、可能可重用的函数。
第一个版本做了更多工作 - 它就地转置片段,然后将它们交换到正确的位置。
第二个版本一次转置一个元素,但已经转置到最后位置。
此外,在顺序过程中,分而治之仅在工作集不适合 L3 缓存(8MB 或更多)时才有用,这相当于大小 >1000*1000 的矩阵。
尽管将其并行化(在 CPU 级别)也无益,因为矩阵转置是完全 DRAM 绑定的操作。