我正在编写一些非常占用 CPU 的并发数字代码,这些代码将处理存储在 Java 数组中的大量数据(例如,大量 double [100000])。有些算法可能会在几天内运行数百万次,因此获得最大的稳态性能是当务之急。
本质上,每个算法都是一个 Java 对象,它有一个方法 API,例如:
public double[] runMyAlgorithm(double[] inputData);
或者可以将引用传递给数组以存储输出数据:
public runMyAlgorithm(double[] inputData, double[] outputData);
鉴于此要求,我正在尝试确定分配/管理数组空间的最佳策略。算法经常需要大量的临时存储空间。他们还将大型数组作为输入并创建大型数组作为输出。
我正在考虑的选项包括:
- 总是在需要时将新数组分配为局部变量(例如 new double[100000])。可能是最简单的方法,但会产生很多垃圾。
- 预分配临时数组并将它们作为最终字段存储在算法对象中 - 最大的缺点是这意味着在任何时候只有一个线程可以运行该算法。
- 将预先分配的临时数组保存在 ThreadLocal 存储中,以便线程可以在需要时使用固定数量的临时数组空间。 ThreadLocal 将是必需的,因为多个线程将同时运行相同的算法。
- 传递大量数组作为参数(包括供算法使用的临时数组)。不好,因为如果调用者必须负责提供临时数组空间,它会使算法 API 变得非常难看....
- 分配非常大的数组(例如 double[10000000]),但也为算法提供数组的偏移量,以便不同的线程将独立使用数组的不同区域。显然需要一些代码来管理数组范围的偏移量和分配。
关于哪种方法最好(以及为什么)有什么想法吗?
最佳答案
我在 Java 中处理内存时注意到以下内容。如果您的内存需求模式很简单(主要是 2-3 种类型的内存分配),您通常可以比默认分配器更好。您可以在应用程序启动时预先分配一个缓冲区池并根据需要使用它们,或者转到其他路线(在开始时分配一个巨大的数组并在需要时提供其中的一部分)。实际上,您正在编写自己的内存分配器。但很可能你会比 Java 的默认分配器做得更差。
我可能会尝试执行以下操作:标准化缓冲区大小并正常分配。这样过了一会儿,唯一的内存分配/释放将是固定大小,这将极大地帮助垃圾收集器快速运行。我要做的另一件事是在算法设计时确保任何一点所需的总内存不会超过机器内存的 80-85%,以免无意中触发完整收集。
除了这些启发式之外,我可能会测试我选择的任何解决方案,看看它在实践中是如何工作的。
关于java - 优化大型 Java 数据数组的处理和管理,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4633859/