可能有点晚了,但是我最近在使用 Android 智能手机扫描魔方并计算解决方案的魔方求解机器人时也遇到了这个问题,所以我会把我发现的东西放在这里。
有什么问题?
让我们首先讨论导致该性能问题的问题的实际位置。
之所以这么慢是因为 CoordCube 类,它看起来(非常简化)如下:
class CoordCube {
short[] pruneTables;
static {
/* compute and save ~50MB data in `pruneTables` */
}
}
基本上它的作用是将大量数据加载到快速求解过程所需的查找表中。一旦首次实例化此类,此加载将由 JVM 自动执行。这发生在line 159 中的Search.solution():
/* simplified code of solution() */
if (cube.isValid()) {
CoordCube c = new CoordCube(); // pruning tables will be loaded on this line
这也是为什么只要传递的多维数据集无效,此方法在可忽略不计的时间内执行的原因,因为它永远不会加载表。
可能的解决方案:
既然我们已经确定了问题的出处,接下来让我们关注如何解决它。
我提出了 3 种不同的方法,其中第一种可能是最简单的(但也是最慢的执行方式......),并且也用于我的应用程序中。另外两个只是关于如何进一步提高性能的想法。
方法一:
第一种也是最简单的方法是手动以LoadingActivity 的形式预加载查找表,ProgressBar 显示我们当前的进度。为此,我们首先希望能够准确地手动控制何时加载哪些表(当类首次实例化时不够精确),如下所示:
loadTable1() {
/* load pruning table number 1 */
}
为此,我编写了一些简单的实用程序here(代码太长,无法粘贴)。请务必查看我的说明,了解如何在您的应用中正确导入该代码。
此外,我们可能希望在后台进行加载,即在AsyncTask 中。这就是我在我的应用程序中所做的(PruneTableLoader 包含在上一个链接中):
private class LoadPruningTablesTask extends AsyncTask<Void, Void, Void> {
private PruneTableLoader tableLoader = new PruneTableLoader();
@Override
protected Void doInBackground(Void... params) {
/* load all tables if they are not already in RAM */
while (!tableLoader.loadingFinished()) { // while tables are left to load
tableLoader.loadNext(); // load next pruning table
publishProgress(); // increment `ProgressBar` by one
}
return null;
}
@Override
protected void onProgressUpdate(Void... values) {
super.onProgressUpdate(values);
/* increment `ProgressBar` by 1 */
}
}
当使用我的PruneTableLoader 时,在我的Samsung Galaxy S3 和250 MB RAM free 上加载所有表需要大约40s。 (相比之下,自动加载它们时需要well over 2min,而且通常会导致崩溃......)
考虑到它在 PC 上需要 < 1s,这听起来可能仍然很慢,但至少您只能这样做一次,因为 Android 会缓存静态变量,因此您不必在每次启动时加载它们应用程序。
方法 2:(未经测试)
我认为将修剪表保存在 file 或 database 中并从那里加载它们而不是总是重新计算它们会更快。不过我还没有测试过,它可能需要相当多的工作才能让保存和加载正常工作。 (也可能由于访问时间的原因它甚至不会更快)
方法 3:(未经测试)
嗯,最难也是几十年来工作成本最高的解决方案是,简单地在C 或C++ 中重写整个算法,然后通过JNI 在应用程序中调用它。 (Herbert Kociemba 据我所知,他的 C-sourcecode 还没有发布...)
这肯定是性能方面最快的解决方案。 (也适用于求解过程本身)
总而言之,方法 1 可能是一开始的最佳努力/收益最佳方法(对我来说也是如此),所以我建议你这样做,以防加载对于您的应用程序来说,时间并不是什么大问题。
不过,我对自己的表现并不完全满意,所以我可能会在以后尝试方法 2,甚至可能尝试方法 3。如果我这样做了,我会用我的结果更新这篇文章。