大家好,我是Oleksandr Karpov,这个是我第一次发表文章,希望大家喜欢。
在这我将为大家展示和介绍怎么样在C#和.NET下使用汇编秒速拷贝数据,在我是实例里面我用了一运用程序创建了一段视频,里面包含图片,视频和声音。
当然如果你也需要在C#使用汇编的情况,这方法给你提供一个快速简单的解决途径。
背景
理解本文的内容, 最好具备以下知识: 汇编语言,?内存对齐,?c#, windows?和 .net 高级技巧(advanced techniques).
?要提高数据复制(copy-past )的速度, 我们需要将内存地址按 16 个字节对齐. 否则, 速度不会有明显的改变. (我的例子大概快?1.02 倍?)
?
Pentium III+ (KNI/MMX2) 和?AMD Athlon (AMD EMMX) 这两种处理器都支持本文代码用到 SSE 指令集.
我用配置为: Pentium Dual-Core E5800 3.2GHz,?4GB RAM 双通道内存的计算机做测试, 16 个字节内存对齐的速度要比标准方式快 1.5 倍, 而非内存对齐方式的速度几乎没有变化(1.02倍).
这是一个完整的演示测试,向你展示了性能测试以及如何使用。
FastMemCopy ? 类包含了用于快速内存拷贝逻辑的所有内容。
首先你需要创建一个默认的Windows Forms应用程序工程,在窗体上放两个按钮,一个PictureBox 控件,因为我们将用图片来测试。
声明几个字段先:
string bitmapPath;
Bitmap bmp, bmp2;
BitmapData bmpd, bmpd2;
byte[] buffer = null;
现在创建两个方法用来处理按钮的点击事件。
标准方法如下:
private void btnStandard_Click(object sender, EventArgs e)
{
? ? ? ? using (OpenFileDialog ofd = new OpenFileDialog())
? ? ? ? {
? ? ? ? ? ? if (ofd.ShowDialog() != System.Windows.Forms.DialogResult.OK)
? ? ? ? ? ? ? ? return;
?
? ? ? ? ? ? bitmapPath = ofd.FileName;
? ? ? ? }
?
? //open a selected image and create an empty image with the same size
? ? ? ? OpenImage();
?
? //unlock for read and write images
? ? ? ? UnlockBitmap();
?
? //copy data from one image to another by standard method
? ? ? ? CopyImage();
?
? //lock images to be able to see them
? ? ? ? LockBitmap();
?
? //lets see what we have
? ? ? ? pictureBox1.Image = bmp2;
}
快速方法如下:
private void btnFast_Click(object sender, EventArgs e)
{
? using (OpenFileDialog ofd = new OpenFileDialog())
? ? ? ? {
? ? ? ? ? ? if (ofd.ShowDialog() != System.Windows.Forms.DialogResult.OK)
? ? ? ? ? ? ? ? return;
? ? ? ? ? ? bitmapPath = ofd.FileName;
? ? ? ? }
?
? //open a selected image and create an empty image with the same size
? ? ? ? OpenImage();
?
? //unlock for read and write images
? ? ? ? UnlockBitmap();
?
? //copy data from one image to another with our fast method
? ? ? ? FastCopyImage();
?
? //lock images to be able to see them
? ? ? ? LockBitmap();
?
? //lets see what we have
? ? ? ? pictureBox1.Image = bmp2;
}
好的,现在我们有按钮并且也有了事件处理,下面来实现打开图片、锁定、解锁它们的方法,以及标准拷贝方法:
打开一个图片:
void OpenImage()
{
? pictureBox1.Image = null;
? buffer = null;
? if (bmp != null)
? {
? ? bmp.Dispose();
? ? bmp = null;
? }
? if (bmp2 != null)
? {
? ? bmp2.Dispose();
? ? bmp2 = null;
? }
? GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
?
? bmp = (Bitmap)Bitmap.FromFile(bitmapPath);
?
? buffer = new byte[bmp.Width * 4 * bmp.Height];
? bmp2 = new Bitmap(bmp.Width, bmp.Height, bmp.Width * 4, PixelFormat.Format32bppArgb,
? ? Marshal.UnsafeAddrOfPinnedArrayElement(buffer, 0));
}
锁定和解锁位图:
void UnlockBitmap()
{
? bmpd = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite,
? ? PixelFormat.Format32bppArgb);
? bmpd2 = bmp2.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite,
? ? PixelFormat.Format32bppArgb);
}
?
void LockBitmap()
{
? bmp.UnlockBits(bmpd);
? bmp2.UnlockBits(bmpd2);
}
从一个图片拷贝数据到另一个图片,并且显示测得的时间:
void CopyImage()
{
? //start stopwatch
? Stopwatch sw = new Stopwatch();
? sw.Start();
?
? //copy-past data 10 times
? for (int i = 0; i < 10; i++)
? {
? ? System.Runtime.InteropServices.Marshal.Copy(bmpd.Scan0, buffer, 0, buffer.Length);
? }
?
? //stop stopwatch
? sw.Stop();
?
? //show measured time
? MessageBox.Show(sw.ElapsedTicks.ToString());
}
这就是标准快速拷贝方法。其实一点也不复杂,我们使用了知名的? System.Runtime.InteropServices.Marshal.Copy? 方法。
以及又一个“中间方法(middle-method)”以用于快速拷贝逻辑:
void FastCopyImage()
{
? FastMemCopy.FastMemoryCopy(bmpd.Scan0, bmpd2.Sc