如何高效计算log-returns
How to efficiently calculate log-returns
我有一个多维数组 double[,] results
,其中每一列代表特定项目(例如汽车、房屋...)的价格时间序列。我想将每个时间序列的 log-returns 计算为
日志(price_t / price_t1)
其中 t>t1。所以我会为double[,] results
的每一列生成一个新的log-returns的时间序列。
如何以有效的方式在 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 执行矢量化除法的函数!所以使用 "normal" 除法进行除法。但是,我仍然希望对数是最昂贵的操作。最初性能很糟糕,100 次迭代需要 17 秒,但后来我注意到 Yeppp 中存在问题!关于 运行 作为 32 位进程时性能不佳的问题。切换到 64 位显着提高了性能,导致大约 1.3 秒的执行时间。摆脱函数内部的两个数组分配(重复 100 次)将执行时间降低到大约 0.7 秒,这比并行实现更快。使用 Parallel.For
进行乘法运算将执行时间降低到大约 0.4 秒。如果耶普!如果有一种执行除法的方法(SSE 有),您可能会获得更短的执行时间,可能会导致速度提高十倍。
根据我对 SSE 的实验,您应该能够实现相当大的性能改进。但是,如果这很重要,您可能应该注意精度。与 .NET 实现相比,SSE 日志函数可能提供略有不同的结果。
我有一个多维数组 double[,] results
,其中每一列代表特定项目(例如汽车、房屋...)的价格时间序列。我想将每个时间序列的 log-returns 计算为
日志(price_t / price_t1)
其中 t>t1。所以我会为double[,] results
的每一列生成一个新的log-returns的时间序列。
如何以有效的方式在 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 执行矢量化除法的函数!所以使用 "normal" 除法进行除法。但是,我仍然希望对数是最昂贵的操作。最初性能很糟糕,100 次迭代需要 17 秒,但后来我注意到 Yeppp 中存在问题!关于 运行 作为 32 位进程时性能不佳的问题。切换到 64 位显着提高了性能,导致大约 1.3 秒的执行时间。摆脱函数内部的两个数组分配(重复 100 次)将执行时间降低到大约 0.7 秒,这比并行实现更快。使用 Parallel.For
进行乘法运算将执行时间降低到大约 0.4 秒。如果耶普!如果有一种执行除法的方法(SSE 有),您可能会获得更短的执行时间,可能会导致速度提高十倍。
根据我对 SSE 的实验,您应该能够实现相当大的性能改进。但是,如果这很重要,您可能应该注意精度。与 .NET 实现相比,SSE 日志函数可能提供略有不同的结果。