2016-10-11 26 views
6

Gần đây tôi đã nhận ra rằng tôi có thể tạo các hàm cục bộ trong Rust (một hàm trong một hàm). Có vẻ như một cách hay để dọn sạch mã của tôi mà không gây ô nhiễm không gian chức năng của một tệp. Mẫu nhỏ về ý nghĩa của tôi bên dưới theo hàm cục bộ so với hàm 'bên ngoài':Có bất kỳ ý nghĩa hiệu suất tiêu cực nào đối với việc sử dụng các hàm cục bộ trong Rust không?

fn main() { 
    fn local_plus(x: i64, y: i64) -> i64 { 
     x + y 
    } 
    let x = 2i64; 
    let y = 5i64; 

    let local_res = local_plus(x, y); 
    let external_res = external_plus(x,y); 
    assert_eq!(local_res, external_res); 
} 

fn external_plus(x: i64, y: i64) -> i64 { 
    x + y 
} 

Tôi đã tự hỏi liệu có bất kỳ ý nghĩa hiệu suất tiêu cực nào khi thực hiện việc này không? Giống như Rust tái tuyên bố chức năng hoặc chiếm một số lượng không gian chức năng không mong muốn mỗi lần hàm chứa chạy? Hay nó có nghĩa đen không có ý nghĩa hiệu suất?

Một chút mẹo nhỏ về cách tôi có thể tìm ra câu trả lời cho chính mình (hoặc thông qua việc đọc bất kỳ bộ tài liệu cụ thể nào hoặc công cụ tôi có thể sử dụng).

+1

Bạn có thể tự tìm thấy nó bằng cách chạy một số điểm chuẩn hoặc kiểm tra đầu ra MIR/assembly như tôi đã làm. – ljedrz

+0

@ljedrz: Điểm chuẩn là những con thú không ổn định, tốt nhất hãy kiểm tra IR/lắp ráp MIR/LLVM không được tối ưu hóa và tối ưu hóa. –

+0

@MatthieuM. đồng ý - điểm chuẩn phù hợp hơn cho các trường hợp phức tạp hơn, nơi các cách khác không còn thực tế nữa. – ljedrz

Trả lời

5

Không có tác động; Tôi đã kiểm tra lắp ráp được tạo ra cho cả hai biến thể và nó là giống hệt nhau.

Hai phiên bản tôi so:

"bên ngoài":

fn main() { 
    let x = 2i64; 
    let y = 5i64; 

    let external_res = external_plus(x,y); 
} 

fn external_plus(x: i64, y: i64) -> i64 { 
    x + y 
} 

"địa phương":

fn main() { 
    fn local_plus(x: i64, y: i64) -> i64 { 
     x + y 
    } 
    let x = 2i64; 
    let y = 5i64; 

    let local_res = local_plus(x, y); 
} 

Và cả hai mang lại kết quả asm cùng (chế độ phát hành trong ngày hôm nay đêm) :

.text 
    .file "rust_out.cgu-0.rs" 
    .section .text._ZN8rust_out4main17hb497928495d48c40E,"ax",@progbits 
    .p2align 4, 0x90 
    .type _ZN8rust_out4main17hb497928495d48c40E,@function 
_ZN8rust_out4main17hb497928495d48c40E: 
    .cfi_startproc 
    retq 
.Lfunc_end0: 
    .size _ZN8rust_out4main17hb497928495d48c40E, .Lfunc_end0-_ZN8rust_out4main17hb497928495d48c40E 
    .cfi_endproc 

    .section .text.main,"ax",@progbits 
    .globl main 
    .p2align 4, 0x90 
    .type main,@function 
main: 
    .cfi_startproc 
    movq %rsi, %rax 
    movq %rdi, %rcx 
    leaq _ZN8rust_out4main17hb497928495d48c40E(%rip), %rdi 
    movq %rcx, %rsi 
    movq %rax, %rdx 
    jmp [email protected] 
.Lfunc_end1: 
    .size main, .Lfunc_end1-main 
    .cfi_endproc 


    .section ".note.GNU-stack","",@progbits 

Điều đó có nghĩa là sẽ không có sự khác biệt (không chỉ hiệu năng) trong nhị phân được tạo ra.

Còn gì nữa, thậm chí không quan trọng nếu bạn sử dụng hàm; phương pháp sau:

fn main() { 
    let x = 2i64; 
    let y = 5i64; 

    let res = x + y; 
} 

Cũng mang lại cùng một cụm.

Điểm mấu chốt là, nói chung, các hàm được gạch chân bất kể bạn khai báo chúng trong main() hoặc bên ngoài nó.

