ฉันกระแทกหัวของฉันกับกำแพงที่นี่ดังนั้นฉันหวังว่าบางท่านอาจจะให้การศึกษาแก่ฉัน ฉันกำลังทำการวัดประสิทธิภาพโดยใช้ BenchmarkDotNet และฉันพบกรณีแปลก ๆ นี้ซึ่งดูเหมือนว่าการประกาศสมาชิกconst
ลดประสิทธิภาพลงอย่างมาก
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using System;
namespace PerfTest
{
[DisassemblyDiagnoser(printAsm: true, printSource: true)]
public class Test
{
private int[] data;
private int Threshold = 90;
private const int ConstThreshold = 90;
[GlobalSetup]
public void GlobalSetup()
{
data = new int[1000];
var random = new Random(42);
for (var i = 0; i < data.Length; i++)
{
data[i] = random.Next(100);
}
}
static void Main(string[] args)
{
var summary = BenchmarkRunner.Run<Test>();
}
[Benchmark(Baseline = true)]
public void ClampToMemberValue()
{
for (var i = 0; i < data.Length; i++)
{
if (data[i] > Threshold) data[i] = Threshold;
}
}
[Benchmark]
public void ClampToConstValue()
{
for (var i = 0; i < data.Length; i++)
{
if (data[i] > ConstThreshold) data[i] = ConstThreshold;
}
}
}
}
ขอให้สังเกตว่าความแตกต่างเพียงอย่างเดียวระหว่างวิธีการทดสอบทั้งสองวิธีคือพวกเขาเปรียบเทียบกับตัวแปรสมาชิกปกติหรือสมาชิก const
ตาม BenchmarkDotNet การใช้ค่า const ช้าลงอย่างมากและฉันไม่เข้าใจว่าทำไม
BenchmarkDotNet=v0.11.5, OS=Windows 10.0.18362
Intel Core i7-5820K CPU 3.30GHz (Broadwell), 1 CPU, 12 logical and 6 physical cores
.NET Core SDK=3.0.100
[Host] : .NET Core 3.0.0 (CoreCLR 4.700.19.46205, CoreFX 4.700.19.46214), 64bit RyuJIT
DefaultJob : .NET Core 3.0.0 (CoreCLR 4.700.19.46205, CoreFX 4.700.19.46214), 64bit RyuJIT
| Method | Mean | Error | StdDev | Ratio |
|------------------- |---------:|---------:|---------:|------:|
| ClampToMemberValue | 590.4 ns | 1.980 ns | 1.852 ns | 1.00 |
| ClampToConstValue | 724.6 ns | 4.184 ns | 3.709 ns | 1.23 |
การดูโค้ดที่คอมไพล์ของ JIT นั้นไม่ได้อธิบายเท่าที่ฉันสามารถบอกได้ นี่คือรหัสสำหรับสองวิธี ข้อแตกต่างคือว่าการเปรียบเทียบนั้นทำกับรีจิสเตอร์หรือตัวอักษร
00007ff9`7f1b8500 PerfTest.Test.ClampToMemberValue()
for (var i = 0; i < data.Length; i++)
^^^^^^^^^
00007ff9`7f1b8504 33c0 xor eax,eax
for (var i = 0; i < data.Length; i++)
^^^^^^^^^^^^^^^
00007ff9`7f1b8506 488b5108 mov rdx,qword ptr [rcx+8]
00007ff9`7f1b850a 837a0800 cmp dword ptr [rdx+8],0
00007ff9`7f1b850e 7e2e jle 00007ff9`7f1b853e
00007ff9`7f1b8510 8b4910 mov ecx,dword ptr [rcx+10h]
if (data[i] > Threshold) data[i] = Threshold;
^^^^^^^^^^^^^^^^^^^^^^^^
00007ff9`7f1b8513 4c8bc2 mov r8,rdx
00007ff9`7f1b8516 458b4808 mov r9d,dword ptr [r8+8]
00007ff9`7f1b851a 413bc1 cmp eax,r9d
00007ff9`7f1b851d 7324 jae 00007ff9`7f1b8543
00007ff9`7f1b851f 4c63c8 movsxd r9,eax
00007ff9`7f1b8522 43394c8810 cmp dword ptr [r8+r9*4+10h],ecx
00007ff9`7f1b8527 7e0e jle 00007ff9`7f1b8537
if (data[i] > Threshold) data[i] = Threshold;
^^^^^^^^^^^^^^^^^^^^
00007ff9`7f1b8529 4c8bc2 mov r8,rdx
00007ff9`7f1b852c 448bc9 mov r9d,ecx
00007ff9`7f1b852f 4c63d0 movsxd r10,eax
00007ff9`7f1b8532 47894c9010 mov dword ptr [r8+r10*4+10h],r9d
for (var i = 0; i < data.Length; i++)
^^^
00007ff9`7f1b8537 ffc0 inc eax
00007ff9`7f1b8539 394208 cmp dword ptr [rdx+8],eax
00007ff9`7f1b853c 7fd5 jg 00007ff9`7f1b8513
}
^
00007ff9`7f1b853e 4883c428 add rsp,28h
และ
00007ff9`7f1a8500 PerfTest.Test.ClampToConstValue()
for (var i = 0; i < data.Length; i++)
^^^^^^^^^
00007ff9`7f1a8504 33c0 xor eax,eax
for (var i = 0; i < data.Length; i++)
^^^^^^^^^^^^^^^
00007ff9`7f1a8506 488b5108 mov rdx,qword ptr [rcx+8]
00007ff9`7f1a850a 837a0800 cmp dword ptr [rdx+8],0
00007ff9`7f1a850e 7e2d jle 00007ff9`7f1a853d
if (data[i] > ConstThreshold) data[i] = ConstThreshold;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
00007ff9`7f1a8510 488bca mov rcx,rdx
00007ff9`7f1a8513 448b4108 mov r8d,dword ptr [rcx+8]
00007ff9`7f1a8517 413bc0 cmp eax,r8d
00007ff9`7f1a851a 7326 jae 00007ff9`7f1a8542
00007ff9`7f1a851c 4c63c0 movsxd r8,eax
00007ff9`7f1a851f 42837c81105a cmp dword ptr [rcx+r8*4+10h],5Ah
00007ff9`7f1a8525 7e0f jle 00007ff9`7f1a8536
if (data[i] > ConstThreshold) data[i] = ConstThreshold;
^^^^^^^^^^^^^^^^^^^^^^^^^
00007ff9`7f1a8527 488bca mov rcx,rdx
00007ff9`7f1a852a 4c63c0 movsxd r8,eax
00007ff9`7f1a852d 42c74481105a000000 mov dword ptr [rcx+r8*4+10h],5Ah
for (var i = 0; i < data.Length; i++)
^^^
00007ff9`7f1a8536 ffc0 inc eax
00007ff9`7f1a8538 394208 cmp dword ptr [rdx+8],eax
00007ff9`7f1a853b 7fd3 jg 00007ff9`7f1a8510
}
^
00007ff9`7f1a853d 4883c428 add rsp,28h
ฉันแน่ใจว่ามีบางสิ่งที่ฉันมองข้ามไป แต่ฉันไม่สามารถคลานมันได้ในจุดนี้ดังนั้นฉันจึงกำลังมองหาสิ่งที่สามารถอธิบายสิ่งนี้ได้
cmp
และmov
คำแนะนำที่ใช้สำหรับเส้นทาง const ครอบครองหน่วยความจำมากกว่าคำแนะนำตามการลงทะเบียนเพราะการเข้ารหัสจำนวนต้องไบต์เพิ่มเติมและในเวลารวมมากขึ้นรอบการทำงานในการดำเนินการ (9 ไบต์ VS 5 ไบต์mov
และ 6 ไบต์ VS 5 ไบต์สำหรับ CMP) . และแม้ว่าจะมีmov ecx,dword ptr [rcx+10h]
คำสั่งเพิ่มเติมสำหรับเวอร์ชั่นที่ไม่ใช่ const แต่ก็มีการปรับปรุงให้ดีที่สุดโดยคอมไพเลอร์ JIT ที่จะอยู่นอกลูปในเวอร์ชันรีลีส