Java:微优化数组操作
我正在尝试制作一个简单的前馈神经网络的Java端口。Java:微优化数组操作,java,performance,optimization,neural-network,micro-optimization,Java,Performance,Optimization,Neural Network,Micro Optimization,我正在尝试制作一个简单的前馈神经网络的Java端口。 这显然涉及到大量的数值计算,所以我正在尽可能优化我的中心循环。结果应在float数据类型的限制范围内正确 我的当前代码如下所示(已删除错误处理和初始化): /** *前馈神经网络的简单实现。网络支持 *包括一个恒定输出为1.0的偏置神经元和加权突触 *隐藏和输出图层。 * *@作者Martin Wiboe */ 公共类前馈网络{ private final int outputNeurons;//输出层中的神经元数 私有final int
这显然涉及到大量的数值计算,所以我正在尽可能优化我的中心循环。结果应在
float
数据类型的限制范围内正确
我的当前代码如下所示(已删除错误处理和初始化):
/**
*前馈神经网络的简单实现。网络支持
*包括一个恒定输出为1.0的偏置神经元和加权突触
*隐藏和输出图层。
*
*@作者Martin Wiboe
*/
公共类前馈网络{
private final int outputNeurons;//输出层中的神经元数
私有final int inputNeurons;//输入层中的神经元数
private int largestLayerNeurons;//最大层中的神经元数量
私有final int numberLayers;//层数
private final int[]neuronCounts;//每层神经元计数,输入0
//层。
私有最终浮点[][]fWeights;//神经元之间的权重。
//fWeight[fromLayer][fromNeuron][toNeuron]
//fromNeuron的重量是多少
//从图层到图层中的toNeuron
//fromLayer+1。
私有浮点[][]输出;//上一层输出的临时存储
公共浮点[]计算(浮点[]输入){
//将输入值复制到输入层输出
对于(int i=0;i
我使用-server选项运行JVM,到目前为止,我的代码比类似的C代码慢25%到50%。我能做些什么来改善这种情况
谢谢,
马丁·维博
编辑#1:在看到大量的回应之后,我可能应该澄清我们场景中的数字。在典型运行期间,将使用不同的输入调用该方法大约50000次。一个典型的网络将有numberLayers=3层,分别有190个、2个和1个神经元。因此,最里面的循环将有大约2*191+3=385次迭代(当计算第0层和第1层中添加的偏差神经元时)
编辑#1:在这个线程中实现了各种建议之后,我们的实现速度实际上与C版本一样快(在~2%以内)。谢谢你的帮助!所有的建议都很有帮助,但由于我只能将一个答案标记为正确答案,因此我将把它交给@Durandal,以建议数组优化,并作为唯一一个预先计算for
循环头的人。我要研究的第一件事是看Math.exp
是否会让你慢下来。请参阅以获取本机替代方案。首先,不要这样做:
// Copy input values to input layer output
for (int i = 0; i < inputNeurons; i++) {
neuronOutput[0][i] = input[i];
}
纯粹基于代码检查,您最内部的循环必须计算对三维参数的引用,并且需要做很多工作。根据您的阵列尺寸,您可能会因为每次循环迭代都必须在内存中跳转而出现缓存问题。也许您可以重新排列维度,以便内部循环尝试访问比现在更接近的内存元素
在任何情况下,在进行任何更改之前,请先评测代码,看看真正的瓶颈在哪里。我建议使用定点系统,而不是浮点系统。在几乎所有使用int的处理器上都比float快。实现这一点的最简单方法是将所有剩余的数据移位一定量(4或5是良好的起点),并将底部的4位视为十进制
您最内部的循环正在进行浮点运算,因此这可能会给您带来很大的提升。优化的关键是首先测量所花费的时间。通过调用System.nanoTime()围绕算法的各个部分:
我想使用System.arraycopy()会有所帮助,但您会在内部循环中发现实际成本
根据您所发现的,可以考虑用整数运算替换浮点运算。 一些提示
- 在最内部的循环中,考虑如何遍历CPU缓存并重新排列矩阵,以便按顺序访问最外部的阵列。这将导致您按顺序访问缓存,而不是到处乱跳。缓存命中可以比缓存未命中快两个数量级。
e、 g重组权重,使其作为
激活+=神经元输出[第1层][输入神经元]*重量[第1层][神经元][输入神经元]
- 不要在内部执行工作
// Copy input values to input layer output
for (int i = 0; i < inputNeurons; i++) {
neuronOutput[0][i] = input[i];
}
System.arraycopy( input, 0, neuronOutput[0], 0, inputNeurons );
long start_time = System.nanoTime();
doStuff();
long time_taken = System.nanoTime() - start_time;
final int[] neuronOutputSlice = neuronOutput[layer - 1];
final int[][] fWeightSlice = fWeights[layer - 1];
for (int inputNeuron = 0; inputNeuron < biasNeuron; inputNeuron++) {
activation += neuronOutputSlice[inputNeuron] * fWeightsSlice[inputNeuron][neuron];
}
for (int neuron = 0; neuron < neuronCounts[layer]; neuron++) { ... }
// transform to precalculated exit condition (move invariant array access outside loop)
for (int neuron = 0, neuronCount = neuronCounts[layer]; neuron < neuronCount; neuron++) { ... }
float activation = 1.0F * fWeights[layer - 1][biasNeuron][neuron];