Là một so sánh 1 <10 rẻ hơn 1 <1000000?


65

Tôi mới chỉ sử dụng ~ 1 tỷ làm số liệu cho z-indexCSS và đã suy nghĩ về những so sánh phải tiếp tục. Có sự khác biệt về hiệu suất ở cấp độ ALU so sánh giữa số lượng rất lớn so với số lượng rất nhỏ không?

Ví dụ: một trong hai đoạn này có đắt hơn đoạn kia không?

snippet 1

for (int i = 0; i < 10000000; i++){
    if (i < 10000000000000) {
        //do nothing
    }
}

snippet 2

for (int i = 0; i < 10000000; i++){
    if (i < 1000) {
        //do nothing
    }
}


12
OP không hỏi việc phân nhánh sẽ mất bao nhiêu thời gian. Rõ ràng, ví dụ này nhằm đảm bảo rằng nó mất chính xác cùng một thời gian trong cả hai đoạn. Câu hỏi là về việc CMPhướng dẫn máy cá nhân sẽ chậm hơn nếu ilớn hơn.
Kilian Foth

18
Vì điều này được thực hiện trong CSS, việc chuyển đổi một chuỗi thành một số nguyên có thể sẽ chi phối chính hoạt động so sánh về thời gian thực hiện.

58
Nếu bạn cần sử dụng 1000000000 làm chỉ mục z trong tệp CSS, bạn đã làm sai điều gì đó.
Bergi 2/2/2015

6
Đối với CSS, chi phí chuyển đổi văn bản thành số nguyên sẽ phụ thuộc vào số chữ số được chuyển đổi (trong đó một số có 6 chữ số như 1000000 có thể đắt gấp 6 lần số 1 chữ số như 1); và chi phí này có thể là các đơn đặt hàng có cường độ lớn hơn tổng chi phí so sánh số nguyên.
Brendan

Câu trả lời:


82

Mọi bộ xử lý mà tôi đã làm đều so sánh bằng cách trừ đi một trong các toán hạng khác, loại bỏ kết quả và để lại các cờ của bộ xử lý (không, âm, v.v.). Vì phép trừ được thực hiện như một thao tác đơn lẻ, nên nội dung của toán hạng không thành vấn đề.

Cách tốt nhất để trả lời câu hỏi chắc chắn là biên dịch mã của bạn thành tập hợp và tham khảo tài liệu của bộ xử lý đích cho các hướng dẫn được tạo. Đối với các CPU Intel hiện tại, đó sẽ là Hướng dẫn dành cho nhà phát triển phần mềm Kiến trúc Intel 64 và IA-32 .

Mô tả của CMPhướng dẫn ("so sánh") nằm trong tập 2A, trang 3-126 hoặc trang 618 của PDF và mô tả hoạt động của nó như sau:

temp ← SRC1 − SignExtend(SRC2);
ModifyStatusFlags; (* Modify status flags in the same manner as the SUB instruction*)

Điều này có nghĩa là toán hạng thứ hai được mở rộng ký nếu cần thiết, được trừ khỏi toán hạng thứ nhất và kết quả được đặt trong một khu vực tạm thời trong bộ xử lý. Sau đó, các cờ trạng thái được đặt theo cùng một cách như đối với SUBhướng dẫn ("trừ") (trang 1492 của PDF).

Không có đề cập nào trong tài liệu CMPhoặc SUBtài liệu rằng các giá trị của toán hạng có bất kỳ ảnh hưởng nào đến độ trễ, vì vậy mọi giá trị bạn sử dụng đều an toàn.


5
Điều gì xảy ra nếu số quá lớn đối với số học 32 bit? Sau đó nó sẽ không được phân chia để tính toán chậm hơn?
Falco

3
@Falco Không phải trên CPU có ALU 64 bit (gần như là tất cả trong số chúng ngoại trừ trong không gian được nhúng trong những ngày này.)
thiệu lại vào

8
@Falco: Có, nhưng vì câu hỏi hỏi về hiệu suất ALU, nên hàm ý là các giá trị phù hợp với kích thước từ của CPU hoặc khả năng của bất kỳ hướng dẫn SIMD nào có thể có. Hoạt động với số lượng lớn hơn số đó sẽ phải được thực hiện với nhiều hướng dẫn bên ngoài CPU. Điều đó rất phổ biến 30 năm trước khi bạn chỉ có các thanh ghi 8- hoặc 16 bit để làm việc.
Blrfl

