Po naszej sesji pojawiły się dwa pytania, na które udzieliliśmy odpowiedzi, której nie byliśmy do końca pewni. Oczywiście zaznaczyliśmy, że najlepiej jest to przetestować i zobaczyć jak zachowa się telefon. Wyniki potwierdziły nasze odpowiedzi.
Pytanie 1
Pierwsze pytanie dotyczyło tego, czy nie wystarczy jedynie zamienić kolejności pętli, aby uzyskać podobne przyspieszenie działania programu, bez wyciągania operacji wyliczenia indeksu. Wtedy nasz kod zmieni się z poniższego:
public void Convert1(WriteableBitmap bitmap) { int[] grayScale = new int[bitmap.PixelHeight * bitmap.PixelWidth]; for (int i = 0; i < bitmap.PixelWidth; i++) { for (int j = 0; j < bitmap.PixelHeight; j++) { Color currentColor = this.ConvertToColor( bitmap.Pixels[i + (bitmap.PixelHeight * j)]); Color grayColor = this.ToGrayScale(currentColor); grayScale[i + (bitmap.PixelHeight * j)] = this.ToIntFromColor(grayColor); } } }
Na następujący:
public void Convert2(WriteableBitmap bitmap) { int[] grayScale = new int[bitmap.PixelHeight * bitmap.PixelWidth]; for (int j = 0; j < bitmap.PixelHeight; j++) { for (int i = 0; i < bitmap.PixelWidth; i++) { Color currentColor = this.ConvertToColor( bitmap.Pixels[i + (bitmap.PixelHeight * j)]); Color grayColor = this.ToGrayScale(currentColor); grayScale[i + (bitmap.PixelHeight * j)] = this.ToIntFromColor(grayColor); } } }
Dla przypomnienia podam jeszcze zaproponowaną przez nas wersję pierwszego kroku optymalizacji:
public void Convert3(WriteableBitmap bitmap) { int[] grayScale = new int[bitmap.PixelHeight * bitmap.PixelWidth]; for (int j = 0; j < bitmap.PixelHeight; j++) { int heightIndex = bitmap.PixelHeight * j; for (int i = 0; i < bitmap.PixelWidth; i++) { int pixelIndex = i + heightIndex; Color currentColor = this.ConvertToColor(bitmap.Pixels[pixelIndex]); Color grayColor = this.ToGrayScale(currentColor); grayScale[pixelIndex] = this.ToIntFromColor(grayColor); } } }
Jak pokazują testy na urządzeniach – na każdym z nich zostało wykonane 1000 pomiarów – czas uległ skróceniu, ale nieznacznie. Poniżej zamieszczam tabelę z wynikami:
Metoda | Nokia Lumia 800 | HTC Titan | HTC 7 Pro |
Convert1 | 236,57 ms | 263,42 ms | 393,08 ms |
Convert2 | 223,17 ms | 234,88 ms | 350,30 ms |
Convert3 | 179,32 ms | 170,63 ms | 253,50 ms |
Na podstawie otrzymanych wyników możemy zaobserwować, że pierwsza modyfikacja skróciła czas wykonywania metody średnio o 9%. Natomiast jednokrotne obliczenie indeksu wraz z redukcją ilości wykonywanych operacji mnożenia oraz zamiana kolejności wykonywania pętli skraca czas wykonywania metody średnio o 32%.
Pytanie 2
Drugie pytanie dotyczyło finalnej implementacji:
public void Convert4(WriteableBitmap bitmap) { int[] pixels = bitmap.Pixels; int length = pixels.Length; int[] grayScale = new int[length]; for (int i = 0; i < length; i++) { int currentPixel = pixels[i]; int a = (byte)(currentPixel >> 24); int r = (byte)(currentPixel >> 16); int g = (byte)(currentPixel >> 8); int b = (byte)currentPixel; int gray = (6966 * r + 23436 * g + 2366 * b) >> 15; int grayColor = (a << 24) | (gray << 16) | (gray << 8) | gray; grayScale[i] = grayColor; } }
Pojawił się pomysł, aby wyciągnąć deklarację zmiennych [mark]int[/mark] przez pętlę i zobaczyć co się stanie:
public void Convert5(WriteableBitmap bitmap) { int[] pixels = bitmap.Pixels; int length = pixels.Length; int[] grayScale = new int[length]; int currentPixel, a, r, g, b, gray, grayColor; for (int i = 0; i < length; i++) { currentPixel = pixels[i]; a = (byte)(currentPixel >> 24); r = (byte)(currentPixel >> 16); g = (byte)(currentPixel >> 8); b = (byte)currentPixel; gray = (6966 * r + 23436 * g + 2366 * b) >> 15; grayColor = (a << 24) | (gray << 16) | (gray << 8) | gray; grayScale[i] = grayColor; } }
W tym przypadku, nie udało się zaobserwować żadnej zmiany. Zarówno czas wykonania funkcji, jak również zużycie pamięci pozostało to samo.
Zostaw komentarz