ประสิทธิภาพใน .NET Core
สวัสดีทุกคน! บทความนี้คือชุดแนวทางปฏิบัติที่ดีที่สุดที่เพื่อนร่วมงานและฉันใช้มานานเมื่อทำงานในโครงการต่างๆ
ข้อมูลเกี่ยวกับเครื่องจักรที่ทำการคำนวณ:เกณฑ์มาตรฐานDotNet=v0.11.5, OS=Windows 10.0.18362
Intel Core i5-8250U CPU 1.60GHz (Kaby Lake R), 1 CPU, 8 ลอจิคัล และ 4 ฟิสิคัลคอร์
.NET Core SDK=3.0.100
[โฮสต์]: .NET Core 2.2.7 (CoreCLR 4.6.28008.02, CoreFX 4.6.28008.03), 64 บิต RyuJIT
แกนหลัก: .NET Core 2.2.7 (CoreCLR 4.6.28008.02, CoreFX 4.6.28008.03), 64 บิต RyuJIT
[โฮสต์]: .NET Core 3.0.0 (CoreCLR 4.700.19.46205, CoreFX 4.700.19.46214), 64 บิต RyuJIT
แกนหลัก: .NET Core 3.0.0 (CoreCLR 4.700.19.46205, CoreFX 4.700.19.46214), 64 บิต RyuJIT
งาน=คอร์ รันไทม์=คอร์
ToList กับ ToArray และ Cycles
ฉันวางแผนที่จะเตรียมข้อมูลนี้ด้วยการเปิดตัว .NET Core 3.0 แต่พวกเขาเอาชนะฉัน ฉันไม่ต้องการขโมยเกียรติของคนอื่นและคัดลอกข้อมูลของผู้อื่น ดังนั้นฉันจะชี้ให้เห็น
ในนามของฉันเอง ฉันแค่อยากนำเสนอการวัดและผลลัพธ์ของฉันแก่คุณ ฉันเพิ่มการวนซ้ำแบบย้อนกลับให้กับผู้ชื่นชอบการเขียนแบบ "สไตล์ C++"
รหัส:
public class Bench
{
private List<int> _list;
private int[] _array;
[Params(100000, 10000000)] public int N;
[GlobalSetup]
public void Setup()
{
const int MIN = 1;
const int MAX = 10;
Random random = new Random();
_list = Enumerable.Repeat(0, N).Select(i => random.Next(MIN, MAX)).ToList();
_array = _list.ToArray();
}
[Benchmark]
public int ForList()
{
int total = 0;
for (int i = 0; i < _list.Count; i++)
{
total += _list[i];
}
return total;
}
[Benchmark]
public int ForListFromEnd()
{
int total = 0;t
for (int i = _list.Count-1; i > 0; i--)
{
total += _list[i];
}
return total;
}
[Benchmark]
public int ForeachList()
{
int total = 0;
foreach (int i in _list)
{
total += i;
}
return total;
}
[Benchmark]
public int ForeachArray()
{
int total = 0;
foreach (int i in _array)
{
total += i;
}
return total;
}
[Benchmark]
public int ForArray()
{
int total = 0;
for (int i = 0; i < _array.Length; i++)
{
total += _array[i];
}
return total;
}
[Benchmark]
public int ForArrayFromEnd()
{
int total = 0;
for (int i = _array.Length-1; i > 0; i--)
{
total += _array[i];
}
return total;
}
}
ความเร็วประสิทธิภาพใน .NET Core 2.2 และ 3.0 เกือบจะเท่ากัน นี่คือสิ่งที่ฉันได้รับใน. NET Core 3.0:
เราสามารถสรุปได้ว่าการประมวลผลซ้ำของคอลเลกชัน Array นั้นเร็วกว่าเนื่องจากการเพิ่มประสิทธิภาพภายในและการจัดสรรขนาดคอลเลกชันที่ชัดเจน นอกจากนี้ โปรดจำไว้ว่าคอลเลกชันรายการมีข้อดีในตัวเอง และคุณควรใช้คอลเลกชันที่ถูกต้องโดยขึ้นอยู่กับการคำนวณที่จำเป็น แม้ว่าคุณจะเขียนตรรกะสำหรับการทำงานกับลูป อย่าลืมว่านี่เป็นลูปธรรมดาและอาจมีการปรับให้เหมาะสมสำหรับลูปที่เป็นไปได้ด้วย บทความถูกตีพิมพ์บน habr เมื่อนานมาแล้ว:
โยน
ปีที่แล้ว ฉันทำงานในบริษัทแห่งหนึ่งในโครงการเดิม ซึ่งในโครงการนั้นเป็นเรื่องปกติที่จะประมวลผลการตรวจสอบภาคสนามผ่านโครงสร้างแบบ try-catch-throw ฉันเข้าใจแล้วว่านี่เป็นตรรกะทางธุรกิจที่ไม่ดีต่อสุขภาพสำหรับโปรเจ็กต์ ดังนั้นทุกครั้งที่เป็นไปได้ ฉันพยายามที่จะไม่ใช้การออกแบบดังกล่าว แต่ลองมาดูกันว่าทำไมแนวทางการจัดการข้อผิดพลาดกับโครงสร้างดังกล่าวจึงไม่ดี ฉันเขียนโค้ดเล็กๆ เพื่อเปรียบเทียบทั้งสองวิธีและทำการวัดประสิทธิภาพสำหรับแต่ละตัวเลือก
รหัส:
public bool ContainsHash()
{
bool result = false;
foreach (var file in _files)
{
var extension = Path.GetExtension(file);
if (_hash.Contains(extension))
result = true;
}
return result;
}
public bool ContainsHashTryCatch()
{
bool result = false;
try
{
foreach (var file in _files)
{
var extension = Path.GetExtension(file);
if (_hash.Contains(extension))
result = true;
}
if(!result)
throw new Exception("false");
}
catch (Exception e)
{
result = false;
}
return result;
}
ผลลัพธ์ใน .NET Core 3.0 และ Core 2.2 มีผลลัพธ์ที่คล้ายกัน (.NET Core 3.0):
Try catch ทำให้โค้ดเข้าใจยากขึ้น และเพิ่มเวลาดำเนินการของโปรแกรมของคุณ แต่ถ้าคุณต้องการโครงสร้างนี้ คุณไม่ควรแทรกบรรทัดโค้ดที่ไม่คาดว่าจะรองรับข้อผิดพลาด ซึ่งจะทำให้โค้ดเข้าใจได้ง่ายขึ้น ในความเป็นจริง มันไม่ได้จัดการข้อยกเว้นที่โหลดระบบมากนัก แต่เป็นการส่งข้อผิดพลาดด้วยตนเองผ่านโครงสร้าง Throw ใหม่
การโยนข้อยกเว้นช้ากว่าบางคลาสที่จะรวบรวมข้อผิดพลาดในรูปแบบที่ต้องการ หากคุณกำลังประมวลผลแบบฟอร์มหรือข้อมูลบางส่วน และคุณทราบอย่างชัดเจนว่าข้อผิดพลาดควรเป็นอย่างไร ทำไมไม่ดำเนินการประมวลผลล่ะ
คุณไม่ควรเขียนโครงสร้าง Throw new Exception() หากสถานการณ์นี้ไม่ได้ผิดปกติ การจัดการและการโยนข้อยกเว้นมีราคาแพงมาก!!!
ToLower, ToLowerInvariant, ToUpper, ToUpperInvariant
จากประสบการณ์ 5 ปีในการทำงานบนแพลตฟอร์ม .NET ฉันได้พบเห็นหลายโครงการที่ใช้การจับคู่สตริง ฉันยังเห็นภาพต่อไปนี้: มีโซลูชัน Enterprise หนึ่งโซลูชันที่มีหลายโปรเจ็กต์ ซึ่งแต่ละโซลูชันทำการเปรียบเทียบสตริงต่างกัน แต่ควรใช้อะไรและจะรวมเข้าด้วยกันได้อย่างไร? ในหนังสือ CLR ผ่าน C# โดย Richter ฉันอ่านข้อมูลที่ว่าวิธี ToUpperInvariant() นั้นเร็วกว่า ToLowerInvariant()
ข้อความที่ตัดตอนมาจากหนังสือ:
แน่นอนว่าฉันไม่เชื่อและตัดสินใจทำการทดสอบบางอย่างบน .NET Framework และผลลัพธ์ทำให้ฉันตกใจ - ประสิทธิภาพเพิ่มขึ้นมากกว่า 15% จากนั้น เมื่อมาถึงที่ทำงานในเช้าวันรุ่งขึ้น ฉันได้แสดงการวัดเหล่านี้ให้หัวหน้าของฉันดู และให้พวกเขาสามารถเข้าถึงซอร์สโค้ดได้ หลังจากนั้น 2 ใน 14 โปรเจ็กต์ก็มีการเปลี่ยนแปลงเพื่อรองรับการวัดใหม่ และเมื่อพิจารณาว่าทั้งสองโปรเจ็กต์นี้มีอยู่ในการประมวลผลตาราง Excel ขนาดใหญ่ ผลลัพธ์ที่ได้ก็มีความสำคัญมากกว่าสำหรับผลิตภัณฑ์
ฉันยังนำเสนอการวัดสำหรับ .NET Core เวอร์ชันต่างๆ ให้คุณด้วย เพื่อให้พวกคุณแต่ละคนสามารถเลือกวิธีแก้ปัญหาที่เหมาะสมที่สุดได้ และฉันแค่อยากจะเสริมว่าในบริษัทที่ฉันทำงาน เราใช้ ToUpper() เพื่อเปรียบเทียบสตริง
รหัส:
public const string defaultString = "VXTDuob5YhummuDq1PPXOHE4PbrRjYfBjcHdFs8UcKSAHOCGievbUItWhU3ovCmRALgdZUG1CB0sQ4iMj8Z1ZfkML2owvfkOKxBCoFUAN4VLd4I8ietmlsS5PtdQEn6zEgy1uCVZXiXuubd0xM5ONVZBqDu6nOVq1GQloEjeRN8jXrj0MVUexB9aIECs7caKGddpuut3";
[Benchmark]
public bool ToLower()
{
return defaultString.ToLower() == defaultString.ToLower();
}
[Benchmark]
public bool ToLowerInvariant()
{
return defaultString.ToLowerInvariant() == defaultString.ToLowerInvariant();
}
[Benchmark]
public bool ToUpper()
{
return defaultString.ToUpper() == defaultString.ToUpper();
}
[Benchmark]
public bool ToUpperInvariant()
{
return defaultString.ToUpperInvariant() == defaultString.ToUpperInvariant();
}
ใน .NET Core 3.0 การเพิ่มสำหรับแต่ละวิธีเหล่านี้คือ ~x2 และสร้างสมดุลการใช้งานระหว่างกัน
การรวบรวมระดับ
ในบทความล่าสุดของฉัน ฉันอธิบายฟังก์ชันนี้โดยย่อ ฉันต้องการแก้ไขและเสริมคำพูดของฉัน การคอมไพล์หลายระดับจะช่วยเร่งเวลาเริ่มต้นของโซลูชันของคุณ แต่คุณเสียสละส่วนต่างๆ ของโค้ดของคุณที่จะถูกคอมไพล์เป็นเวอร์ชันที่ได้รับการปรับปรุงให้เหมาะสมยิ่งขึ้นในเบื้องหลัง ซึ่งอาจก่อให้เกิดค่าใช้จ่ายเล็กน้อย ด้วยการถือกำเนิดของ NET Core 3.0 เวลาในการสร้างสำหรับโปรเจ็กต์ที่เปิดใช้งานการคอมไพล์ระดับลดลงและข้อบกพร่องที่เกี่ยวข้องกับเทคโนโลยีนี้ได้รับการแก้ไขแล้ว ก่อนหน้านี้ เทคโนโลยีนี้ทำให้เกิดข้อผิดพลาดในการร้องขอครั้งแรกใน ASP.NET Core และค้างระหว่างการสร้างครั้งแรกในโหมดการคอมไพล์หลายระดับ ปัจจุบันเปิดใช้งานตามค่าเริ่มต้นใน .NET Core 3.0 แต่คุณสามารถปิดการใช้งานได้หากต้องการ หากคุณอยู่ในตำแหน่งหัวหน้าทีม อาวุโส กลาง หรือคุณเป็นหัวหน้าแผนก คุณต้องเข้าใจว่าการพัฒนาโครงการอย่างรวดเร็วจะเพิ่มมูลค่าให้กับทีม และเทคโนโลยีนี้จะช่วยให้คุณประหยัดเวลาสำหรับนักพัฒนาทั้งสองคน และเวลาของโครงการเอง
.NET ยกระดับขึ้น
อัปเกรดเวอร์ชัน .NET Framework / .NET Core ของคุณ บ่อยครั้งที่เวอร์ชันใหม่แต่ละเวอร์ชันให้ประสิทธิภาพที่เพิ่มขึ้นและเพิ่มคุณสมบัติใหม่ๆ
แต่ประโยชน์ที่แท้จริงคืออะไร? ลองดูบางส่วนของพวกเขา:
- .NET Core 3.0 เปิดตัวอิมเมจ R2R ที่จะช่วยลดเวลาเริ่มต้นของแอปพลิเคชัน .NET Core
- ด้วยเวอร์ชัน 2.2 Tier Compilation ปรากฏขึ้น ต้องขอบคุณโปรแกรมเมอร์ที่จะใช้เวลาน้อยลงในการเปิดตัวโปรเจ็กต์
- รองรับมาตรฐาน .NET ใหม่
- รองรับภาษาการเขียนโปรแกรมเวอร์ชันใหม่
- การเพิ่มประสิทธิภาพ โดยแต่ละเวอร์ชันใหม่จะมีการปรับปรุงไลบรารีฐาน Collection/Struct/Stream/String/Regex และอื่นๆ อีกมากมาย หากคุณกำลังย้ายจาก .NET Framework ไปยัง .NET Core คุณจะได้รับประสิทธิภาพที่เพิ่มขึ้นอย่างมากตั้งแต่แกะกล่อง ตามตัวอย่าง ฉันแนบลิงก์ไปยังการปรับปรุงประสิทธิภาพบางส่วนที่เพิ่มลงใน .NET Core 3.0:
https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-core-3-0/
ข้อสรุป
เมื่อเขียนโค้ด คุณควรให้ความสนใจกับแง่มุมต่างๆ ของโปรเจ็กต์ของคุณ และใช้ฟีเจอร์ของภาษาและแพลตฟอร์มการเขียนโปรแกรมของคุณเพื่อให้ได้ผลลัพธ์ที่ดีที่สุด ฉันยินดีเป็นอย่างยิ่งหากคุณแบ่งปันความรู้เกี่ยวกับการเพิ่มประสิทธิภาพใน .NET
ที่มา: will.com