2010-09-07 29 views

Trả lời

12

Không được xác định. Bạn rõ ràng yêu cầu C# - nhưng đó là cái gì đó phụ thuộc vào kiến ​​trúc bộ vi xử lý, tức là trình biên dịch thời gian chạy CLR.

+1

Tôi cho rằng điều này đúng với hầu hết mọi ngôn ngữ lập trình. – Andy

+0

... và những gì tốt hơn được xác định bởi những gì dễ đọc hơn trong ngữ cảnh được sử dụng. –

44

Không; họ cả hai nên biên dịch xuống cùng một điều nếu một là nhanh hơn hoặc tốt hơn.

Quan trọng hơn, hầu hết các lập trình viên có lẽ sẽ tìm thấy > 0 dễ đọc hơn và dễ đọc hơn quan trọng hơn so với tối ưu hóa vi mô như thế này.

+7

+1 để chỉ ra tầm quan trọng của khả năng đọc – Matt

+7

Tôi không biết về xác nhận khả năng đọc của bạn; Tôi đọc> = 1 là "1 trở lên" và đó là cách tự nhiên hơn để xác định điều kiện hơn "nhiều hơn 0". Tuy nhiên, "hơn năm" làm cho cảm giác hoàn hảo trong cuộc trò chuyện. Tôi nghĩ khả năng đọc sẽ phụ thuộc vào ngữ cảnh, và sự hiện diện và từ ngữ của bất kỳ tài liệu yêu cầu hoặc hướng dẫn người dùng nào mà các nhà phát triển sẽ xem xét song song với mã. Nó cũng phụ thuộc rất nhiều vào loại toán liên quan; như Jesse Millikan đã nói, hai so sánh sẽ không tương đương với thế giới dấu phẩy động. – KeithS

+0

@KiethS: Tôi đọc ý định của '> 0' là" số nguyên dương không phải là số nguyên dương ". Đọc '> = 1' khiến tôi phải suy nghĩ về cách các số nguyên không có số thập phân chính xác để quyết định điều đó. Và vâng, các biểu thức không giống nhau; mặc dù tôi đã không thực hiện các chương trình điểm nổi mở rộng trước đây. Có trường hợp nào '> = 1' sẽ tốt hơn không? Chắc chắn rồi. Nhưng với cách viết của câu hỏi tôi giả định "số nguyên dương không phải là dương" là mục đích. –

7

Sự khác biệt hiệu suất sẽ không đáng kể giữa hai (nếu có ngay cả hai). Tôi đang làm việc để chứng minh chính xác những gì nó có thể được (nó sẽ là nền tảng phụ thuộc vì bất kỳ khác nhau có thể sẽ đi xuống mã được phát ra và thực hiện bởi JIT).

Tuy nhiên, hãy nhớ rằng hiệu suất đó là một tối ưu hóa vi mô cực kỳ có khả năng là không chính đáng.

Lựa chọn tốt hơn sẽ dễ đọc hơn và chuyển tải ý định của bạn tốt nhất trong mã của bạn.

1

Để trả lời câu hỏi nhanh hơn, tôi không chắc chắn, nhưng tôi nghĩ chúng tương đương nhau. Và để trả lời tốt hơn, tôi nghĩ nó phụ thuộc vào ngữ cảnh.

0

Bạn sẽ không nhận thấy bất kỳ sự khác biệt nào trừ khi có thể trong một vòng lặp rất rất chặt chẽ là hiệu suất quan trọng trong ứng dụng của bạn. Sau đó, bạn cần phải cấu hình mã của bạn anyway để quyết định cái nào tốt hơn.

Sử dụng ứng dụng có ý nghĩa nhất trong ứng dụng của bạn.

26

Cái tốt hơn là số rõ ràng nhất thể hiện ý định của bạn.

Nếu bạn đang thử nghiệm để xem nếu một số nguyên nằm trong khoảng [1, 6] thì bạn nên viết nó như:

if (x >= 1 && x <= 6) { ... } 

