- Crate tạo số ngẫu nhiên tiêu biểu của Rust,
rand, có các thao tác thường dùng nằm rải rác ở nhiều trait, nên urandom được phát triển với bề mặt API công khai/triển khai nhỏ hơn và trải nghiệm sử dụng nhất quán hơn - Gom các thao tác cấp cao vào một cấu trúc
Randomvà niêm phong traitRng, ưu tiên khả năng khám phá API và tối ưu hóa nội bộ hơn là hỗ trợ generator tùy ý - Không đưa vào thuật toán tạo số ngẫu nhiên mới; thay vào đó chọn hàm xuất của Xoshiro256 theo từng mục đích, đạt thông lượng cao hơn khoảng 31% so với
rand0.10.2 trong benchmark tạo 1.000 giá trịf64 - Lấy mẫu số nguyên phân bố đều đã hợp nhất đường dẫn tái sử dụng và dùng một lần bằng một triển khai không thiên lệch duy nhất với ngưỡng được tính trễ; trong benchmark phạm vi
500..20_000, nó nhanh hơn cả hai đường dẫn củarand - Đầu ra thô với seed tường minh được bảo đảm tái lập trên các kiến trúc được hỗ trợ và các bản phát hành tương thích SemVer, nhưng phải đánh đổi việc kết nối generator tùy ý cùng hệ sinh thái phân phối và tích hợp bên thứ ba rộng lớn của
rand
API Random được gom về một nơi
- Các thao tác hữu ích của
randnằm rải rác ở nhiều trait- Tạo số ngẫu nhiên trong phạm vi cần
RngExt, chọn từ sequence cầnIndexedRandom, xáo trộn cầnSliceRandom rand0.10 cung cấp các helper ở cấp root nhưrand::random_rangecho lệnh gọi dùng một lần- Nếu muốn giữ handle RNG hoặc dùng các thao tác trên sequence như chọn/xáo trộn, vẫn phải tìm các method thuộc nhiều trait
- Tạo số ngẫu nhiên trong phạm vi cần
- Dù prelude giảm bớt import, bạn vẫn phải biết extension method áp dụng cho kiểu nào trong RNG, slice hay iterator, nên khó tìm chỉ bằng tự động hoàn thành của IDE
- urandom đặt API tiêu thụ cấp cao vào một cấu trúc wrapper
Randomduy nhất- Tạo
Random<urandom::rng::Xoshiro256Rng>bằngurandom::new() - Có thể gọi
uniform,choose,shuffletrên cùng một đối tượng - Tự động hoàn thành có thể hiển thị
random,uniform,chance,choose,shuffle,sample, v.v. - Tất cả đều là method riêng có sẵn, nên không cần tìm hay import trait mở rộng cấp cao
- Tạo
Trait Rng được niêm phong, chọn tối ưu hóa thay vì khả năng mở rộng
randxem trait RNG cấp thấp là điểm mở rộng công khai, nhưng traitRngcủaurandomđược niêm phong, để crate tự chọn và triển khai các generator được hỗ trợ- Không thể gắn generator tùy ý vào
Random - Muốn thêm generator mới thì phải thay đổi chính
urandom
- Không thể gắn generator tùy ý vào
- Nếu mục tiêu là thuật toán tốt hơn, các lựa chọn mặc định theo vai trò hiện nay đã là Xoshiro256 và ChaCha, và khuyến nghị cũng thay đổi chậm
- Nếu có lựa chọn tốt hơn, có thể áp dụng trong một bản phát hành major trong tương lai
- Để tương thích với dự án/ngôn ngữ lập trình/thuật toán legacy/phần cứng đặc thù/generator chuyên cho mô phỏng khác, chỉ giống generator là chưa đủ
- Các thuật toán liên quan như lấy mẫu đều và xáo trộn cũng phải giống nhau, nên một triển khai chuyên biệt đáp ứng toàn bộ hợp đồng sẽ phù hợp hơn
- Nhờ trait được niêm phong, có thể thêm đúng các thao tác thô mà
urandomcần mà không phải thiết kế/tài liệu hóa hợp đồng triển khai cho các generator chưa biết và những tình huống ngoại lệ- Có thể chuyên biệt hóa generator và thuật toán để khớp nhau, cho phép một số tối ưu hóa không thể dùng trong
rand
- Có thể chuyên biệt hóa generator và thuật toán để khớp nhau, cho phép một số tối ưu hóa không thể dùng trong
- Với phần lớn ứng dụng, lựa chọn entropy hữu ích hơn việc triển khai PRNG mới
- Các generator cụ thể công khai constructor
from_seednative - Có thể tạo
Randomvới seed tường minh nhưChaCha12Rng::from_seed(seed) - Không nhận triển khai RNG tùy ý, nhưng vẫn giữ các điểm mở rộng mà người dùng nâng cao được dự đoán sẽ cần
- Các generator cụ thể công khai constructor
Cải thiện hiệu năng từ cùng thuật toán
urandomkhông dùng thuật toán tạo số ngẫu nhiên mới- Trên hệ 64-bit,
urandom::new()cho mục đích phi mật mã vàrand::rngs::SmallRngdùng cùng họ Xoshiro256 urandom::csprng()cho mục đích mật mã vàrand::rngs::StdRngdùng ChaCha12- Generator bên trong của hàm tiện ích
rand::rng()cũng là ChaCha12
- Trên hệ 64-bit,
- Giao diện generator của
randcung cấp word số nguyên và lấp đầy byte, nên ngay cả phân phối cầnf64cũng yêu cầu cảu64trước urandom::Rngcung cấp không chỉnext_u32,next_u64mà cảnext_f32vànext_f64- Số ngẫu nhiên dấu phẩy động cần ít bit ngẫu nhiên hơn một word đầy đủ
- Generator có thể override các method này bằng hàm xuất rẻ hơn
- Triển khai Xoshiro dùng chung chuyển đổi trạng thái nhưng tách đường xuất
- Giữ Xoshiro256++ cho
u64 - Với
u32và dấu phẩy động, dùng Xoshiro256+ nhanh hơn, có các bit cao được thiết kế phù hợp mục đích đó
- Giữ Xoshiro256++ cho
- Kết quả microbenchmark tạo 1.000 số ngẫu nhiên bằng
urandom1.0 vàrand0.10.2 như sau- Xoshiro
u64: cả hai đều 814ns - Xoshiro
u32:rand836ns,urandom788ns - Xoshiro
f64:rand1.033ns,urandom788ns - ChaCha12
f64:rand2.199ns,urandom2.011ns
- Xoshiro
- Thông lượng đầu-cuối của Xoshiro
f64cao hơn khoảng 31% và thời gian chạy ngắn hơn 24%, nhưng đường dẫnu64thực hiện cùng công việc thì gần như hòa - ChaCha12 không override
next_f64, nên hiệu năng nhìn chung tương tự - Thời gian chính xác thay đổi theo máy và compiler; có thể xem điều kiện chi tiết trong ghi chú benchmark đầy đủ
Một đường dẫn lấy mẫu đều hợp nhất
- Nếu chỉ lấy phần dư của số nguyên theo độ dài phạm vi, sẽ sinh ra thiên lệch, nên lấy mẫu số nguyên đều đúng cách phải loại bỏ một phần đầu ra của generator
- Tính ngưỡng loại bỏ chính xác cần phép lấy dư tốn kém
- Nếu sampler được dùng lặp lại, chi phí thiết lập ban đầu có thể chấp nhận được
- Khi chỉ tạo một giá trị, chi phí này trở nên tương đối lớn
randphơi bày khác biệt này qua traitUniformSamplerUniformIntđược tạo ra tính trước ngưỡng để lấy mẫu không thiên lệchRng::random_rangedùng hooksample_singlehoặcsample_single_inclusiveriêng để tránh thiết lập ban đầu- Ở tính năng mặc định, đường tắt dùng một lần sử dụng một thuật toán thứ hai hơi thiên lệch
- Tính năng tùy chọn
unbiasedthay nó bằng một phiên bản lặp phức tạp hơn
urandomtính trễ ngưỡng và dùng một triển khai nhân–loại bỏ không thiên lệch duy nhất cho cả phạm vi tái sử dụng lẫn dùng một lần- Theo phương pháp được mô tả trong bài báo năm 2018 của Daniel Lemire, Fast Random Integer Generation in an Interval
- Với hầu hết phạm vi thực tế, ứng viên đầu tiên được trả về trước khi cần phép chia
- Nếu không thể trả về ứng viên đầu tiên, ngưỡng chính xác được tính rồi lặp lại không thiên lệch
- Cũng xử lý ngoại lệ
range == 0khi yêu cầu toàn bộ phạm vi
- Cùng một triển khai xử lý phân phối tái sử dụng và phạm vi dùng một lần mà không cần method riêng, thuật toán thứ hai, chi phí thiết lập trước hay đường nhanh thiên lệch
- Kết quả benchmark lấy 1.000 mẫu trong phạm vi
500..20_000như sauUniformInttái sử dụng:rand1.098ns,urandom950ns- Phạm vi dùng một lần:
rand1.079ns,urandom942ns
- Kết quả của
randdựa trên tính năng mặc định, nên dòng dùng một lần nhanh hơn là đường hơi thiên lệch, trong khiurandomkhông thiên lệch mà vẫn nhanh hơn cả hai đường dẫn
Khả năng tái lập xuyên suốt bản phát hành và kiến trúc
urandomxem khả năng tái lập là một phần của hợp đồng công khai- Với cùng seed tường minh và cùng thứ tự gọi RNG cấp thấp, đầu ra thô của generator quyết định luận được giữ nguyên
- Bảo đảm ổn định trên các kiến trúc được hỗ trợ và trong toàn bộ các bản phát hành tương thích SemVer
- Server 64-bit và client WebAssembly 32-bit có thể dùng cùng nền tảng generator để phát lại
- Để duy trì tương thích này, hiệu năng trên kiến trúc 32-bit bị đánh đổi
- Đây là bảo đảm mạnh hơn chính sách tái lập của
rand- Các generator portable và thuật toán lấy mẫu của
randcó thể thay đổi đầu ra trong bản phát hành minor SmallRngvàStdRngđược nêu rõ là không portable và cũng có thể thay đổi theo nền tảng hoặc bản phát hành thư viện
- Các generator portable và thuật toán lấy mẫu của
Cái giá của lựa chọn và tiêu chí áp dụng
urandomgom các thao tác phổ biến vàoRandomđể dễ tìm mà không cần trait mở rộng- Thiết kế generator và phân phối cùng nhau để triển khai đường xuất Xoshiro rẻ hơn và một đường lấy mẫu đều không thiên lệch duy nhất
- Dòng thô ổn định của generator được seed tường minh có thể dùng cho game và mô phỏng quyết định luận
- Đổi lại, không thể mang generator tùy ý vào, và cũng không có danh sách phân phối lớn hơn cùng hệ sinh thái tích hợp bên thứ ba mà
randcung cấp - Nếu cần hệ sinh thái rộng,
randlà lựa chọn phù hợp; nếu ưu tiên bề mặt API nhỏ, khả năng khám phá, tối ưu hóa tích hợp và chính sách tái lập mạnh, có thể chọnurandom - Có thể xem package tại crates.io, tài liệu API và mã nguồn GitHub
1 bình luận
Ý kiến trên Lobste.rs
Có đủ lý do để fork
rand, nhưng cái tênurandomlại nghe như một thư viện liên quan đến/dev/urandomTôi đồng ý với vấn đề được nêu ra, nhưng không thích
pub fn new() -> Random<impl Rng + Clone>Nếu tham số hóa toàn bộ ứng dụng thành
Random<T> where T: Rngthì sẽ phát sinh thêm nhiều việc phiền phức, và các vấn đề về thời gian biên dịch cùngdynsẽ trở nên nghiêm trọng. Tôi thà đểstruct Randomcó kiểu cụ thể, hoặc phương án thứ hai là chọnstruct Random<T = rng::Xoshiro256Rng>Vì cảm giác bức bối tương tự nên tôi cũng từng tự làm một cái, nhưng không phải bản fork, và ít tính năng hơn
randrất nhiềuThật vui khi thấy có người gặp vấn đề giống tôi và thực sự bắt tay vào giải quyết. Có vẻ Rust kỳ lạ ở chỗ có xu hướng khiến người ta tạo ra các thư viện “súp trait”
Kiểu dữ liệu cốt lõi của cơ sở dữ liệu tôi làm ở công ty phải implement ít nhất 15 trait, nên tự động hoàn thành trở nên lộn xộn và tài liệu cũng rối rắm. Tôi đã giảm bớt được một phần số trait, nhưng thường bị chặn bởi các vấn đề như phụ thuộc vòng hoặc không thể viết các bài kiểm thử cốt lõi
Thư viện này khiến tôi nhớ одновременно đến giao diện sâu của APOSD và công việc mật mã của Filippo được thiết kế để khó dùng sai, và cả hai đều là lời khen rất lớn
urandom::new()không trả về trình tạo số ngẫu nhiên an toàn về mặt mật mã, nên đây chưa phải một thiết kế hoàn toàn không thể dùng sai. Đặc biệt là vì/dev/urandomtrên Linux lại an toàn, nên càng dễ gây nhầm lẫnMột lựa chọn thay thế khác cho
randlàfastrand, một trình tạo số ngẫu nhiên đơn giản và nhanh. Nó đơn giản hơnrandvàurandom, nhưng cũng ít tính năng hơn