6
@Falco Làm thế nào mà yêu cầu gỡ lỗi? Đó không phải là một lỗi; chỉ chậm hơn một chút khi thực hiện op 64 bit trên CPU không hỗ trợ ops 64 bit. Đề xuất rằng không bao giờ nên sử dụng một số trên 2 ^ 31-1 có vẻ hơi vô lý.
thiệu lại

2
@Falco Có nói rằng, các công cụ kết xuất trong trình duyệt thậm chí sử dụng số nguyên để biểu diễn các chỉ số z không? Hầu hết các công cụ kết xuất Tôi quen thuộc với việc sử dụng các phao chính xác đơn cho mọi thứ (cho đến giai đoạn rasterization cuối cùng), nhưng tôi chưa thực sự nghiên cứu các công cụ kết xuất trình duyệt.
thiệu lại vào

25

Có sự khác biệt về hiệu suất ở cấp độ ALU so sánh giữa số lượng rất lớn so với số lượng rất nhỏ không?

Điều đó rất khó xảy ra, trừ khi việc chuyển từ một số nhỏ sang một số lớn thay đổi loại số của bạn, hãy nói từ một intsang a long. Ngay cả khi đó, sự khác biệt có thể không đáng kể. Bạn có nhiều khả năng thấy sự khác biệt nếu ngôn ngữ lập trình của bạn âm thầm chuyển sang số học chính xác tùy ý dưới vỏ bọc.

Tuy nhiên, trình biên dịch cụ thể của bạn có thể đang thực hiện một số tối ưu hóa thông minh mà bạn không biết. Cách bạn tìm ra là để đo lường. Chạy một hồ sơ trên mã của bạn; xem những so sánh mất nhiều thời gian nhất. Hoặc đơn giản là bắt đầu và dừng một bộ đếm thời gian.


Cần phải đề cập rằng các Số được đề xuất trong Câu hỏi có loại số khác nhau trong loại số nguyên 32 bit điển hình ...
Falco

19

Nhiều bộ xử lý có các hướng dẫn "nhỏ" có thể thực hiện các phép toán số học, bao gồm các phép so sánh, trên các toán hạng được chỉ định ngay lập tức. Các toán tử khác với các giá trị đặc biệt đó phải sử dụng định dạng lệnh lớn hơn hoặc, trong một số trường hợp, phải sử dụng lệnh "tải giá trị từ bộ nhớ". Ví dụ, trong tập lệnh ARM Cortex-M3, có ít nhất năm cách một giá trị có thể được so sánh với hằng số:

    cmp r0,#1      ; One-word instruction, limited to values 0-255

    cmp r0,#1000   ; Two-word instruction, limited to values 0-255 times a power of 2

    cmn r0,#1000   ; Equivalent to comparing value with -1000
                   ; Two-word instruction, limited to values 0-255 times a power of 2

    mov r1,#30000  ; Two words; can handle any value 0-65535
    cmp r0,r1      ; Could use cmn to compare to values -1 to -65535

    ldr r1,[constant1000000] ; One or two words, based upon how nearby the constant is
    cmp r0,r1
    ...

constant1000000:
    dd  1000000

Hình thức đầu tiên là nhỏ nhất; hình thức thứ hai và thứ ba có thể hoặc không thể thực thi nhanh chóng, tùy thuộc vào tốc độ của bộ nhớ mà từ đó mã được tìm nạp. Dạng biểu mẫu thứ tư gần như chắc chắn sẽ chậm hơn ba dạng đầu tiên và dạng thứ năm thậm chí chậm hơn, nhưng dạng thứ hai có thể được sử dụng với bất kỳ giá trị 32 bit nào.

Trên các bộ xử lý x86 cũ hơn, các hướng dẫn so sánh dạng ngắn sẽ thực thi nhanh hơn các bộ xử lý dạng dài, nhưng nhiều bộ xử lý mới hơn sẽ chuyển đổi cả dạng dài và dạng ngắn thành cùng một biểu diễn khi chúng được tìm nạp lần đầu và lưu trữ biểu diễn thống nhất đó trong bộ đệm. Do đó, trong khi các bộ điều khiển nhúng (như các bộ điều khiển được tìm thấy trên nhiều nền tảng di động) sẽ có sự khác biệt về tốc độ, nhiều máy tính dựa trên x86 sẽ không.

Cũng lưu ý rằng trong nhiều trường hợp sử dụng hằng số trong vòng lặp, trình biên dịch sẽ chỉ cần tải hằng số vào một thanh ghi một lần - trước khi vòng lặp bắt đầu - hiển thị sự phân biệt thời gian. Mặt khác, có một số tình huống, ngay cả trong các vòng nhỏ, nơi điều đó sẽ không luôn xảy ra; nếu một vòng lặp nhỏ nhưng được thực hiện nhiều, đôi khi có thể có một hiệu suất chính giữa các so sánh liên quan đến các giá trị ngắn hạn và các vòng lặp liên quan đến các giá trị dài hơn.