Viết này sẽ làm việc, nhưng không vì thế rõ ràng là thực hiện các đặc điểm kỹ thuật :

if (x > 0 && x < 7) { ... } 

Tôi cũng giả định rằng bạn đang nói về các loại số nguyên tại đây. Nếu bạn đang đối phó với dấu chấm động hoặc số thập phân thì chúng không tương đương.


Trừ khi bạn đã lược tả mã của mình và nhận thấy đây là nút cổ chai, bạn không nên lo lắng về tối ưu hóa vi mô. Mặc dù vậy nó có thể là thú vị để kiểm tra mã được tạo ra bởi trình biên dịch C# trong mỗi trường hợp để xem nếu chúng được biên dịch cho cùng một IL hay không. Điều này có thể được thực hiện bằng cách sử dụng .NET Reflector.

if (x >= 1) 
{ 
    Console.WriteLine("True!"); 
} 

Kết quả trong:

L_000b: ldloc.0   // Load the value of x 
L_000c: ldc.i4.1   // Load the constant 1 
L_000d: blt.s L_0019  // Branch if less than 
L_000f: ldstr "True!" 
L_0014: call void [mscorlib]System.Console::WriteLine(string) 

Trong đó:

if (x > 0) 
{ 
    Console.WriteLine("True!"); 
} 

kết quả trong IL sau:

L_000b: ldloc.0   // Load the value of x 
L_000c: ldc.i4.0   // Load the constant 0 
L_000d: ble.s L_0019  // Branch if less than or equal 
L_000f: ldstr "True!" 
L_0014: call void [mscorlib]System.Console::WriteLine(string) 

Trong cả hai trường hợp, các trình biên dịch đã đảo ngược so sánh. Phép thử "lớn hơn hoặc bằng" được biên dịch thành lệnh "nhỏ hơn" và phép thử "lớn hơn" được biên dịch thành "nhỏ hơn hoặc bằng". Nói chung, trình biên dịch được tự do thực hiện các sửa đổi đó và chạy một phiên bản khác của trình biên dịch có thể tạo ra bytecode khác nhau (nhưng tương đương).

Vì chúng không biên dịch thành cùng một IL, cách tốt nhất để xem nhanh nhất là chạy mã trong vòng lặp và xem mỗi phiên bản thực thi bao lâu. Tôi đã thử làm điều này nhưng tôi không thấy bất kỳ sự khác biệt hiệu suất đo lường giữa hai cách để viết mã.

+0

Tôi đồng ý! Đó là những gì mong đợi của tuyên bố ...> 0 và> = 1 thực sự làm 2 điều khác nhau (và đôi khi giống nhau) ... bạn muốn tất cả hoặc một phần của nó, hơn là không ai trong số đó! –

+0

Tôi thích rằng bạn đã cho thấy IL, đặc biệt là khi nó chuyển đổi so sánh trong bytecode. – Nick

0

Vì vậy, thường là khi tôi so sánh một cái gì đó với> 0 hoặc> = 1, tôi đang cố gắng xem một mảng/bộ sưu tập có chứa bất kỳ phần tử nào không. Nếu đúng như vậy, thay vì sử dụng .Count > 0, hãy thử sử dụng phương thức trợ giúp Enumerable.Any() trong System.Linq sẽ nhanh hơn nhiều.

Nếu không, tôi không biết :)

+0

Tại sao 'Any()' lại nhanh hơn? Tôi nghĩ nó sẽ chậm hơn, vì 'Count' chỉ là một accessor thuộc tính nhưng' Any() 'phải liệt kê thuật ngữ đầu tiên của chuỗi. – jnylen

+0

@jnylen: Tùy thuộc vào số bạn đang sử dụng. 'Enumerable.Count()' còn tệ hơn 'Enumerable.Any()'. Nếu vùng chứa của bạn có thuộc tính 'Đếm' (nghĩa là' Danh sách') thì có, bạn nên sử dụng nó. –

