/* This file is part of JOP, the Java Optimized Processor see <http://www.jopdesign.com/> Copyright (C) Martin Schoeberl <martin@jopdesign.com> Thomas B. Preusser <thomas.preusser@tu-dresden.de> This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ package csp.stream; import csp.PrivateScope; import org.bouncycastle.crypto.BlockCipher; import org.bouncycastle.crypto.CipherParameters; import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.crypto.engines.AESLightEngine; import com.jopdesign.io.IOFactory; import com.jopdesign.io.SysDevice; import com.jopdesign.sys.Startup; import java.util.Random; /** * Streaming benchmark with four processing stages: * * 1. Data Generation 2. Encryption 3. Decryption 4. Verification * * This benchmark can, by its nature, not scale beyond 4 independent Threads. * The measured performance will typically scale even less due to * platform-dependent imbalances of the computational complexities of the * individual stages. Moreso, the serialized version running within a single * Thread may even perform best on platforms with significant cache memory due * to the improved locality. Thus, this benchmark also evaluates the performance * of the inter-Thread communication. * * @author Thomas B. Preusser <thomas.preusser@tu-dresden.de> * @author Martin Schoeberl <martin@jopdesign.com> */ public class AES { final static int BLOCK_SIZE = 128; // This MUST be a multiple of 16. final static int POOL_LENGTH = 8; final BufferQueue q1, q2, q3, free; final Source source; final Encrypt enc; final Decrypt dec; final Sink sink; final Runnable[] runners; private int blockCnt; private volatile boolean finished; public AES() { // Pool of Data Blocks q1 = new BufferQueue(POOL_LENGTH); q2 = new BufferQueue(POOL_LENGTH); q3 = new BufferQueue(POOL_LENGTH); free = new BufferQueue(POOL_LENGTH); for (int i = 0; i < POOL_LENGTH; ++i) { final byte[] block = new byte[BLOCK_SIZE]; free.checkedEnq(block); } final CipherParameters params; { // Encryption Parameters final byte[] key = new byte[16]; final Random rnd = new Random(127); for (int i = key.length; i > 0; key[--i] = (byte) rnd.nextInt()) ; params = new KeyParameter(key); } source = new Source(); enc = new Encrypt(params); dec = new Decrypt(params); sink = new Sink(); runners = new Runnable[] { source, enc, dec, sink }; } public String toString() { return "AES"; } protected Runnable[] getWorkers() { return runners; } protected int getDepth() { return 4; } protected void reset(int cnt) { finished = false; source.reset(); enc.reset(); dec.reset(); sink.reset(); blockCnt = cnt; } protected boolean isFinished() { return finished; } private class Source implements Runnable { private final Random rnd; private int cnt; PrivateScope scope; public Source() { rnd = new Random(); scope = new PrivateScope(1000); } public void reset() { rnd.setSeed(127); cnt = 0; } public void run() { while (cnt < blockCnt) { if (!free.empty() && !q1.full()) { final byte[] block = free.deq(); for (int i = 0; i < block.length; i++) { block[i] = (byte) rnd.nextInt(); } q1.enq(block); ++cnt; } } } } private class Encrypt implements Runnable { private final BlockCipher crypt; private int cnt; private byte[] ciph; public Encrypt(CipherParameters params) { crypt = new AESLightEngine(); crypt.init(true, params); ciph = new byte[BLOCK_SIZE]; reset(); } public void reset() { cnt = 0; } public void run() { while (cnt < blockCnt) { if (!q1.empty() && !q2.full()) { final byte[] block = q1.deq(); int ofs = BLOCK_SIZE; do { ofs -= crypt.getBlockSize(); crypt.processBlock(block, ofs, ciph, ofs); } while (ofs > 0); q2.enq(ciph); ciph = block; ++cnt; } } } } private class Decrypt implements Runnable { private final BlockCipher decrypt; private int cnt; private byte[] deciph; public Decrypt(CipherParameters params) { decrypt = new AESLightEngine(); decrypt.init(false, params); deciph = new byte[BLOCK_SIZE]; reset(); } public void reset() { cnt = 0; } public void run() { while (cnt < blockCnt) { if (!q2.empty() && !q3.full()) { final byte[] block = q2.deq(); int ofs = BLOCK_SIZE; do { ofs -= decrypt.getBlockSize(); decrypt.processBlock(block, ofs, deciph, ofs); } while (ofs > 0); q3.enq(deciph); deciph = block; ++cnt; } } } } private class Sink implements Runnable { private final Random rnd; private int cnt; private boolean ok; public Sink() { rnd = new Random(); } public void reset() { rnd.setSeed(127); cnt = 0; ok = true; } public void run() { while (cnt < blockCnt) { if (!q3.empty() && !free.full()) { final byte[] block = q3.deq(); for (int i = 0; i < block.length; i++) { if (block[i] != (byte) rnd.nextInt()) ok = false; } free.enq(block); ++cnt; } } } } public static void main(String[] args) { SysDevice sys = IOFactory.getFactory().getSysDevice(); AES aes = new AES(); // Initialization for benchmarking int start = 0; int stop = 0; int time = 0; System.out.println("AES Benchmark"); int nrCpu = Runtime.getRuntime().availableProcessors(); if (nrCpu < 4) { throw new Error("Not enogh CPUs"); } // ni must be translated from proc index to NoC address! Startup.setRunnable(aes.source, 0); Startup.setRunnable(aes.enc, 1); Startup.setRunnable(aes.dec, 2); aes.reset(1000); // start the other CPUs System.out.println("starting cpus."); sys.signal = 1; start = (int) System.currentTimeMillis(); aes.sink.run(); // End of measurement stop = (int) System.currentTimeMillis(); System.out.println("StartTime: " + start); System.out.println("StopTime: " + stop); time = stop - start; System.out.println("TimeSpent: " + time); System.out.println("Result = " + aes.sink.ok); } }