有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

网络Java优化将值作为位写入bytebuffer

我目前正在编写一些网络代码(这是我的第一台服务器),并有一个关于优化特定函数的快速问题,该函数将值写入位,然后将它们打包成字节。优化此函数的原因是,在将数据打包发送到多个客户端时,每个服务器都会使用它数千次

为了更好地解释函数试图实现的功能,可以使用一个示例: 值3可由两位表示。 在二进制中,它看起来像00000011。函数将把这个二进制值转换成11000000。再次调用该函数时,它将知道从第三个最高有效位(从右/十进制32开始的第三个)开始,最多向当前字节写入6位。如果还有剩余的位要写,它将从一个新字节开始

如果有多个小于字节的值,则这样做的目的是节省空间

我当前的函数如下所示:

 private ByteBuffer out = ByteBuffer.allocate(1024);
 private int bitIndex = 0;
 /*
  * Value: The value to write
  * Amount: The number of bits to represent the value in.
  */
     public OutputBuffer writeBits(long value, int amount) {
    if (bitIndex != 0) {
        int remainingBits = 8 - bitIndex;
        int bytePos = out.position() - 1;
        byte current = out.get(bytePos);
        int shiftAmount = amount - remainingBits;
        int bitsWritten = amount < remainingBits ? amount : remainingBits;
        int clearShiftAmount = 8 - bitsWritten + 56;

        byte b;
        if (shiftAmount < 0) {
            b = (byte) (current | (value << remainingBits - amount));
        } else {
            //deal with negative values
            long temp = (value >> shiftAmount);
            temp =  (temp << clearShiftAmount);
            temp = (byte) (temp >>> clearShiftAmount);
            b = (byte) (current | temp);
        }
        out.put(bytePos,b);
        bitIndex = (bitIndex + bitsWritten) % 8;
        amount -= bitsWritten;
    }
    if (amount <= 0) {
        return this;
    }
    bitIndex = amount & 7;
    int newAmount = amount - bitIndex;
    //newValue should not equal 2047
    for (int i = 0; i != newAmount; i += 8) {
        writeByte((byte) ((value >> i)), false);
    }
    if (bitIndex > 0)
        writeByte((byte) (value << (8 - bitIndex)), false);
    return this;
}

由于我是新手,我认为可能有更有效的方法,可能使用位屏蔽或某种查找表?任何正确方向的想法或方向都会很好。干杯


共 (3) 个答案

  1. # 1 楼答案

    像这样的怎么样

    private ByteBuffer out = ByteBuffer.allocate(1024);
    private int position = 0;
    private int dataBits = 0;
    private long data = 0;
    
    /**
     * value: The value to write
     * amount: The number of bits to represent the value in.
     */
    public void writeBits(long value, int amount) {
        if (amount <= 0) {
            // need to flush what's left
            if (dataBits > 0) {
                dataBits = Byte.SIZE;
            }
        } else {
            int totalBits = dataBits + amount;
    
            // need to handle overflow?
            if (totalBits > Long.SIZE) {
                // the new data is to big for the room that remains;  by how much?
                int excess = totalBits - Long.SIZE;
    
                // drop off the excess and write what's left
                writeBits(value >> excess, amount - excess);
    
                // now we can continue processing just the rightmost excess bits
                amount = excess;
            }
    
            // push the bits we're interested in all the way to the left of the long
            long temp = value << (Long.SIZE - amount);
    
            // make room for any existing (leftover) data bits, filling with zeros to the left (important)
            temp = temp >> dataBits;
    
            // append the new data to the existing
            data |= temp;
    
            // account for new bits of data
            dataBits += amount;
        }
    
        while (dataBits >= Byte.SIZE) {
            // shift one byte left, rotating the byte that falls off into the rightmost byte
            data = Long.rotateLeft(data, Byte.SIZE);
    
            // add the rightmost byte to the buffer
            out.put(position++, (byte)(data & 0xff));
    
            // drop off the rightmost byte
            data &= 0xffffffffffffff00L;
    
            // setup for next byte
            dataBits -= Byte.SIZE;
        }
    }
    
  2. # 2 楼答案

    这里有一个使用递归的更好的解决方案(比我以前的解决方案),它非常适合这个问题

    private static final long[] mask = { 0, 0x1, 0x3, 0x7, 0xf, 0x1f, 0x3f, 0x7f, 0xff };
    
    private ByteBuffer out = ByteBuffer.allocate(1024);
    private int position = 0;
    private int dataBits = 0;
    private byte remainder = 0;
    
    /**
     * value: The value to write
     * amount: The number of bits to represent the value in.
     */
    public void writeBits(long value, int amount) {
        if (amount <= Long.SIZE) {
            if (amount > 0) {
                // left align the bits in value
                writeBitsLeft(value << (Long.SIZE - amount), amount);
            } else {
                // flush what's left
                out.put(position++, remainder);
            }
        } else {
            // the data provided is invalid
            throw new IllegalArgumentException("the amount of bits to write is out of range");
        }
    }
    
    /**
     * write amount bits from the given value
     * 
     * @param value represents bits aligned to the left of a long
     * @param amount bits left to be written from value
     */
    private void writeBitsLeft(long value, int amount) {
        if (amount > 0) {
            // how many bits are left to be filled in the current byte?
            int room = Byte.SIZE - dataBits;
    
            // how many bits are we going to add to the current byte?
            int taken = Math.min(amount, room);
    
            // rotate those number of bits into the rightmost position
            long temp = Long.rotateLeft(value, taken);
    
            // add count taken to the count of bits in the current byte
            dataBits += taken;
    
            // add in that number of data bits
            remainder &= temp & mask[taken];
    
            // have we filled the byte yet?
            if (Byte.SIZE == dataBits) {
                out.put(position++, remainder);
    
                // reset the current byte
                remainder = 0;
                dataBits = 0;
    
                // process any bits left over
                writeBitsLeft(temp, amount - taken);
            }
        } 
    } // writeBitsLeft()
    

    此解决方案具有较少的数学运算、移位运算和if语句,因此应该比原始解决方案更有效,更不用说它可能更容易理解

  3. # 3 楼答案

    好的,我调整了你原来的算法,去掉了一些多余的数学运算,我减少了大约10%(在我的机器上从0.016毫秒减少到了大约0.014毫秒)。我还修改了测试,使每个算法运行1000次

    在最后一个for循环中似乎也有一些节省,因为相同的位被反复移位。如果你能以某种方式保留上一次换班的结果,那可能会有所帮助。但这会改变字节输出的顺序,因此需要更多的思考

    public void writeBits3(long value, int amount) {
        if (bitIndex != 0) {
            int remainingBits = 8 - bitIndex;
            int bytePos = out.position() - 1;
            byte current = out.get(bytePos);
            int shiftAmount = amount - remainingBits;
    
            int bitsWritten = 0;
            if (shiftAmount < 0) {
                bitsWritten = amount;
                out.put(bytePos, (byte) (current | (value << -shiftAmount)));
            } else {
                bitsWritten = remainingBits;
                out.put(bytePos, (byte) (current | (value >> shiftAmount)));
            }
    
            bitIndex += bitsWritten;
            amount -= bitsWritten;
            if (bitIndex >= 8) {
                bitIndex = 0;
            }
        }
        if (amount <= 0) {
            return;
        }
        bitIndex = amount & 7;
        int newAmount = amount - bitIndex;
        long newValue = (value >> bitIndex);
        for (; newAmount >= 8; newAmount -= 8) {
            out.put((byte) (newValue >> newAmount));
        }
        out.put((byte) (value << (8 - bitIndex)));
    }