+2

Tôi nghĩ .Any() đánh giá thành .Count> 0 nếu bộ sưu tập hỗ trợ thành viên đó. Và nó luôn luôn nhanh hơn .Count() khi bộ sưu tập không hỗ trợ nó. Không chắc chắn những gì IL nó biên dịch, vì vậy nó có thể không được nhiều giúp đỡ ... –

2

Không có sự khác biệt bởi vì CPU trong nội bộ thực hiện một phép trừ của hai số và kiểm tra kết quả và tràn. Không có thêm bước nào liên quan đến một trong hai hướng dẫn.

Khi nói đến mã, nó phụ thuộc vào những gì bạn đang cố gắng ghi lại. > = 1 có nghĩa là 1 là số thấp nhất có thể. > 0 có nghĩa là 0 không được phép. Có một sự khác biệt nhỏ về ngữ nghĩa mà những người chuyên nghiệp sẽ chú ý. Họ sẽ chọn nhà điều hành phù hợp để ghi lại ý định của họ.

Nếu bạn nghĩ rằng> = n và> = n + 1 được cùng bạn đang nhầm lẫn:> = int.MaxValue và> (int.MaxValue + 1) là khác nhau ^^

3

Tôi đồng ý với các câu trả lời khác mà không nên xem xét tối ưu hóa vi mô thường. Tuy nhiên nó có thể là thú vị để xem mà trong hai phiên bản có nhỏ hơn/appar_faster IL.

Vì vậy:

using System; 

namespace IL_Test 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      int i = 3; 
      if (i > 0) 
      { 
       Console.Write("i is greater than zero"); 
      } 
     } 
    } 
} 

chuyển thành:

(DEBUG)

.method private hidebysig static void Main(string[] args) cil managed 
{ 
    .entrypoint 
    .maxstack 2 
    .locals init (
     [0] int32 i, 
     [1] bool CS$4$0000) 
    L_0000: nop 
    L_0001: ldc.i4.3 
    L_0002: stloc.0 
    L_0003: ldloc.0 
    L_0004: ldc.i4.0 
    L_0005: cgt 
    L_0007: ldc.i4.0 
    L_0008: ceq 
    L_000a: stloc.1 
    L_000b: ldloc.1 
    L_000c: brtrue.s L_001b 
    L_000e: nop 
    L_000f: ldstr "i is greater than zero" 
    L_0014: call void [mscorlib]System.Console::Write(string) 
    L_0019: nop 
    L_001a: nop 
    L_001b: ret 
} 

(CHÍ)

.method private hidebysig static void Main(string[] args) cil managed 
{ 
    .entrypoint 
    .maxstack 2 
    .locals init (
     [0] int32 i) 
    L_0000: ldc.i4.3 
    L_0001: stloc.0 
    L_0002: ldloc.0 
    L_0003: ldc.i4.0 
    L_0004: ble.s L_0010 
    L_0006: ldstr "i is greater than zero" 
    L_000b: call void [mscorlib]System.Console::Write(string) 
    L_0010: ret 
} 

khi

using System; 

namespace IL_Test 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      int i = 3; 
      if (i >= 1) 
      { 
       Console.Write("i is greater than zero"); 
      } 
     } 
    } 
} 

vào

(DEBUG)

.method private hidebysig static void Main(string[] args) cil managed 
{ 
    .entrypoint 
    .maxstack 2 
    .locals init (
     [0] int32 i, 
     [1] bool CS$4$0000) 
    L_0000: nop 
    L_0001: ldc.i4.3 
    L_0002: stloc.0 
    L_0003: ldloc.0 
    L_0004: ldc.i4.1 
    L_0005: clt 
    L_0007: stloc.1 
    L_0008: ldloc.1 
    L_0009: brtrue.s L_0018 
    L_000b: nop 
    L_000c: ldstr "i is greater than zero" 
    L_0011: call void [mscorlib]System.Console::Write(string) 
    L_0016: nop 
    L_0017: nop 
    L_0018: ret 
} 

