可能有点晚了,但是我最近在使用 Android 智能手机扫描魔方并计算解决方案的魔方求解机器人时也遇到了这个问题,所以我将在这里放置什么我发现了。
问题是什么?
让我们从讨论导致该性能问题的问题的实际位置开始。
这么慢的原因是 class 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 */
}
为此,我在这里编写了一些简单的实用程序(代码太长,无法粘贴)。请务必查看我的说明,了解如何在您的应用程序中正确导入该代码。
此外,我们可能希望在后台进行加载,即在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 */
}
}
在使用 myPruneTableLoader
时,所有表的加载都需要40s
在 mySamsung Galaxy S3
上使用250 MB RAM free
。(相比之下,well over 2min
自动加载它们时需要它,而且通常会导致崩溃......)
考虑到它在 PC 上的需要,这听起来可能仍然很慢< 1s
,但至少您只能这样做一次,因为 Android 会缓存静态变量,因此您不必在每次启动应用程序时加载它们。
方法2:(未经测试)
我认为将修剪表保存在 afile
或 a中database
并从那里加载它们而不是总是重新计算它们会更快。我还没有测试过,它可能需要相当多的工作才能让保存和加载正常工作。(也可能由于访问时间的原因它甚至不会更快)
方法3:(未经测试)
嗯,最难也是几十年来工作成本最高的解决方案是,简单地重写整个算法,C
或者C++
在应用程序中通过JNI
. (据我所知,Herbert Kociemba还没有发表他的作品……)C-sourcecode
这肯定是性能方面最快的解决方案。(也适用于求解过程本身)
总而言之,方法 1可能是一开始就努力/受益的最佳方法(对我来说也是如此),所以我建议您使用它,以防加载时间对您的应用程序来说不是那么大的问题.
不过,我对自己的表现并不完全满意,所以我可能会尝试方法 2,甚至可能在未来的某个时间尝试方法 3 。如果我这样做,我会用我的结果更新这篇文章。