teratail header banner
teratail header banner
質問するログイン新規登録

回答編集履歴

3

Unsafe, Span を比較

2023/09/16 03:18

投稿

KOZ6.0
KOZ6.0

スコア2736

answer CHANGED
@@ -1,63 +1,119 @@
1
1
  こんなもんでしょうか?
2
- Span<T> が使えるともっと速くなります。
3
2
 
4
3
  ```csharp
5
4
  using System;
5
+ using System.Diagnostics;
6
6
  using System.IO;
7
7
  using System.Runtime.InteropServices;
8
8
  using System.Windows.Media.Imaging;
9
9
 
10
+ public static class BitmapImageHelper
11
+ {
10
- [StructLayout(LayoutKind.Sequential, Pack = 2)]
12
+ [StructLayout(LayoutKind.Sequential, Pack = 2)]
11
- private struct BITMAPFILEHEADER {
13
+ private struct BITMAPFILEHEADER
14
+ {
12
- public ushort bfType;
15
+ public ushort bfType;
13
- public uint bfSize;
16
+ public uint bfSize;
14
- public ushort bfReserved1;
17
+ public ushort bfReserved1;
15
- public ushort bfReserved2;
18
+ public ushort bfReserved2;
16
- public uint bfOffBits;
19
+ public uint bfOffBits;
20
+ }
21
+
22
+ [StructLayout(LayoutKind.Sequential)]
23
+ private struct BITMAPINFOHEADER
24
+ {
25
+ public uint biSize;
26
+ public int biWidth;
27
+ public int biHeight;
28
+ public ushort biPlanes;
29
+ public ushort biBitCount;
30
+ public uint biCompression;
31
+ public uint biSizeImage;
32
+ public int biXPelsPerMeter;
33
+ public int biYPelsPerMeter;
34
+ public uint biClrUsed;
35
+ public uint biClrImportant;
36
+ }
37
+
38
+ public enum BitmapDirection
39
+ {
40
+ TopDown, BottomUp
41
+ }
42
+
43
+ public static BitmapImage BitmapImageFromFile(
44
+ string fileName,
45
+ out BitmapDirection direction) {
46
+ byte[] bytes = File.ReadAllBytes(fileName);
47
+ direction = GetBitmapDirection(bytes);
48
+ using (MemoryStream mem = new MemoryStream(bytes)) {
49
+ BitmapImage bitmap = new BitmapImage();
50
+ bitmap.BeginInit();
51
+ bitmap.StreamSource = mem;
52
+ bitmap.CacheOption = BitmapCacheOption.OnLoad;
53
+ bitmap.EndInit();
54
+ bitmap.Freeze();
55
+ return bitmap;
56
+ }
57
+ }
58
+
59
+ public static BitmapDirection GetBitmapDirection(byte[] bytes) {
60
+ int offset = Marshal.SizeOf(typeof(BITMAPFILEHEADER));
61
+ int size = Marshal.SizeOf(typeof(BITMAPINFOHEADER));
62
+ IntPtr ptr = Marshal.AllocCoTaskMem(size);
63
+ try {
64
+ Marshal.Copy(bytes, offset, ptr, size);
65
+ BITMAPINFOHEADER infoHeader = Marshal.PtrToStructure<BITMAPINFOHEADER>(ptr);
66
+ return infoHeader.biHeight > 0 ? BitmapDirection.BottomUp
67
+ : BitmapDirection.TopDown;
68
+ } finally {
69
+ Marshal.FreeCoTaskMem(ptr);
70
+ }
71
+ }
17
72
  }
73
+ ```
18
74
 
