Ton*_*Nam 22 c# compression performance 7zip lzma
我发现7-zip很棒,我想在.net应用程序上使用它.我有一个10MB的文件(a.001),它需要:

2秒编码.
如果我能在c#上做同样的事情,那将会很好.我已经下载了http://www.7-zip.org/sdk.html LZMA SDK c#源代码.我基本上将CS目录复制到visual studio中的控制台应用程序中:

然后我编译和eveything编译顺利.所以在输出目录中我放置了a.00110MB大小的文件.关于我放置的源代码的主要方法:
[STAThread]
    static int Main(string[] args)
    {
        // e stands for encode
        args = "e a.001 output.7z".Split(' '); // added this line for debug
        try
        {
            return Main2(args);
        }
        catch (Exception e)
        {
            Console.WriteLine("{0} Caught exception #1.", e);
            // throw e;
            return 1;
        }
    }
当我执行控制台应用程序时,应用程序运行良好,我得到a.7z工作目录上的输出.问题是需要很长时间.执行大约需要15秒!我也试过/sf/answers/614314921/方法,这也需要很长时间.为什么它比实际程序慢10倍?
即使我设置只使用一个线程:

它仍然需要更少的时间(3秒对15):
可能是因为C#比汇编还是C慢?我注意到该算法执行了大量繁重的操作.例如,比较这两个代码块.他们都做同样的事情:
#include <time.h>
#include<stdio.h>
void main()
{
    time_t now; 
    int i,j,k,x;
    long counter ;
    counter = 0;
    now = time(NULL);
    /* LOOP  */
    for(x=0; x<10; x++)
    {
        counter = -1234567890 + x+2;
        for (j = 0; j < 10000; j++)     
            for(i = 0; i< 1000; i++)                
                for(k =0; k<1000; k++)
                {
                    if(counter > 10000)
                        counter = counter - 9999;
                    else
                        counter= counter +1;
                }
        printf (" %d  \n", time(NULL) - now); // display elapsed time
    }
    printf("counter = %d\n\n",counter); // display result of counter        
    printf ("Elapsed time = %d seconds ", time(NULL) - now);
    gets("Wait");
}
产量

static void Main(string[] args)
{       
    DateTime now;
    int i, j, k, x;
    long counter;
    counter = 0;
    now = DateTime.Now;
    /* LOOP  */
    for (x = 0; x < 10; x++)
    {
        counter = -1234567890 + x + 2;
        for (j = 0; j < 10000; j++)            
            for (i = 0; i < 1000; i++)                
                for (k = 0; k < 1000; k++)
                {
                    if (counter > 10000)
                        counter = counter - 9999;
                    else
                        counter = counter + 1;
                }
        Console.WriteLine((DateTime.Now - now).Seconds.ToString());            
    }
    Console.Write("counter = {0} \n", counter.ToString());
    Console.Write("Elapsed time = {0} seconds", DateTime.Now - now);
    Console.Read();
}
产量

注意c#慢多少.这两个程序都是在发布模式下从外部visual studio运行的.也许这就是为什么它在.net上花费的时间比在c ++上花费的时间长得多.
我似乎无法知道导致问题的原因.我想我会使用7z.dll并从c#中调用必要的方法.这样做的库位于:http://sevenzipsharp.codeplex.com/ ,这样我使用的是7zip使用的相同库:
    // dont forget to add reference to SevenZipSharp located on the link I provided
    static void Main(string[] args)
    {
        // load the dll
        SevenZip.SevenZipCompressor.SetLibraryPath(@"C:\Program Files (x86)\7-Zip\7z.dll");
        SevenZip.SevenZipCompressor compress = new SevenZip.SevenZipCompressor();
        compress.CompressDirectory("MyFolderToArchive", "output.7z");
    }
Bry*_*ner 11
我在代码上运行了一个分析器,最昂贵的操作似乎是在搜索匹配项.在C#中,它一次只搜索一个字节.LzBinTree.cs中有两个函数(GetMatches和Skip),它们包含以下代码片段,它在此代码上花费了40-60%的时间:
if (_bufferBase[pby1 + len] == _bufferBase[cur + len])
{
    while (++len != lenLimit)
        if (_bufferBase[pby1 + len] != _bufferBase[cur + len])
            break;
它基本上试图一次找到一个字节的匹配长度.我把它提取到自己的方法中:
if (GetMatchLength(lenLimit, cur, pby1, ref len))
{
如果您使用不安全的代码并将字节*转换为ulong*并一次比较8个字节而不是1,则我的测试数据的速度几乎翻倍(在64位进程中):
private bool GetMatchLength(UInt32 lenLimit, UInt32 cur, UInt32 pby1, ref UInt32 len)
{
    if (_bufferBase[pby1 + len] != _bufferBase[cur + len])
        return false;
    len++;
    // This method works with or without the following line, but with it,
    // it runs much much faster:
    GetMatchLengthUnsafe(lenLimit, cur, pby1, ref len);
    while (len != lenLimit
        && _bufferBase[pby1 + len] == _bufferBase[cur + len])
    {
        len++;
    }
    return true;
}
private unsafe void GetMatchLengthUnsafe(UInt32 lenLimit, UInt32 cur, UInt32 pby1, ref UInt32 len)
{
    const int size = sizeof(ulong);
    if (lenLimit < size)
        return;
    lenLimit -= size - 1;
    fixed (byte* p1 = &_bufferBase[cur])
    fixed (byte* p2 = &_bufferBase[pby1])
    {
        while (len < lenLimit)
        {
            if (*((ulong*)(p1 + len)) == *((ulong*)(p2 + len)))
            {
                len += size;
            }
            else
                return;
        }
    }
}
这种二进制算术和分支密集代码是C编译器喜欢的以及.NET JIT讨厌的东西..NET JIT不是一个非常智能的编译器.它针对快速编译进行了优化.如果微软想要调整它以获得最大性能,他们会插入VC++后端,但是故意不这样做.
另外,我可以通过7z.exe(6MB/s)来判断你使用多核的速度,可能是使用LZMA2.我的快速核心i7每个核心可以提供2MB/s,所以我猜7z.exe正在为你运行多线程.如果可能的话,尝试在7zip库中打开线程.
我建议您使用本机编译的库或使用调用7z.exe而不是使用托管代码LZMA算法Process.Start.后者应该让你很快就能获得好成绩.