我有一个多维数组double[,] results
,其中每一列代表特定项目(例如汽车,房屋...)的价格时间序列。我想将每个时间序列的对数回报计算为Log(price_t / price_t1)其中t> t1。因此,我将为的每一列生成一个新的对数返回时间序列double[,] results
。如何在C#中以有效的方式完成此操作?数据量很大,我正在尝试以下解决方案:
for(int col = 1; col <= C; col++)
{
for(int row = 1; row <= R; row++)
{
ret = Math.Log(results[row+1;col]/ results[row;col])
}
}
其中C和R是中的列数和行数double[,] results
。这种解决方案运行速度很慢,效率似乎很低。任何建议如何更快地执行类似的计算?
我看到在像MATLAB这样的语言中,人们可以对代码进行矢量化处理,然后将原始矩阵除以另一矩阵,而后者又被一个元素滞后。然后取除法得到的整个矩阵的对数。它在C#中可行吗?
如果您的计算机具有多个内核,则可以轻松提高计算速度。为了自己尝试一下,我首先创建了以下功能:
Double[,] ComputeLogReturns(Double[,] data) {
var rows = data.GetLength(0);
var columns = data.GetLength(1);
var result = new Double[rows - 1, columns];
for (var row = 0; row < rows - 1; row += 1)
for (var column = 0; column < columns; column += 1)
result[row, column] = Math.Log(data[row + 1, column]/data[row, column]);
return result;
}
我使用1,000 x 1,000值的输入数组对该功能进行了基准测试。在我的计算机上,执行100次呼叫的时间约为3秒。
因为循环的主体可以并行执行,所以我重写了要使用的函数Parallel.For
:
Double[,] ComputeLogReturnsParallel(Double[,] data) {
var rows = data.GetLength(0);
var columns = data.GetLength(1);
var result = new Double[rows - 1, columns];
Parallel.For(0, rows - 1, row => {
for (var column = 0; column < columns; column += 1)
result[row, column] = Math.Log(data[row + 1, column]/data[row, column]);
});
return result;
}
在具有4个核心(8个逻辑核心)的计算机上,执行100个调用大约需要0.9秒。这快了3倍多,表明只有物理核而不是逻辑核才能够计算对数。
现代x86 CPU具有称为SSE的特殊指令,使您可以向量化某些计算。我希望MATLAB使用这些指令,并且可以解释为什么与您自己的C#代码相比,您在MATLAB中的性能要好得多。
为了测试SSE,我尝试了Yeppp!绑定到C#。该库可作为预发行版本在NuGet上使用,并具有对数函数。SSE指令仅适用于一维数组,因此我重写了基线函数:
Double[] ComputeLogReturns(Double[] data, Int32 rows, Int32 columns) {
var result = new Double[(rows - 1)*columns];
for (var row = 0; row < rows - 1; row += 1)
for (var column = 0; column < columns; column += 1)
result[row*columns + column] = Math.Log(data[(row + 1)*columns + column]/data[row*columns + column]);
return result;
}
在相同的输入和100次迭代的情况下,执行时间现在似乎少于3秒,这表明一维数组可能会稍微提高性能(但从逻辑上讲,除非是影响执行时间的额外参数检查,否则它不应这样做)。
使用Yeppp!该函数变为:
Double[] ComputeLogReturnsSse(Double[] data, Int32 rows, Int32 columns) {
var quotient = new Double[(rows - 1)*columns];
for (var row = 0; row < rows - 1; row += 1)
for (var column = 0; column < columns; column += 1)
quotient[row*columns + column] = data[(row + 1)*columns + column]/data[row*columns + column];
var result = new Double[(rows - 1)*columns];
Yeppp.Math.Log_V64f_V64f(quotient, 0, result, 0, quotient.Length);
return result;
}
我找不到使用Yeppp执行矢量化除法的函数!因此该划分是使用“常规”划分执行的。但是,我仍然希望对数是最昂贵的运算。最初的性能令人恐惧,迭代100次,耗时17秒,但后来我注意到Yeppp中存在问题!关于以32位进程运行时的不良性能。切换到64位可显着提高性能,从而导致大约1.3秒的执行时间。摆脱函数内部的两个数组分配(重复执行100次),将执行时间降低到大约0.7秒,这比并行实现要快。使用Parallel.For
进行乘法运算可以将执行时间降低到0.4秒左右。如果是耶普!有一种执行除法的方法(SSE拥有这种方法),您可能会得到更短的执行时间,也许会导致速度提高十倍。
根据我对SSE的实验,您应该能够实现相当大的性能改进。但是,如果这很重要,您可能应该注意精度。与.NET实现相比,SSE日志功能提供的结果可能略有不同。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句