Sửa: như Shepmaster chỉ ra, trong chương trình này không có tác dụng phụ, do đó lắp ráp tạo ra cho cả hai biến thể thực sự là giống như một cho:

fn main() {} 

Tuy nhiên, sản lượng MIR cho cả hai là như nhau, quá (và khác với một cho một trống main()), do đó, không nên có bất kỳ sự khác biệt đến từ vị trí chức năng ngay cả khi tác dụng phụ đã có mặt.

+2

Tôi khá chắc chắn trình tối ưu hóa đang xóa mọi thứ trong chương trình, vì nó không có tác dụng phụ quan sát được. 'res' là hoàn toàn không được sử dụng, vì vậy tôi nghĩ bạn sẽ thấy tương tự cho' fn main() {} '. – Shepmaster

+0

@Shepmaster TIL :). Tôi sẽ thêm điều này vào câu trả lời. – ljedrz

6

Một chút lời khuyên về cách tôi có thể tìm ra câu trả lời cho bản thân mình (hoặc thông qua việc đọc bất kỳ bộ tài liệu cụ thể nào hoặc công cụ tôi có thể sử dụng).

Bạn có biết the Rust playground không?

Nhập mã của bạn, nhấp vào "LLVM IR", "Assembly" hoặc "MIR" thay vì "Run" và bạn sẽ thấy biểu diễn mức thấp được phát ra cho mã được nói là gì.

Cá nhân tôi thích LLVM IR (tôi thường đọc nó từ C++), vẫn còn cao hơn cấp độ trong khi vẫn đang là bài đăng ngôn ngữ.

Tôi đã tự hỏi liệu có bất kỳ ý nghĩa hiệu suất tiêu cực nào khi thực hiện việc này không?

Đó là một câu hỏi rất phức tạp; thực ra.

Chỉ khác biệt duy nhất giữa việc khai báo hàm cục bộ hoặc bên ngoài trong Rust là một trong phạm vi. Khai báo nó cục bộ chỉ đơn giản là giảm phạm vi của nó. Không có gì khác.

Tuy nhiên ... phạm vi và mức sử dụng, có thể có tác động mạnh đến việc biên dịch.

Một hàm chỉ được sử dụng một lần, ví dụ, có nhiều khả năng được gạch chân hơn một hàm được sử dụng 10 lần. Trình biên dịch không thể dễ dàng ước tính số lượng sử dụng của hàm pub (không bị chặn), nhưng có kiến ​​thức hoàn hảo cho các hàm cục bộ hoặc không phải là pub. Và cho dù một chức năng được inlined hay không có thể ảnh hưởng mạnh đến hồ sơ hiệu suất (cho tồi tệ hơn hoặc tốt hơn). Vì vậy, bằng cách giảm phạm vi, và do đó hạn chế việc sử dụng, bạn đang khuyến khích trình biên dịch xem xét chức năng của bạn cho nội tuyến (trừ khi bạn đánh dấu nó là "lạnh").

Mặt khác, vì phạm vi bị giảm, nó không thể được chia sẻ (rõ ràng).

Vậy ... cái gì?

Làm theo cách sử dụng: xác định mục trong phạm vi hẹp nhất có thể.

Đây là đóng gói: bây giờ, lần tiếp theo bạn cần sửa đổi đoạn này, bạn sẽ biết chính xác phạm vi ảnh hưởng.

Có một số tin tưởng trong Rust, nó sẽ không giới thiệu chi phí nếu nó có thể tránh được.

+0

Câu trả lời hay! Nitor nhỏ mặc dù: có thể bạn muốn nhấn mạnh rằng không có hiệu ứng * trực tiếp * nào. Ví dụ, trong Java có một số loại "lớp bên trong" tự động có tham chiếu đến lớp bên ngoài. Cùng với Rust đóng cửa (chỉ cần nói). Vì vậy, tôi muốn nói rằng điều quan trọng là phải làm rõ rằng các hàm bên trong không tham chiếu bất cứ thứ gì từ hàm kèm theo của chúng và không chiếm không gian ngăn xếp của hàm kèm theo. Chỉ các hiệu ứng * gián tiếp * trên nội tuyến. –

+0

@LukasKalbertodt: Tôi đã thêm một số chữ đậm ở đây. Tôi miễn cưỡng bắt đầu một danh sách những gì nó KHÔNG ảnh hưởng vì tôi sợ nó có thể khiến mọi người nghĩ rằng có lẽ nếu nó không có trong danh sách nó bị ảnh hưởng, thay vào đó tôi nhấn mạnh (nhiều hơn) nó chỉ ảnh hưởng phạm vi và không có gì khác. –

Các vấn đề liên quan