2016-03-17 2 views
0

Учитывая следующий код, я могу получить доступ к пикселям изображения и сопоставить их с структурой 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;} 
} 

UPDATE

Так что с некоторым исследованием я был в состоянии выполнить некоторые улучшения с использованием небезопасного кода.

Вот один подход к моему индексатору:

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 собственности. Это когда-нибудь очищается сборщиком мусора? Должен ли образ реализовывать 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% быстрее, и, насколько я могу определить, это означает, что я не должен ничего делать для дальнейшего управления памятью.

Есть ли очевидные недостатки в любом подходе?

+2

Это тот, который, вероятно, лучше подходит для [Обзор кода] (http://codereview.stackexchange.com/). –

+0

Я думал, что, но я осмотрелся и увидел подобный вопрос на этом сайте, а также вопрос о механизмах небезопасного кода. Я уверен, что администратор перенесет его, если они подумают, что там лучше, и я не буду иметь проблемы, если они это сделают. –

+0

Используйте LockBits, если вам нужна скорость. Более подробную информацию можно найти здесь [http://stackoverflow.com/questions/190385/how-to-manipulate-images-at-pixel-level-in-c](http://stackoverflow.com/questions/190385/how-to-manipulate-images-at-pixel-level-in-c) – Dbuggy

ответ

1

Вы работаете исключительно с 32-разрядными пикселями. Зачем использовать byte[] вообще, когда вы можете использовать uint[] вместо этого и использовать Color.FromArgb?

Buffer.BlockCopy можно очень эффективно использовать для копирования между byte[] и uint[] (и любого другого типа значения AFAICT), так что если вы читаете из файла, это один вариант. Использование небезопасного кода - другое. В обоих случаях уделять большое внимание проверке границ - вы теряете много гарантий безопасности памяти.

Если вы собираетесь использовать скорость голых костей, вам понадобится много небезопасного кода. Это очень сложно, поэтому убедитесь, что на самом деле нужно, что скорость - профилирование вашего приложения в целом - это, как правило, хорошая идея.

+0

@ james-south действительно должен выполнять профилирование, потому что по крайней мере в режиме выпуска более 80% времени переходит в 'Color..ctor', и это точка доступа, а не доступ к массиву. –

+1

Я скорее предполагаю, что Color.FromArgb также недоступен, так как это .NET Core, где System.Drawing явно отсутствует (по понятным причинам). :) – Rytmis

+0

Как @Rytmis говорит, что нет 'Color.FromArgb' Это ядро ​​.NET. Кроме того, я не скрываю копии. –