在.Net 7源碼中bool代碼優(yōu)化
起因
代碼總是從簡單到復(fù)雜,從易讀到晦澀,有的是業(yè)務(wù)邏輯復(fù)雜導(dǎo)致代碼也復(fù)雜,有的是為了性能優(yōu)化,導(dǎo)致代碼不那么易讀易懂了.這里主要是看到最近.Net bool類型源碼變化.
- Streamline bool.TryParse/Format (#64782)
- Fix bool.TryParse/Format on big-endian systems (#65078)
在.Net 6中TryParse
public static bool TryParse(ReadOnlySpan<char> value, out bool result)
{
if (IsTrueStringIgnoreCase(value)) //關(guān)注IsTrueStringIgnoreCase
{
result = true;
return true;
}
if (IsFalseStringIgnoreCase(value)) //關(guān)注和IsFalseStringIgnoreCase
{
result = false;
return true;
}
value = TrimWhiteSpaceAndNull(value);
if (IsTrueStringIgnoreCase(value))
{
result = true;
return true;
}
if (IsFalseStringIgnoreCase(value))
{
result = false;
return true;
}
result = false;
return false;
}
在TryParse中,調(diào)用IsTrueStringIgnoreCase和IsFalseStringIgnoreCase這兩個函數(shù)這時候是易讀易懂.
在.Net 7中TryParse(優(yōu)化)
//只保留IsTrueStringIgnoreCase和IsFalseStringIgnoreCase
internal static bool IsTrueStringIgnoreCase(ReadOnlySpan<char> value)
{
// "true" as a ulong, each char |'d with 0x0020 for case-insensitivity
// 先判斷cpu支持大小端模式, 采用無符號long類型存儲4個字符的ASCII碼值 小端:: 65→e 75→u 72→r 74→t
// 將value轉(zhuǎn)為byte數(shù)組,讀取為ulong類型,然后或運(yùn)算, 0x20為十六進(jìn)制 是十進(jìn)制32 或運(yùn)算:: A | 32 = a 這樣就不用區(qū)分大小寫
// 0x0020002000200020為4個0x20,因?yàn)橹狄粯?不區(qū)分大小端
ulong true_val = BitConverter.IsLittleEndian ? 0x65007500720074ul : 0x74007200750065ul;
return value.Length == 4 &&
(MemoryMarshal.Read<ulong>(MemoryMarshal.AsBytes(value)) | 0x0020002000200020) == true_val;
}
internal static bool IsFalseStringIgnoreCase(ReadOnlySpan<char> value)
{
// "fals" as a ulong, each char |'d with 0x0020 for case-insensitivity
// ulong類型只能存4個字符,所以存儲 "fals"這4個字符 73→s 6c→l 61→a 66→f
// 最后1個字符進(jìn)行與0x20進(jìn)行或運(yùn)算,得到小寫字符和'e'進(jìn)行判斷
ulong fals_val = BitConverter.IsLittleEndian ? 0x73006C00610066ul : 0x660061006C0073ul;
return value.Length == 5 &&
(((MemoryMarshal.Read<ulong>(MemoryMarshal.AsBytes(value)) | 0x0020002000200020) == fals_val) &
((value[4] | 0x20) == 'e'));
}
在.Net 6中TryFormat
public bool TryFormat(Span<char> destination, out int charsWritten)
{
if (m_value) //為真
{
if ((uint)destination.Length > 3u)
{
//進(jìn)行4次賦值操作
destination[0] = 'T';
destination[1] = 'r';
destination[2] = 'u';
destination[3] = 'e';
charsWritten = 4;
return true;
}
}
else if ((uint)destination.Length > 4u) //不為真
{
//進(jìn)行5次賦值操作
destination[0] = 'F';
destination[1] = 'a';
destination[2] = 'l';
destination[3] = 's';
destination[4] = 'e';
charsWritten = 5;
return true;
}
charsWritten = 0;
return false;
}
在.Net 7中TryFormat(優(yōu)化)
public bool TryFormat(Span<char> destination, out int charsWritten)
{
if (m_value)
{
if ((uint)destination.Length > 3) // uint cast, per https://github.com/dotnet/runtime/issues/10596
{
//先判斷cpu支持大小端模式, 采用無符號long類型存儲4個字符的ASCII碼值 小端:: 65→e 75→u 72→r 74→t
//將Span<char>轉(zhuǎn)為byte數(shù)組,將true_val寫入,減少賦值的次數(shù),與.Net 6中4次賦值操作,這里只有1次
ulong true_val = BitConverter.IsLittleEndian ? 0x65007500720054ul : 0x54007200750065ul; // "True"
MemoryMarshal.Write<ulong>(MemoryMarshal.AsBytes(destination), ref true_val);
charsWritten = 4;
return true;
}
}
else
{
if ((uint)destination.Length > 4)
{
//不為真時,進(jìn)行2次賦值操作 第1次 ulong 只能存4個字符, 第2次通過下標(biāo)賦值為'e'
ulong fals_val = BitConverter.IsLittleEndian ? 0x73006C00610046ul : 0x460061006C0073ul; // "Fals"
MemoryMarshal.Write<ulong>(MemoryMarshal.AsBytes(destination), ref fals_val);
destination[4] = 'e';
charsWritten = 5;
return true;
}
}
charsWritten = 0;
return false;
}
在.Net 7中對bool的改進(jìn),就是減少賦值操作,將4個字符轉(zhuǎn)為ulong,實(shí)現(xiàn)了一次將4個字符賦值.還有通過位或操作巧妙的實(shí)現(xiàn)將大寫字母進(jìn)行轉(zhuǎn)換.這種實(shí)現(xiàn)不是第一次看到,曾在wrk(壓力工具,使用c語言編寫)看到:
#define LOWER(c) (unsigned char)(c | 0x20)