Trên MIPS, bạn chỉ có thể có các bit 16 bit, do đó, chắc chắn so sánh với 1 sẽ ngắn hơn và (có thể) nhanh hơn 1000000. Có thể giống với Sparc và PowerPC. Và tôi nghĩ tôi đã đọc từ một số nguồn tin rằng Intel cũng sẽ tối ưu hóa hoạt động trên immediates nhỏ trong một số trường hợp, nhưng tôi không chắc chắn để so sánh hay không
phuclv

@ LưuViênPhúc: Một thanh ghi có thể được tải trước vòng lặp. Tại thời điểm đó, so sánh thực tế sẽ có cùng số lượng hướng dẫn trong cả hai trường hợp.
cHao 3/2/2015

Vì Loop chỉ là một ví dụ của op và câu hỏi ví dụ là chỉ số z, nếu bạn có 1000 đối tượng, mỗi đối tượng có chỉ số z riêng và bạn đặt chúng thành 100000000 ... 1000000999 hoặc 10000 ... 10999 và bạn lặp lại chúng để sắp xếp trước khi kết xuất, có nhiều so sánh và nhiều hướng dẫn tải. Ở đó nó có thể làm cho một sự khác biệt!
Falco

@Falco: Trong trường hợp đó, ngay lập tức sẽ không có yếu tố nào; tải và so sánh với một đăng ký dường như không thể tránh khỏi.
cHao 4/2/2015

@cHao: Nếu một người đang so sánh các chỉ số Z với nhau, họ sẽ ở trong sổ đăng ký. Nếu một người đang xử lý một số chỉ số nhất định khác nhau có thể đòi hỏi phải so sánh ngay lập tức. Thông thường các hằng số sẽ được tải trước khi một vòng lặp bắt đầu, nhưng nếu ví dụ, một vòng lặp cần đọc các cặp giá trị từ bộ nhớ và so sánh giá trị đầu tiên của mỗi cặp với năm hằng số khác nhau (không cách đều nhau) trong phạm vi 100000 đến 100499 và giá trị khác với năm hằng số khác như vậy, có thể nhanh hơn rất nhiều để trừ 100250 (được giữ trong sổ đăng ký) và sau đó so sánh với các giá trị -250 đến 250 ...
supercat

5

Câu trả lời ngắn cho câu hỏi này là, không , không có sự khác biệt về thời gian để so sánh hai số dựa trên cường độ của những số đó giả sử chúng được lưu trữ trong cùng một loại dữ liệu (ví dụ: cả int 32 bit hoặc cả 64 bit.)

Hơn nữa, với kích thước từ của ALU , việc so sánh hai số nguyên với nhau sẽ không bao giờ mất hơn 1 chu kỳ, vì đây là một phép toán tầm thường tương đương với phép trừ. Tôi nghĩ rằng mọi kiến ​​trúc tôi từng xử lý đều có so sánh số nguyên một chu kỳ.

Các trường hợp duy nhất tôi có thể nghĩ là tôi đã gặp phải khi so sánh hai số không phải là phép toán một chu kỳ như sau:

  • Hướng dẫn khi thực sự có độ trễ bộ nhớ trong tìm nạp toán hạng, nhưng điều đó không liên quan gì đến cách so sánh hoạt động (và thường không thể thực hiện được trên các kiến ​​trúc RISC, mặc dù thường có thể có trên các thiết kế CISC, như x86 / x64.)
  • So sánh điểm nổi có thể là nhiều chu kỳ, tùy thuộc vào kiến ​​trúc.
  • Các số trong câu hỏi không phù hợp với kích thước từ của ALU và do đó, việc so sánh phải được chia thành nhiều hướng dẫn.

4

Câu trả lời của @ RobertHarvey là tốt; xem xét câu trả lời này một bổ sung cho mình.


Bạn cũng nên xem xét Dự đoán chi nhánh :

Trong kiến ​​trúc máy tính, bộ dự báo nhánh là một mạch kỹ thuật số cố gắng đoán xem nhánh nào (ví dụ cấu trúc if-then-other) sẽ đi trước khi điều này được biết chắc chắn. Mục đích của bộ dự báo nhánh là cải thiện dòng chảy trong đường ống dẫn. Các bộ dự báo nhánh đóng một vai trò quan trọng trong việc đạt được hiệu suất cao trong nhiều kiến ​​trúc bộ vi xử lý hiện đại như x86.

