我发现7-zip很棒,我想在.net应用程序上使用它。我有一个10MB的文件(a.001),需要:
编码 2秒。
如果我能在c#上做同样的事情,那将会很好。我已经下载了http://www.7-zip.org/sdk.html LZMA SDK c#源代码。我基本上将CS目录复制到visual studio中的控制台应用程序中:
然后我编译并顺利编译了eveything。所以在输出目录中我放置了10MB大小的文件a.001
。关于我放置的源代码的主要方法:
[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倍?
即使我设置只使用一个线程:
它仍然需要更少的时间(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");
}
答案 0 :(得分: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;
}
}
}
答案 1 :(得分:7)
这种二进制算术和分支密码是C编译器喜欢的以及.NET JIT讨厌的东西。 .NET JIT不是一个非常智能的编译器。它针对快速编译进行了优化。如果微软想要调整它以获得最大性能,他们会插入VC ++后端,但是故意不这样做。
此外,我可以通过7z.exe(6MB / s)获得您使用多核的速度,可能使用LZMA2。我的快速核心i7每个核心可以提供2MB / s,所以我猜7z.exe正在为你运行多线程。如果可能的话,尝试在7zip库中打开线程。
我建议您使用本机编译的库或使用Process.Start
调用7z.exe,而不是使用托管代码LZMA算法。后者应该让你很快就能获得好成绩。
答案 2 :(得分:3)
我自己没有使用过LZMA SDK,但我很确定默认情况下7-zip正在许多线程上运行大部分操作。由于我自己没有这样做,我唯一建议的是检查是否可以强制它使用多个线程(如果默认情况下不使用它)。
编辑:
<小时/> 由于线程似乎不是(唯一的)与性能相关的问题,因此我可以想到其他问题:
您是否检查过使用7-zip UI时设置的选项与设置的选项完全相同?输出文件的大小是否相同?如果不是 - 可能会发生一种压缩方法比另一种压缩方法快得多。
您是否正在使用VS执行您的应用程序?如果是这样的话 - 这可能会增加一些开销(但我想它不会导致应用程序运行速度慢5倍)。
答案 3 :(得分:3)
我刚刚看了一下LZMA CS实现,它都是在托管代码中执行的。最近对我当前项目的压缩要求进行了一些调查,大多数托管代码压缩实现的效率似乎低于原生代。
我只能假设这是问题的原因。如果您查看另一个压缩工具QuickLZ的性能表,您可以看到本机代码和托管代码(无论是C#还是Java)之间的性能差异。
我想到了两个选项:使用.NET的互操作工具来调用本机压缩方法,或者如果您能够牺牲压缩大小,请查看http://www.quicklz.com/。
答案 4 :(得分:3)
另一种方法是使用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 });
答案 5 :(得分:2)
.net运行时比本机指令慢。如果c中出现问题,我们通常会遇到蓝屏死机的应用程序崩溃。但是在c#中它没有,因为我们没有在c中做出的任何检查实际上是在c#中添加的。如果不对null进行额外检查,运行时永远不会捕获空指针异常。在不检查索引和长度的情况下,运行时永远不会超出范围异常。
这些是在使.net运行时变慢的每条指令之前的隐式指令。在典型的业务应用程序中,我们不关心性能,业务和ui逻辑的兼容性更重要,这就是为什么.net运行时保护每条指令都非常谨慎,让我们可以快速调试和解决问题。
本机c程序总是比.net运行时更快,但它们很难调试,需要深入了解c来编写正确的代码。因为c会执行所有操作,但不会给你任何例外或线索,说明出了什么问题。