19
- [StructLayout(LayoutKind.Sequential)]
20
- private struct BITMAPINFOHEADER {
75
+ Unsafe 版とSpan版を作って比較してみました。
21
- public uint biSize;
76
+ ```csharp
22
- public int biWidth;
23
- public int biHeight;
24
- public ushort biPlanes;
77
+ // Unsafe
78
+ public static unsafe BitmapDirection UnsafeGetBitmapDirection(byte[] bytes) {
25
- public ushort biBitCount;
79
+ fixed (byte* lpFileHeader = bytes) {
80
+ BITMAPINFOHEADER* lpInfoHeader =
81
+ (BITMAPINFOHEADER*)(lpFileHeader + sizeof(BITMAPFILEHEADER));
82
+ return lpInfoHeader->biHeight > 0 ? BitmapDirection.BottomUp
26
- public uint biCompression;
83
+ : BitmapDirection.TopDown;
27
- public uint biSizeImage;
28
- public int biXPelsPerMeter;
29
- public int biYPelsPerMeter;
30
- public uint biClrUsed;
84
+ }
31
- public uint biClrImportant;
32
85
  }
33
86
 
87
+ // Span 版
88
+ public static BitmapDirection SpanGetBitmapDirection(byte[] bytes) {
89
+ int offset = Marshal.SizeOf(typeof(BITMAPFILEHEADER));
90
+ int size = Marshal.SizeOf(typeof(BITMAPINFOHEADER));
91
+ Span<byte> span = new Span<byte>(bytes, offset, size);
92
+ BITMAPINFOHEADER infoHeader = MemoryMarshal.Read<BITMAPINFOHEADER>(span);
93
+ return infoHeader.biHeight > 0 ? BitmapDirection.BottomUp
34
- public enum BitmapDirection
94
+ : BitmapDirection.TopDown;
35
- {
36
- TopDown, BottomUp
37
95
  }
38
96
 
39
- public static BitmapImage BitmapImageFromFile(
97
+ public static void TryGetBitmapDirection(string fileName) {
40
- string fileName,
41
- out BitmapDirection direction) {
42
- var bytes = File.ReadAllBytes(fileName);
98
+ byte[] bytes = File.ReadAllBytes(fileName);
43
- var ptr = Marshal.AllocHGlobal(bytes.Length);
44
- try {
45
- Marshal.Copy(bytes, 0, ptr, bytes.Length);
46
- var offset = Marshal.SizeOf(typeof(BITMAPFILEHEADER));
47
- var header = Marshal.PtrToStructure<BITMAPINFOHEADER>(ptr + offset);
48
- direction = header.biHeight >= 0 ? BitmapDirection.BottomUp
99
+ Watch("GetBitmapDirection", () => { GetBitmapDirection(bytes); });
100
+ Watch("UnsafeGetBitmapDirection", () => { UnsafeGetBitmapDirection(bytes); });
101
+ Watch("SpanGetBitmapDirection", () => { SpanGetBitmapDirection(bytes); });
102
+ }
103
+
49
- : BitmapDirection.TopDown;
104
+ public static void Watch(string message, Action action) {
50
- } finally {
51
- Marshal.FreeHGlobal(ptr);
105
+ Stopwatch sw = new Stopwatch();
106
+ sw.Start();
107
+ for (int i = 0; i < 100000; i++) {
108
+ action.Invoke();
52
109
  }
53
- var mem = new MemoryStream(bytes);
54
- var bitmap = new BitmapImage();
55
- bitmap.BeginInit();
56
- bitmap.StreamSource = mem;
57
- bitmap.CacheOption = BitmapCacheOption.OnLoad;
58
- bitmap.EndInit();
59
- bitmap.Freeze();
110
+ sw.Stop();
60
- return bitmap;
111
+ Console.WriteLine($"{message} {sw.ElapsedMilliseconds}ms");
61
112
  }
113
+
114
+ 結果
115
+ GetBitmapDirection 61ms
116
+ UnsafeGetBitmapDirection 1ms
117
+ SpanGetBitmapDirection 12ms
62
118
  ```
63
- 思えば 16bit VB を 32bit VB に書き直したとき、最初にひっったのがこれ(BITMAPFILEHEADER)だったなぁ・・・
119
+ Span も速いですが、やはり Unsafe は速かった

2

逆だった

2023/09/15 20:57

投稿

KOZ6.0
KOZ6.0

スコア2736

answer CHANGED
@@ -45,8 +45,8 @@
45
45
  Marshal.Copy(bytes, 0, ptr, bytes.Length);
46
46
  var offset = Marshal.SizeOf(typeof(BITMAPFILEHEADER));
47
47
  var header = Marshal.PtrToStructure<BITMAPINFOHEADER>(ptr + offset);
48
- direction = header.biHeight >= 0 ? BitmapDirection.TopDown
48
+ direction = header.biHeight >= 0 ? BitmapDirection.BottomUp
49
- : BitmapDirection.BottomUp;
49
+ : BitmapDirection.TopDown;
50
50
  } finally {
51
51
  Marshal.FreeHGlobal(ptr);
52
52
  }

1

using

2023/09/15 14:14

投稿

KOZ6.0
KOZ6.0

スコア2736

answer CHANGED
@@ -2,6 +2,11 @@
2
2
  Span<T> が使えるともっと速くなります。
3
3
 
4
4
  ```csharp
5
+ using System;
6
+ using System.IO;
7
+ using System.Runtime.InteropServices;
8
+ using System.Windows.Media.Imaging;
9
+
5
10
  [StructLayout(LayoutKind.Sequential, Pack = 2)]
6
11
  private struct BITMAPFILEHEADER {
7
12
  public ushort bfType;
@@ -31,7 +36,7 @@
31
36
  TopDown, BottomUp
32
37
  }
33
38
 
34
- public static BitmapImage FromBitmapImage(
39
+ public static BitmapImage BitmapImageFromFile(
35
40
  string fileName,
36
41
  out BitmapDirection direction) {
37
42
  var bytes = File.ReadAllBytes(fileName);