Về cơ bản, trong ví dụ của bạn, nếu ifcâu lệnh bên trong vòng lặp luôn trả về cùng một câu trả lời, thì hệ thống có thể tối ưu hóa nó bằng cách đoán chính xác cách nó sẽ phân nhánh. Trong ví dụ của bạn, vì ifcâu lệnh trong trường hợp đầu tiên luôn trả về cùng một kết quả, nó sẽ chạy nhanh hơn một chút so với trường hợp thứ hai.

Câu hỏi chồng chéo tuyệt vời về chủ đề này


Dự đoán chi nhánh ảnh hưởng đến thời gian phân nhánh, nhưng không phải là thời gian so sánh.
thiệu lại

3

Nó phụ thuộc vào việc thực hiện, nhưng nó sẽ rất, rất khó xảy ra .

Tôi thừa nhận rằng tôi chưa đọc qua các chi tiết triển khai của các công cụ trình duyệt khác nhau và CSS không chỉ định bất kỳ loại lưu trữ cụ thể nào cho các số. Nhưng tôi tin rằng sẽ an toàn khi giả định rằng tất cả các trình duyệt chính đang sử dụng các số dấu phẩy động chính xác kép 64 bit ("nhân đôi", để mượn một thuật ngữ từ C / C ++) để xử lý hầu hết các nhu cầu số của chúng trong CSS , bởi vì đây là những gì JavaScript sử dụng cho các số và do đó, việc sử dụng cùng loại giúp tích hợp dễ dàng hơn.

Từ quan điểm của máy tính, tất cả các nhân đôi đều mang cùng một lượng dữ liệu: 64 bit, cho dù giá trị là 1 hoặc -3,14 hoặc 1000000 hoặc 1e100 . Lượng thời gian cần thiết để thực hiện một thao tác trên những con số này không phụ thuộc vào giá trị thực của những con số đó, bởi vì nó luôn hoạt động trên cùng một lượng dữ liệu. Có một sự đánh đổi khi thực hiện mọi thứ theo cách này, trong đó nhân đôi không thể biểu diễn chính xác tất cả các số (hoặc thậm chí tất cả các số trong phạm vi của chúng), nhưng chúng có thể đủ gần cho hầu hết các vấn đề và các loại CSS không phải là số - đủ để cần độ chính xác cao hơn thế. Kết hợp điều này với các lợi ích của khả năng tương thích trực tiếp với JavaScript và bạn đã có một trường hợp khá mạnh để nhân đôi.

Không ai có thể thực hiện CSS bằng cách sử dụng mã hóa có độ dài thay đổi cho các số. Nếu ai đó sử dụng mã hóa có độ dài thay đổi, thì việc so sánh với số lượng nhỏ sẽ ít tốn kém hơn so với so sánh với số lượng lớn, bởi vì số lượng lớn có nhiều dữ liệu hơn để khủng hoảng . Các loại mã hóa này có thể chính xác hơn nhị phân, nhưng chúng cũng chậm hơn rất nhiều và đặc biệt đối với CSS, mức tăng độ chính xác có lẽ không đủ để đạt được hiệu suất. Tôi sẽ rất ngạc nhiên khi biết rằng bất kỳ trình duyệt nào cũng làm mọi thứ theo cách này.

Bây giờ, về mặt lý thuyết, có một ngoại lệ có thể xảy ra với mọi thứ tôi đã nói ở trên: so sánh với số 0 thường nhanh hơn so với các số khác . Điều này không phải vì số 0 là ngắn (nếu đó là lý do, thì 1 sẽ nhanh như vậy, nhưng không phải vậy). Đó là bởi vì số không cho phép bạn gian lận. Đó là số duy nhất có tất cả các bit bị tắt, vì vậy nếu bạn biết rằng một trong các giá trị bằng 0, bạn thậm chí không phải xem giá trị khác là một số: nếu có bất kỳ bit nào trên thì nó không bằng không, và sau đó bạn chỉ cần nhìn vào một bit để xem nó lớn hơn hoặc nhỏ hơn 0.


0

Nếu mã này được diễn giải mỗi lần nó chạy, sẽ có một sự khác biệt vì phải mất nhiều thời gian hơn để mã hóa và giải thích 10000000000000so với 1000. Tuy nhiên, đây là tối ưu hóa rõ ràng đầu tiên của các thông dịch viên trong trường hợp này: mã thông báo một lần và giải thích mã thông báo.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.