如何使我的C#代码更快地计算点积

时间:2017-02-07 16:05:57

标签: c# python numpy

我试图在C#中实现神经网络和深度学习代码。我的教科书中的示例代码是用Python编写的,因此我尝试将它们转换为C#。

我的问题是用numpy计算点积比我从头编写的C#码快得多。

虽然我的numpy代码需要几秒钟来计算点积1000次,但我的C#代码需要比它长得多。

这是我的问题。如何让我的C#代码更快?

这是numpy代码:

C:\temp>more dot.py
from datetime import datetime

import numpy as np

W = np.random.randn(784, 100)
x = np.random.randn(100, 784)

print(datetime.now().strftime("%Y/%m/%d %H:%M:%S"))

for i in range(0,1000):
    np.dot(x, W)

print(datetime.now().strftime("%Y/%m/%d %H:%M:%S"))

C:\temp>\Python35\python.exe dot.py
2017/02/08 00:49:14
2017/02/08 00:49:16
C:\temp>

这是C#代码:

public static double[,] dot(double[,] a, double[,] b)
{
    double[,] dot = new double[a0, b1];

    for (int i = 0; i < a.GetLength(0); i++)
    {
        for (int j = 0; j < b.GetLength(1); j++)
        {
            // the next loop looks way slow according to the profiler
            for (int k = 0; k < b.GetLength(0); k++)
                dot[i, j] += a[i, k] * b[k, j];
        }
    }
    return dot;
}

static void Main(string[] args)
{
    // compatible function with np.random.randn()
    double[,] W = random_randn(784, 100);
    double[,] x = random_randn(100, 784);

    Console.WriteLine(DateTime.Now.ToString("F"));
    for (int i = 0; i < 1000; i++)
        dot(W, x);
    Console.WriteLine(DateTime.Now.ToString("F"));
}

此致

4 个答案:

答案 0 :(得分:2)

使用BLAS对Numpy进行极端优化。使用自己的代码可能无法获得如此出色的性能。

点积虽然可以很好地并行化。您可以考虑使用多线程,但说实话,它不值得付出努力。只需找一个为您实现点积的库并使用它!

答案 1 :(得分:2)

您的代码正在进行矩阵乘法。存在用于进行矩阵乘法的快速算法,并且你正在做的是非常慢的O(n ^ 3)[技术上基于列/行长度的O(n * m ^ 2)]。另外,每次分配内存都不是一个好主意。

您的资源:

顺便提一下,如果您想要针对此类事情的桌面性能的最新技术,您可能需要查看CUDA:https://en.wikipedia.org/wiki/CUDA

答案 2 :(得分:1)

让您的C#代码与python代码一样:知道您的语言何时无法与大型犬保持同步,并且当发生这种情况时,请调用常驻BLAS子系统中的本机代码以实现高性能并行原生优化矩阵数学运算。

常驻BLAS子系统由标准API包装。您的C#代码将调用API,但不会知道 - 不知道是件好事! - 当前在主机上安装了哪个特定的BLAS子系统。

我喜欢OpenBLAS。其他人喜欢英特尔MKL(?)。还有一些人喜欢ATLAS。我讨厌ATLAS。

答案 3 :(得分:1)

如果您需要实用的解决方案 - 请使用现有的库。

如果您是出于娱乐/教育目的而这样做:

  • 消除来自最内层循环(GetLength)的所有函数调用 - 任何函数调用都不能被缓存并导致显着减速。外环可以从相同的优化中受益,但不会带来显着的好处。

  • 首先尝试转置第二个矩阵,以便内部循环访问两个数组的顺序元素。

  • 尝试使用数组而不是2d数组。

  • 当使用数组的arras时,尝试在内部循环中使用Length - 这可能会消除至少一个数组的边界检查
  • 尝试将最外层循环与Parallel.Foreach
  • 并行化
  • 如果实际问题需要多个非方形矩阵的乘法 - https://en.wikipedia.org/wiki/Matrix_chain_multiplication

还可以使用Stopwatch来衡量时间 - Exact time measurement for performance testing