随机(但重复)置换Java字节数组中所有位的最快方法是什么?我尝试使用BitSet成功完成它,但有更快的方法吗?显然,for循环占用了大部分的cpu时间。
我刚刚在IDE中进行了一些分析,for循环占整个permute()方法中64%的cpu时间。
为了澄清,数组(preRound)包含一个进入过程的现有数字数组。我希望该数组的各个设置位以随机方式混合。这就是P []的原因。它包含一个随机的位位置列表。因此,例如,如果设置了preRound的第13位,则将其转移到postRound的P [13]。这可能位于postRound的20555位置。整个事情是替换 - 置换网络的一部分,我正在寻找最快的方式来置换输入的比特。
我的代码到目前为止......
private byte[] permute(byte[] preRound) {
BitSet beforeBits = BitSet.valueOf(preRound);
BitSet afterBits = new BitSet(blockSize * 8);
for (int i = 0; i < blockSize * 8; i++) {
assert i != P[i];
if (beforeBits.get(i)) {
afterBits.set(P[i]);
}
}
byte[] postRound = afterBits.toByteArray();
postRound = Arrays.copyOf(postRound, blockSize); // Pad with 0s to the specified length
assert postRound.length == blockSize;
return postRound;
}
仅供参考,blockSize约为60,000,P是随机查询表。
答案 0 :(得分:1)
我没有执行任何性能测试,但您可能需要考虑以下事项: 要省略对Arrays.copyOf的调用(它复制了整数使用的long []的副本,这有点令人讨厌),只需设置最后一位,以防它之前没有设置并在之后取消设置。
此外,有一个很好的习惯用法来迭代输入排列中的设置位。
private byte[] permute(final byte[] preRound) {
final BitSet beforeBits = BitSet.valueOf(preRound);
final BitSet afterBits = new BitSet(blockSize*8);
for (int i = beforeBits.nextSetBit(0); i >= 0; i =
beforeBits.nextSetBit(i + 1)) {
final int to = P[i];
assert i != to;
afterBits.set(to);
}
final int lastIndex = blockSize*8-1;
if (afterBits.get(lastIndex)) {
return afterBits.toByteArray();
}
afterBits.set(lastIndex);
final byte[] postRound = afterBits.toByteArray();
postRound[blockSize - 1] &= 0x7F;
return postRound;
}
如果它没有削减它,如果你使用相同的P进行大量迭代,那么考虑将置换转换为循环符号并就地执行转换可能是值得的。 这样你就可以线性迭代P,这可以让你更好地利用缓存(P是字节数组的32倍,假设它是一个int数组)。 然而,你将失去这样的优势:你只需要看1并最终在字节数组中的每个位移位,设置与否。
如果你想避免使用BitSet,你可以手动完成:
private byte[] permute(final byte[] preRound) {
final byte[] result = new byte[blockSize];
for (int i = 0; i < blockSize; i++) {
final byte b = preRound[i];
// if 1s are sparse, you may want to use this:
// if ((byte) 0 == b) continue;
for (int j = 0; j < 8; ++j) {
if (0 != (b & (1 << j))) {
final int loc = P[i * 8 + j];
result[loc / 8] |= (1 << (loc % 8));
}
}
}
return result;
}