1 // The Computer Language Benchmarks Game
2 // http://benchmarksgame.alioth.debian.org/
4 // contributed by the Rust Project Developers
6 // Copyright (c) 2012-2014 The Rust Project Developers
8 // All rights reserved.
10 // Redistribution and use in source and binary forms, with or without
11 // modification, are permitted provided that the following conditions
14 // - Redistributions of source code must retain the above copyright
15 // notice, this list of conditions and the following disclaimer.
17 // - Redistributions in binary form must reproduce the above copyright
18 // notice, this list of conditions and the following disclaimer in
19 // the documentation and/or other materials provided with the
22 // - Neither the name of "The Computer Language Benchmarks Game" nor
23 // the name of "The Computer Language Shootout Benchmarks" nor the
24 // names of its contributors may be used to endorse or promote
25 // products derived from this software without specific prior
26 // written permission.
28 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
31 // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
32 // COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
33 // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
34 // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
35 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36 // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
37 // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
38 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
39 // OF THE POSSIBILITY OF SUCH DAMAGE.
42 use std::io::{self, Write};
43 use std::sync::{Arc, Mutex};
47 const LINE_LEN: usize = 60;
49 const BLOCK_LINES: usize = 512;
50 const BLOCK_THOROUGHPUT: usize = LINE_LEN * BLOCK_LINES;
51 const BLOCK_LEN: usize = BLOCK_THOROUGHPUT + BLOCK_LINES;
53 const STDIN_BUF: usize = (LINE_LEN + 1) * 1024;
56 const ALU: &'static [u8] =
57 b"GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGG\
58 GAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGA\
59 CCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAAT\
60 ACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCA\
61 GCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGG\
62 AGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCC\
63 AGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAA";
65 const IUB: &'static [(u8, f32)] =
66 &[(b'a', 0.27), (b'c', 0.12), (b'g', 0.12),
67 (b't', 0.27), (b'B', 0.02), (b'D', 0.02),
68 (b'H', 0.02), (b'K', 0.02), (b'M', 0.02),
69 (b'N', 0.02), (b'R', 0.02), (b'S', 0.02),
70 (b'V', 0.02), (b'W', 0.02), (b'Y', 0.02)];
72 const HOMOSAPIENS: &'static [(u8, f32)] =
73 &[(b'a', 0.3029549426680),
74 (b'c', 0.1979883004921),
75 (b'g', 0.1975473066391),
76 (b't', 0.3015094502008)];
79 // We need a specific Rng,
80 // so implement this manually
81 const MODULUS: u32 = 139968;
82 const MULTIPLIER: u32 = 3877;
83 const ADDITIVE: u32 = 29573;
85 // Why doesn't rust already have this?
86 // Algorithm directly taken from Wikipedia
87 fn powmod(mut base: u64, mut exponent: u32, modulus: u64) -> u64 {
92 if exponent & 1 == 1 {
104 // Just a typical LCRNG
110 pub fn new() -> Rng {
114 pub fn max_value() -> u32 {
118 pub fn normalize(p: f32) -> u32 {
119 (p * MODULUS as f32).floor() as u32
122 pub fn gen(&mut self) -> u32 {
123 self.last = (self.last * MULTIPLIER + ADDITIVE) % MODULUS;
127 // This allows us to fast-forward the RNG,
128 // allowing us to run it in parallel.
129 pub fn future(&self, n: u32) -> Rng {
130 let a = MULTIPLIER as u64;
131 let b = ADDITIVE as u64;
132 let m = MODULUS as u64;
134 // (a^n - 1) mod (a-1) m
135 // x_k = ((a^n x_0 mod m) + --------------------- b) mod m
138 // Since (a - 1) divides (a^n - 1) mod (a-1) m,
139 // the subtraction does not overflow and thus can be non-modular.
142 (powmod(a, n, m) * self.last as u64) % m +
143 (powmod(a, n, (a-1) * m) - 1) / (a-1) * b;
145 Rng { last: (new_seed % m) as u32 }
150 // This will end up keeping track of threads, like
151 // in the other multithreaded Rust version, in
152 // order to keep writes in order.
154 // This is stolen from another multithreaded Rust
155 // implementation, although that implementation
156 // was not able to parallelize the RNG itself.
157 struct BlockSubmitter<W: io::Write> {
159 pub waiting_on: usize,
162 impl<W: io::Write> BlockSubmitter<W> {
163 fn submit(&mut self, data: &[u8], block_num: usize) -> Option<io::Result<()>> {
164 if block_num == self.waiting_on {
165 self.waiting_on += 1;
166 Some(self.submit_async(data))
173 fn submit_async(&mut self, data: &[u8]) -> io::Result<()> {
174 self.writer.write_all(data)
179 // For repeating strings as output
180 fn fasta_static<W: io::Write>(
187 // The aim here is to print a short(ish) string cyclically
188 // with line breaks as appropriate.
190 // The secret technique is to repeat the string such that
191 // any wanted line is a single offset in the string.
193 // This technique is stolen from the Haskell version.
195 try!(writer.write_all(header));
197 // Maximum offset is data.len(),
198 // Maximum read len is LINE_LEN
199 let stream = data.iter().cloned().cycle();
200 let mut extended: Vec<u8> = stream.take(data.len() + LINE_LEN + 1).collect();
204 let write_len = min(LINE_LEN, n);
205 let end = offset + write_len;
208 let tmp = extended[end];
209 extended[end] = b'\n';
210 try!(writer.write_all(&extended[offset..end + 1]));
214 offset %= data.len();
221 // For RNG streams as output
222 fn fasta<W: io::Write + Send + 'static>(
223 submitter: &Arc<Mutex<BlockSubmitter<W>>>,
230 // There's another secret technique in use here:
231 // we generate a lookup table to cache search of the
234 // The secret technique used is stolen from Haskell's
235 // implementation, and is the main secret to the Haskell
236 // implementation's speed.
237 fn gen_lookup_table(aa: &[(u8, f32)]) -> Vec<u8> {
238 let mut table = Vec::with_capacity(Rng::max_value() as usize + 1);
240 let mut cumulative_prob = 0.0;
241 let mut cumulative_norm = 0;
243 for &(byte, prob) in aa {
244 let last_norm = cumulative_norm;
245 cumulative_prob += prob;
246 cumulative_norm = min(Rng::max_value(), Rng::normalize(cumulative_prob)) + 1;
248 table.extend((0..cumulative_norm - last_norm).map(|_| byte));
255 try!(submitter.lock().unwrap().submit_async(header));
258 let lookup_table = Arc::new(gen_lookup_table(table));
260 let thread_count = 4; // avoid external dependency
261 let mut threads = Vec::new();
262 for block_num in (0..thread_count) {
263 let offset = BLOCK_THOROUGHPUT * block_num;
265 let local_submitter = submitter.clone();
266 let local_lookup_table = lookup_table.clone();
267 let local_rng = rng.future(offset as u32);
269 threads.push(thread::spawn(move || {
274 n.saturating_sub(offset),
281 for thread in threads {
282 try!(thread.join().unwrap());
285 *rng = rng.future(n as u32);
290 // A very optimized writer.
291 // I have a feeling a simpler version wouldn't slow
292 // things down too much, though, since the RNG
293 // is the really heavy hitter.
294 fn gen_block<W: io::Write>(
295 submitter: Arc<Mutex<BlockSubmitter<W>>>,
296 lookup_table: Arc<Vec<u8>>,
299 mut block_num: usize,
303 // Include newlines in block
304 length += length / LINE_LEN;
305 let block: &mut [u8] = &mut [b'\n'; BLOCK_LEN];
309 let gen_into = &mut block[..min(length, BLOCK_LEN)];
311 // Write random numbers, skipping newlines
312 for (i, byte) in gen_into.iter_mut().enumerate() {
313 if (i + 1) % (LINE_LEN + 1) != 0 {
314 *byte = lookup_table[rng.gen() as usize];
320 if length >= BLOCK_LEN { &mut *block }
321 else if length % (LINE_LEN + 1) == 0 { &mut block[..length] }
322 else { &mut block[..length + 1] }
325 *write_out.last_mut().unwrap() = b'\n';
327 match submitter.lock().unwrap().submit(write_out, block_num) {
328 Some(result) => { try!(result); break; }
329 None => std::thread::yield_now()
332 block_num += block_stride;
333 rng = rng.future((BLOCK_THOROUGHPUT * (block_stride - 1)) as u32);
334 length = length.saturating_sub(BLOCK_LEN * (block_stride - 1));
336 length = length.saturating_sub(BLOCK_LEN);
343 fn run<W: io::Write + Send + 'static>(writer: W) -> io::Result<()> {
344 let n = std::env::args_os().nth(1)
345 .and_then(|s| s.into_string().ok())
346 .and_then(|n| n.parse().ok())
349 let rng = &mut Rng::new();
351 // Use automatic buffering for the static version...
352 let mut writer = io::BufWriter::with_capacity(STDIN_BUF, writer);
353 try!(fasta_static(&mut writer, b">ONE Homo sapiens alu\n", ALU, n * 2));
355 // ...but the dynamic version does its own buffering already
356 let writer = try!(writer.into_inner());
357 let submitter = Arc::new(Mutex::new(BlockSubmitter { writer: writer, waiting_on: 0 }));
359 { submitter.lock().unwrap().waiting_on = 0; }
360 try!(fasta(&submitter, b">TWO IUB ambiguity codes\n", &IUB, rng, n * 3));
361 { submitter.lock().unwrap().waiting_on = 0; }
362 try!(fasta(&submitter, b">THREE Homo sapiens frequency\n", &HOMOSAPIENS, rng, n * 5));
369 run(io::stdout()).unwrap()