Microsoft Monitoring Agent找出效能問題指向一個將Byte Array轉成16進位的字串!
轉16進位字串為何會有效能問題呢?
今天透過 Microsoft Monitoring Agent 2013 來找尋 Web AP的效能問題,使用方式可以參考「利用 Microsoft Monitoring Agent 來找出系統效能及異常問題」。
找到問題點是一個將 Byte Array 轉成 16 進位字串的 Method,它的Code如下,
public static string ByteArrayToHexStrOld(byte[] vabytData)
{
string str;
string str2 = "";
long num2 = vabytData.Length - 1;
for (int i = 0; i <= num2; i++ )
{
string str3 = Convert.ToByte(vabytData[(int)i]).ToString("x");
if (str3.Length == 1)
{
str3 = "0" + str3;
}
str2 = str2 + str3;
}
str = str2;
return str;
}
是一個Byte 一個Byte去轉(.NET 1.1 沒有 BitConverter ),而 .NET 2.0之後有提供 BitConverter 類別 可以達到相同的功能,程式修改如下,
public static string ByteArrayToHexStrNew(byte[] vabytData)
{
return BitConverter.ToString(vabytData).Replace("-", string.Empty).ToLower();
}
//如果轉成 16 進位字串不需要轉小寫的話,或是去除 "-" 的話,會更快哦!
使用 BitConverter 來轉換,效能比較好哦!
另外,在「High performance C# byte array to hex string to byte array」這篇文章中,他的速度也蠻快的哦! 有興趣的話,也可以參考看看哦!
以執行 5百萬次 的時間來看,比較結果如下,
比較的測試程式如下,
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
int value = 1234567890;
byte[] bytes = BitConverter.GetBytes(value);
Console.WriteLine(ByteArrayToHexStrOld(bytes));
Console.WriteLine(ByteArrayToHexStrNew(bytes));
Console.WriteLine(Fast.ToHexString(bytes).ToLower());
// using System.Diagnostics;
Stopwatch stopWatch = new Stopwatch();
long memory = 0;
stopWatch = Stopwatch.StartNew();
memory = GC.GetTotalMemory(true);
for (int i = 0; i < 5000000; i++)
{
ByteArrayToHexStrOld(bytes);
}
stopWatch.Stop();
memory = GC.GetTotalMemory(false) - memory;
TimeSpan ts = stopWatch.Elapsed;
string elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}",
ts.Hours, ts.Minutes, ts.Seconds,
ts.Milliseconds / 10);
Console.WriteLine("ByteArrayToHexStrOld RunTime {0}, Mem:{1} ", elapsedTime, memory);
stopWatch = Stopwatch.StartNew();
memory = GC.GetTotalMemory(true);
for (int i = 0; i < 5000000; i++)
{
ByteArrayToHexStrNew(bytes);
}
stopWatch.Stop();
memory = GC.GetTotalMemory(false) - memory;
ts = stopWatch.Elapsed;
elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}",
ts.Hours, ts.Minutes, ts.Seconds,
ts.Milliseconds / 10);
Console.WriteLine("ByteArrayToHexStrNew RunTime {0}, Mem:{1} ", elapsedTime, memory);
stopWatch = Stopwatch.StartNew();
memory = GC.GetTotalMemory(true);
for (int i = 0; i < 5000000; i++)
{
Fast.ToHexString(bytes).ToLower();
}
stopWatch.Stop();
memory = GC.GetTotalMemory(false) - memory;
ts = stopWatch.Elapsed;
elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}",
ts.Hours, ts.Minutes, ts.Seconds,
ts.Milliseconds / 10);
Console.WriteLine("ByteArrayToHexFast RunTime {0}, Mem:{1} ", elapsedTime, memory);
Console.ReadKey();
}
public static string ByteArrayToHexStrNew(byte[] vabytData)
{
return BitConverter.ToString(vabytData).Replace("-", string.Empty).ToLower();
}
public static string ByteArrayToHexStrOld(byte[] vabytData)
{
string str;
string str2 = "";
long num2 = vabytData.Length - 1;
for (int i = 0; i <= num2; i++)
{
string str3 = Convert.ToByte(vabytData[(int)i]).ToString("x");
if (str3.Length == 1)
{
str3 = "0" + str3;
}
str2 = str2 + str3;
}
str = str2;
return str;
}
}
// class is sealed and not static in my personal complete version
public unsafe sealed partial class Fast
{
#region from/to hex
// assigned int values for bytes (0-255)
static readonly int[] toHexTable = new int[] {
3145776, 3211312, 3276848, 3342384, 3407920, 3473456, 3538992, 3604528, 3670064, 3735600,
4259888, 4325424, 4390960, 4456496, 4522032, 4587568, 3145777, 3211313, 3276849, 3342385,
3407921, 3473457, 3538993, 3604529, 3670065, 3735601, 4259889, 4325425, 4390961, 4456497,
4522033, 4587569, 3145778, 3211314, 3276850, 3342386, 3407922, 3473458, 3538994, 3604530,
3670066, 3735602, 4259890, 4325426, 4390962, 4456498, 4522034, 4587570, 3145779, 3211315,
3276851, 3342387, 3407923, 3473459, 3538995, 3604531, 3670067, 3735603, 4259891, 4325427,
4390963, 4456499, 4522035, 4587571, 3145780, 3211316, 3276852, 3342388, 3407924, 3473460,
3538996, 3604532, 3670068, 3735604, 4259892, 4325428, 4390964, 4456500, 4522036, 4587572,
3145781, 3211317, 3276853, 3342389, 3407925, 3473461, 3538997, 3604533, 3670069, 3735605,
4259893, 4325429, 4390965, 4456501, 4522037, 4587573, 3145782, 3211318, 3276854, 3342390,
3407926, 3473462, 3538998, 3604534, 3670070, 3735606, 4259894, 4325430, 4390966, 4456502,
4522038, 4587574, 3145783, 3211319, 3276855, 3342391, 3407927, 3473463, 3538999, 3604535,
3670071, 3735607, 4259895, 4325431, 4390967, 4456503, 4522039, 4587575, 3145784, 3211320,
3276856, 3342392, 3407928, 3473464, 3539000, 3604536, 3670072, 3735608, 4259896, 4325432,
4390968, 4456504, 4522040, 4587576, 3145785, 3211321, 3276857, 3342393, 3407929, 3473465,
3539001, 3604537, 3670073, 3735609, 4259897, 4325433, 4390969, 4456505, 4522041, 4587577,
3145793, 3211329, 3276865, 3342401, 3407937, 3473473, 3539009, 3604545, 3670081, 3735617,
4259905, 4325441, 4390977, 4456513, 4522049, 4587585, 3145794, 3211330, 3276866, 3342402,
3407938, 3473474, 3539010, 3604546, 3670082, 3735618, 4259906, 4325442, 4390978, 4456514,
4522050, 4587586, 3145795, 3211331, 3276867, 3342403, 3407939, 3473475, 3539011, 3604547,
3670083, 3735619, 4259907, 4325443, 4390979, 4456515, 4522051, 4587587, 3145796, 3211332,
3276868, 3342404, 3407940, 3473476, 3539012, 3604548, 3670084, 3735620, 4259908, 4325444,
4390980, 4456516, 4522052, 4587588, 3145797, 3211333, 3276869, 3342405, 3407941, 3473477,
3539013, 3604549, 3670085, 3735621, 4259909, 4325445, 4390981, 4456517, 4522053, 4587589,
3145798, 3211334, 3276870, 3342406, 3407942, 3473478, 3539014, 3604550, 3670086, 3735622,
4259910, 4325446, 4390982, 4456518, 4522054, 4587590
};
public static string ToHexString(byte[] source)
{
return ToHexString(source, false);
}
// hexIndicator: use prefix ("0x") or not
public static string ToHexString(byte[] source, bool hexIndicator)
{
// freeze toHexTable position in memory
fixed (int* hexRef = toHexTable)
// freeze source position in memory
fixed (byte* sourceRef = source)
{
// take first parsing position of source - allow inline pointer positioning
byte* s = sourceRef;
// calculate result length
int resultLen = (source.Length << 1);
// use prefix ("Ox")
if (hexIndicator)
// adapt result length
resultLen += 2;
// initialize result string with any character expect '\0'
string result = new string(' ', resultLen);
// take the first character address of result
fixed (char* resultRef = result)
{
// pairs of characters explain the endianess of toHexTable
// move on by pairs of characters (2 x 2 bytes) - allow inline pointer positioning
int* pair = (int*)resultRef;
// use prefix ("Ox") ?
if (hexIndicator)
// set first pair value
*pair++ = 7864368;
// more to go
while (*pair != 0)
// set the value of the current pair and move to next pair and source byte
*pair++ = hexRef[*s++];
return result;
}
}
}
// values for '\0' to 'f' where 255 indicates invalid input character
// starting from '\0' and not from '0' costs 48 bytes
// but results 0 subtructions and less if conditions
static readonly byte[] fromHexTable = new byte[] {
255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 0, 1,
2, 3, 4, 5, 6, 7, 8, 9, 255, 255,
255, 255, 255, 255, 255, 10, 11, 12, 13, 14,
15, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 10, 11, 12,
13, 14, 15
};
// same as above but valid values are multiplied by 16
static readonly byte[] fromHexTable16 = new byte[] {
255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 0, 16,
32, 48, 64, 80, 96, 112, 128, 144, 255, 255,
255, 255, 255, 255, 255, 160, 176, 192, 208, 224,
240, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 160, 176, 192,
208, 224, 240
};
public static byte[] FromHexString(string source)
{
// return an empty array in case of null or empty source
if (string.IsNullOrEmpty(source))
return new byte[0]; // you may change it to return null
if (source.Length % 2 == 1) // source length must be even
throw new ArgumentException();
int
index = 0, // start position for parsing source
len = source.Length >> 1; // initial length of result
// take the first character address of source
fixed (char* sourceRef = source)
{
if (*(int*)sourceRef == 7864368) // source starts with "0x"
{
if (source.Length == 2) // source must not be just a "0x")
throw new ArgumentException();
index += 2; // start position (bypass "0x")
len -= 1; // result length (exclude "0x")
}
byte add = 0; // keeps a fromHexTable value
byte[] result = new byte[len]; // initialization of result for known length
// freeze fromHexTable16 position in memory
fixed (byte* hiRef = fromHexTable16)
// freeze fromHexTable position in memory
fixed (byte* lowRef = fromHexTable)
// take the first byte address of result
fixed (byte* resultRef = result)
{
// take first parsing position of source - allow inremental memory position
char* s = (char*)&sourceRef[index];
// take first byte position of result - allow incremental memory position
byte* r = resultRef;
// source has more characters to parse
while (*s != 0)
{
// check for non valid characters in pairs
// you may split it if you don't like its readbility
if (
// check for character > 'f'
*s > 102 ||
// assign source value to current result position and increment source position
// and check if is a valid character
(*r = hiRef[*s++]) == 255 ||
// check for character > 'f'
*s > 102 ||
// assign source value to "add" parameter and increment source position
// and check if is a valid character
(add = lowRef[*s++]) == 255
)
throw new ArgumentException();
// set final value of current result byte and move pointer to next byte
*r++ += add;
}
return result;
}
}
}
#endregion
}
}
因為 High performance C# byte array to hex string to byte array 使用到 unsafe 所以在建置專案時,要設定 Allow unsafe code 的選項哦!
以上提供Byte Array 轉16進位字串的方式,一般來說使用 BitConverter 來轉應該就可以了。大家請依自已的需求來決定使用那一種方式。
如果有更好的方式也請分享出來哦! 謝謝!
參考資料
利用 Microsoft Monitoring Agent 來找出系統效能及異常問題
High performance C# byte array to hex string to byte array
Hi,
亂馬客Blog已移到了 「亂馬客 : Re:從零開始的軟體開發生活」
請大家繼續支持 ^_^