24

7-zipは素晴らしいと思いました。これを、.netアプリケーションで使用したいと思います。10MBのファイル(a.001)があり、次のようになります。

ここに画像の説明を入力してください

エンコードするのに2秒

これで、c#でも同じことができると便利です。http://www.7-zip.org/sdk.html LZMA SDK c#ソースコードをダウンロードしました。基本的に、CSディレクトリをVisualStudioのコンソールアプリケーションにコピーしました。 ここに画像の説明を入力してください

それから私はコンパイルし、すべてがスムーズにコンパイルされました。そのため、出力ディレクトリに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秒かかります!https://stackoverflow.com/a/8775927/637142アプローチも試しましたが、非常に時間がかかります。実際のプログラムの10倍遅いのはなぜですか?

また

1つのスレッドのみを使用するように設定した場合でも: ここに画像の説明を入力してください

それでもはるかに短い時間で済みます(3秒対15):


(編集)別の可能性

C#がアセンブリまたはCよりも遅いためでしょうか?アルゴリズムが多くの重い操作を行うことに気づきました。たとえば、これら2つのコードブロックを比較します。それらは両方とも同じことをします:

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");
}

出力

ここに画像の説明を入力してください

c#

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#がどれだけ遅いかに注意してください。リリースモードで外部のビジュアルスタジオから実行される両方のプログラム。たぶんそれが、C++よりも.netの方がはるかに長い時間がかかる理由です。

また、同じ結果が得られました。C#は、先ほど示した例と同じように3倍遅くなりました。


結論

何が問題を引き起こしているのかわからないようです。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");


    }
4

6 に答える 6

11

コードでプロファイラーを実行しましたが、最もコストのかかる操作は一致するものを検索することであるようです。C#では、一度に1バイトを検索します。LzBinTree.csには次のコードスニペットを含む2つの関数(GetMatchesとSkip)があり、このコードに40〜60%の時間を費やしています。

if (_bufferBase[pby1 + len] == _bufferBase[cur + len])
{
    while (++len != lenLimit)
        if (_bufferBase[pby1 + len] != _bufferBase[cur + len])
            break;

基本的に、一度に1バイトずつ一致する長さを見つけようとします。私はそれを独自の方法に抽出しました:

if (GetMatchLength(lenLimit, cur, pby1, ref len))
{

また、安全でないコードを使用してバイト*をulong *にキャストし、一度に1バイトではなく8バイトを比較すると、テストデータの速度はほぼ2倍になります(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;
        }
    }
}
于 2013-07-11T16:14:14.153 に答える
9

この種のバイナリ算術および分岐の多いコードは、Cコンパイラが気に入っているものであり、.NETJITが嫌っているものです。.NETJITはあまりスマートなコンパイラではありません。高速コンパイル用に最適化されています。Microsoftが最大のパフォーマンスを実現するように調整したい場合は、VC ++バックエンドをプラグインしますが、意図的にプラグインしません。

また、7z.exe(6MB / s)で得られる速度から、おそらくLZMA2を使用して複数のコアを使用していることがわかります。私の高速コアi7はコアあたり2MB/秒を提供できるので、7z.exeはマルチスレッドで実行されていると思います。可能であれば、7zipライブラリでスレッドをオンにしてみてください。

マネージコードLZMAアルゴリズムを使用する代わりに、ネイティブにコンパイルされたライブラリを使用するか、を使用して7z.exeを呼び出すことをお勧めしProcess.Startます。後者の方が、すぐに始めて良い結果が得られるはずです。

于 2012-09-13T19:10:24.927 に答える
3

もう1つの方法は、SevenZipSharp(NuGetで入手可能)を使用して、7z.dllをポイントすることです。次に、速度はほぼ同じになるはずです。

var libPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "7-zip", "7z.dll");
SevenZip.SevenZipCompressor.SetLibraryPath(libPath);
SevenZip.SevenZipCompressor compressor = new SevenZipCompressor();
compressor.CompressFiles(compressedFile, new string[] { sourceFile });
于 2012-09-14T21:42:45.333 に答える
3

私自身はLZMASDKを使用していませんが、デフォルトでは7-zipが多くのスレッドでほとんどの操作を実行していると確信しています。私は自分でそれを行っていないので、私が提案する唯一のことは、それが多くのスレッドを使用するように強制することが可能かどうかをチェックすることです(デフォルトで使用されていない場合)。

編集:


スレッド化は(唯一の)パフォーマンス関連の問題ではないように思われるので、私が考えることができる他の問題があります:

  1. 7-zip UIを使用するときに設定するのとまったく同じオプションを設定したことを確認しましたか?出力ファイルは同じサイズですか?そうでない場合、一方の圧縮方法がもう一方の圧縮方法よりもはるかに高速である可能性があります。

  2. VSを使用せずにアプリケーションを実行していますか?もしそうなら-これもオーバーヘッドを追加する可能性があります(しかし、アプリの実行速度が5倍遅くなることはないと思います)。

  3. ファイルを圧縮する前に他の操作が行われていますか?
于 2012-09-06T03:45:37.070 に答える
3

LZMA CSの実装を見たところ、すべてマネージコードで実行されています。私の現在のプロジェクトの圧縮要件について最近調査を行ったところ、マネージコードでの圧縮のほとんどの実装は、ネイティブよりも効率が悪いようです。

これが問題の原因であると推測できます。別の圧縮ツールであるQuickLZのパフォーマンス表を見ると、ネイティブコードとマネージコード(C#またはJava)のパフォーマンスの違いがわかります。

2つのオプションが思い浮かびます。.NETの相互運用機能を使用してネイティブ圧縮メソッドを呼び出すか、圧縮サイズを犠牲にする余裕がある場合は、http://www.quicklz.com/を参照してください

于 2012-09-13T18:44:47.027 に答える
2

.netランタイムは、ネイティブ命令よりも低速です。cで問題が発生した場合、通常、ブルースクリーンでアプリケーションがクラッシュします。しかし、c#では、cで行わないチェックはすべて、実際にはc#に追加されるため、そうではありません。nullの追加チェックを行わないと、ランタイムはnullポインター例外をキャッチできません。インデックスと長さをチェックしないと、ランタイムは範囲外の例外をキャッチできません。

これらは、.netランタイムを遅くするすべての命令の前の暗黙の命令です。通常のビジネスアプリでは、ビジネスの複雑さとUIロジックがより重要であるパフォーマンスを気にしません。そのため、.netランタイムは、問題をすばやくデバッグして解決できるように、すべての命令を細心の注意を払って保護します。

ネイティブcプログラムは、常に.netランタイムよりも高速ですが、デバッグが難しく、正しいコードを作成するにはcに関する深い知識が必要です。cはすべてを実行しますが、何が悪かったのかについての例外や手がかりを与えることはありません。

于 2012-09-14T20:23:57.763 に答える