2015-05-12 12 views
10

Net 4.6 RC x64 là hai lần chậm như x86 (phát hành phiên bản):.NET 4.6 RC x64 là hai lần chậm như x86 (phát hành phiên bản)

Hãy xem xét đoạn mã này:

class SpectralNorm 
{ 
    public static void Main(String[] args) 
    { 
     int n = 5500; 
     if (args.Length > 0) n = Int32.Parse(args[0]); 

     var spec = new SpectralNorm(); 
     var watch = Stopwatch.StartNew(); 
     var res = spec.Approximate(n); 

     Console.WriteLine("{0:f9} -- {1}", res, watch.Elapsed.TotalMilliseconds); 
    } 

    double Approximate(int n) 
    { 
     // create unit vector 
     double[] u = new double[n]; 
     for (int i = 0; i < n; i++) u[i] = 1; 

     // 20 steps of the power method 
     double[] v = new double[n]; 
     for (int i = 0; i < n; i++) v[i] = 0; 

     for (int i = 0; i < 10; i++) 
     { 
      MultiplyAtAv(n, u, v); 
      MultiplyAtAv(n, v, u); 
     } 

     // B=AtA   A multiplied by A transposed 
     // v.Bv /(v.v) eigenvalue of v 
     double vBv = 0, vv = 0; 
     for (int i = 0; i < n; i++) 
     { 
      vBv += u[i] * v[i]; 
      vv += v[i] * v[i]; 
     } 

     return Math.Sqrt(vBv/vv); 
    } 


    /* return element i,j of infinite matrix A */ 
    double A(int i, int j) 
    { 
     return 1.0/((i + j) * (i + j + 1)/2 + i + 1); 
    } 

    /* multiply vector v by matrix A */ 
    void MultiplyAv(int n, double[] v, double[] Av) 
    { 
     for (int i = 0; i < n; i++) 
     { 
      Av[i] = 0; 
      for (int j = 0; j < n; j++) Av[i] += A(i, j) * v[j]; 
     } 
    } 

    /* multiply vector v by matrix A transposed */ 
    void MultiplyAtv(int n, double[] v, double[] Atv) 
    { 
     for (int i = 0; i < n; i++) 
     { 
      Atv[i] = 0; 
      for (int j = 0; j < n; j++) Atv[i] += A(j, i) * v[j]; 
     } 
    } 

    /* multiply vector v by matrix A and then by matrix A transposed */ 
    void MultiplyAtAv(int n, double[] v, double[] AtAv) 
    { 
     double[] u = new double[n]; 
     MultiplyAv(n, v, u); 
     MultiplyAtv(n, u, AtAv); 
    } 
} 

Trên phiên bản phát hành máy x86 của tôi mất 4,5 giây để hoàn thành, trong khi x64 mất 9,5 giây. Có bất kỳ cờ/cài đặt cụ thể nào cần thiết cho x64 không?

CẬP NHẬT

Nó chỉ ra rằng RyuJIT có một vai trò trong vấn đề này. Nếu useLegacyJit được bật trong app.config, kết quả sẽ khác và lần này x64 nhanh hơn.

<?xml version="1.0" encoding="utf-8"?> 
<configuration> 
    <startup> 
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6"/> 
    </startup> 
    <runtime> 
    <useLegacyJit enabled="1" /> 
</runtime> 
</configuration> 

CẬP NHẬT

Bây giờ vấn đề này đã được báo cáo cho đội CLR coreclr, issue 993

+0

Tôi không quen thuộc với định mức quang phổ, và đó là một số tiền hợp lý của mã để xem xét. Bạn có thể cho chúng tôi một bản tóm tắt về những gì đang làm - hàng trăm hoặc hàng ngàn phép toán ma trận của các ma trận hai điểm nổi lớn với các căn bậc hai và các bộ phận trong đó ở đâu đó? Bạn có thể cấu hình này trong cả hai, bạn có thể nhìn vào bộ tạo ra cho bất kỳ bi quan rõ ràng? – Rup

+4

Bạn có đang chạy bản phát hành bản phát hành và không chạy nó trong trình gỡ lỗi không? –

+0

Nó có giá trị chạy nó một vài lần trong một vòng lặp 'for' và giảm giá vài lần lặp đầu tiên kể từ khi trình biên dịch JIT cần phải làm việc phép thuật của nó lần đầu tiên. –

Trả lời

4

Lý do cho hồi quy Perf được trả lời trên GitHub; một thời gian ngắn, Nó dường như chỉ repro trên Intel và không phải trên máy Amd64. vòng lặp bên trong hoạt động

Av[i] += v[j] * A(i, j); 

kết quả trong

IN002a: 000093 lea  eax, [rax+r10+1] 
IN002b: 000098 cvtsi2sd xmm1, rax 
IN002c: 00009C movsd xmm2, qword ptr [@RWD00] 
IN002d: 0000A4 divsd xmm2, xmm1 
IN002e: 0000A8 movsxd eax, edi 
IN002f: 0000AB movaps xmm1, xmm2 
IN0030: 0000AE mulsd xmm1, qword ptr [r8+8*rax+16] 
IN0031: 0000B5 addsd xmm0, xmm1 
IN0032: 0000B9 movsd qword ptr [rbx], xmm0 

Cvtsi2sd hiện một ghi một phần thấp hơn 8-byte với byte trên của XMM đăng ký chưa sửa đổi. Đối với trường hợp repro xmm1 được viết một phần nhưng có thêm sử dụng xmm1 xuống mã. Điều này tạo ra sự phụ thuộc sai lệch giữa cvtsi2sd và các hướng dẫn khác sử dụng xmm1 ảnh hưởng đến sự song song lệnh. Thực sự sửa đổi codegen của Int to Float cast để phát ra một "xorps xmm1, xmm1" trước khi cvtsi2sd sửa hồi quy hồi quy.

Cách giải quyết: Perf hồi quy cũng có thể tránh được nếu chúng ta đảo ngược thứ tự của toán hạng trong hoạt động nhân trong các phương pháp MultiplyAv/MultiplyAvt

void MultiplyAv(int n, double[] v, double[] Av) 
{ 
    for (int i = 0; i < n; i++) 
    { 
     Av[i] = 0; 
     for (int j = 0; j < n; j++) 
       Av[i] += v[j] * A(i, j); // order of operands reversed 
    } 
} 
Các vấn đề liên quan