(CHÍ)

.method private hidebysig static void Main(string[] args) cil managed 
{ 
    .entrypoint 
    .maxstack 2 
    .locals init (
     [0] int32 i) 
    L_0000: ldc.i4.3 
    L_0001: stloc.0 
    L_0002: ldloc.0 
    L_0003: ldc.i4.1 
    L_0004: blt.s L_0010 
    L_0006: ldstr "i is greater than zero" 
    L_000b: call void [mscorlib]System.Console::Write(string) 
    L_0010: ret 
} 

Theo như tôi có thể nhìn thấy i> = 1 là nhẹ nhanh hơn i> 0 TRÊN DEBUG MODE

Trong chế độ phát hành l các diference là tại offset 0004 một BLE vs BLT. Tôi giả sử rằng hai ops IL chuyển thành CPU không kém tiêu thụ ops bản địa ..

+0

Là những trao đổi hoặc tôi bỏ lỡ một cái gì đó? –

+2

Bạn nói một cái nhanh hơn một chút - tại sao bạn không đăng kết quả benchmark? Bạn đã thử nghiệm máy nào? Tối ưu hóa trên? Tối ưu hóa được thực hiện bởi JIT, không phải bởi trình biên dịch C#, do đó, nhìn vào IL tạo ra không nói nhiều về hiệu suất. – Niki

+2

@Caspar: cái đầu tiên đang làm: 'if (i> 0) == false', sau đó trả về (không console.write). Thứ hai là làm: 'if (i <1) == true', sau đó quay trở lại. IL được sản xuất bởi thử nghiệm của Andrei dường như có nhiều chi tiết hơn so với những gì Mark Byers tìm thấy. Điểm được thực hiện bởi những người khác: chạy nó, chuẩn nó, hỏi là nó thực sự chậm hơn? Các mã ở đây có thể được DEBUG biên dịch, hoặc với một phiên bản của trình biên dịch .. chúng tôi không biết. Có, JIT có thể tối ưu hóa nhiều hơn hay không. Hãy đánh dấu nó .. – maxwellb

4

Tất nhiên, nó phụ thuộc vào kiến ​​trúc CPU mà chương trình của bạn sẽ chạy. Trên x86 các hướng dẫn jgejg, có liên quan ở đây, có cùng số chu kỳ IIRC. Trong trường hợp cụ thể của thử nghiệm cho> 0, nếu bạn đang sử dụng số nguyên unsigned nó có thể (tôi thực sự không biết) được nhanh hơn để sử dụng các test hướng dẫn thay vì cmp, vì cho unsigned số nguyên> 0 là tương đương với! = 0 Các kiến ​​trúc khác có thể khác nhau. Vấn đề là ở mức độ thấp đến mức, ngay cả trong trường hợp hiếm hoi mà nó đáng được tối ưu hóa, không có cách nào độc lập về phần cứng để tối ưu hóa nó.

Chỉnh sửa: Quên đề cập: Bất kỳ trình biên dịch hoặc VM nào có giá trị muối của nó sẽ có thể tìm ra rằng thử nghiệm> = 1 tương đương với thử nghiệm> 0 và thực hiện tối ưu hóa tầm thường như vậy nếu nó thậm chí tạo sự khác biệt ở ngôn ngữ assembly cấp độ.

0

Nếu có sự khác biệt giữa hai, sau đó tôi muốn nói rằng đây sẽ là một tối ưu hóa vi mô, điều này không ảnh hưởng đến hiệu suất tổng thể của ứng dụng. Hơn nữa, khi người ta thực sự tìm ra liệu anh ta có phải sử dụng> 0 hoặc> = 1, thì tôi muốn nói chi phí để tìm ra cái nào nhanh hơn, không vượt quá lợi ích tối thiểu (hiệu suất tối thiểu).

Vì vậy, tôi cũng muốn nói rằng bạn nên sử dụng tùy chọn thể hiện ý định nhất.