鉴于以下代码,我可以访问图像的像素并将它们映射到Color
结构。
在我看来,虽然这看起来效率很低,因为每次访问索引器时我都会从数组中读取四次。我的电脑上运行大约需要9秒钟。
我正在寻找的是一个如何优化代码的例子。我猜我可以使用不安全的代码获取某种类型的数组引用并一次性读取但我还没有找到一个体面的指南来解释你将如何做到这一点以及如何将该方法应用于其他情况。
虽然代码示例本身对我很有帮助,但对于我究竟发生了什么以及为什么我会提高性能的解释对我自己和任何阅读者都会更有益。
public class Program
{
public static void Main(string[] args)
{
Image img = new Image(10000,10000);
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
for (int y = 0; y < img.Height; y++)
{
for (int x = 0; x < img.Width; x++)
{
Color color = img[x,y];
}
}
stopwatch.Stop();
Console.WriteLine(
string.Format("Time: {0} ms", stopwatch.ElapsedMilliseconds));
Console.ReadKey();
}
}
// Define other methods and classes here
public class Image {
private float[] pixels;
public Image (int width, int height){
pixels = new float[width * height * 4];
this.Width = width;
this.Height = height;
}
public int Width { get; private set; }
public int Height { get; private set; }
public Color this[int x, int y]
{
get
{
int start = ((y * this.Width) + x) * 4;
return new Color(this.pixels[start],
this.pixels[start + 1],
this.pixels[start + 2],
this.pixels[start + 3]);
}
set
{
int start = ((y * this.Width) + x) * 4;
this.pixels[start] = value.R;
this.pixels[start + 1] = value.G;
this.pixels[start + 2] = value.B;
this.pixels[start + 3] = value.A;
}
}
}
public struct Color {
public Color (float r, float g, float b, float a):this(){
this.R = r;
this.G = g;
this.B = b;
this.A = a;
}
public float R {get;set;}
public float G {get;set;}
public float B {get;set;}
public float A {get;set;}
}
更新
通过一些调查,我已经能够使用不安全的代码进行一些改进。
以下是我的索引器的一种方法:
public unsafe class Image {
private byte* pixels;
public Image (int width, int height){
fixed(byte* p = &(new byte[width * height * 4])[0]){
this.pixels = p;
}
this.Width = width;
this.Height = height;
}
public int Width { get; private set; }
public int Height { get; private set; }
public Color this[int x, int y]
{
get { return *((Color*)(this.pixels + ((y * this.Width) + x) * 4)); }
set {
Color* c = (Color*)(this.pixels + (((y * this.Width) + x) * 4));
c->R = value.R;
c->G = value.G;
c->B = value.B;
c->A = value.A;
}
}
}
这快了约50%,但我担心pixels
属性会发生什么。垃圾收集器是否会清理它? Image是否应该实现IDisposable
以便我可以将值设置为null?
这是第二种方法:
public unsafe class Image {
private byte[] pixels;
public Image (int width, int height){
pixels = new byte[width * height * 4];
this.Width = width;
this.Height = height;
}
public int Width { get; private set; }
public int Height { get; private set; }
public Color this[int x, int y]
{
get
{
fixed(byte* p = &this.pixels[0]){
return *((Color*)(p + ((y * this.Width) + x) * 4));
}
}
set
{
fixed(byte* p = &this.pixels[0]){
Color* c = (Color*)(p + (((y * this.Width) + x) * 4));
c->R = value.R;
c->G = value.G;
c->B = value.B;
c->A = value.A;
}
}
}
}
这大约快了28%,据我所知,这意味着我不需要做任何事情来进一步管理内存。
这两种方法都有明显的缺陷吗?
答案 0 :(得分:1)
您只使用32位像素。为什么要使用byte[]
,而可以使用uint[]
,并使用Color.FromArgb
?
Buffer.BlockCopy
可以非常高效地在byte[]
和uint[]
(以及任何其他值类型AFAICT)之间进行复制,因此如果您正在从文件中读取,那么这是一个选项。使用不安全的代码是另一回事。在这两种情况下,都要大量注意边界检查 - 你会失去很多内存安全保障。
如果你的速度很快,你将需要很多不安全的代码。这非常棘手,所以请确保您实际上需要这种速度 - 整体分析您的应用程序通常是一个好主意。