【问题标题】:Fastest possible byte array concatenation method最快的字节数组连接方法
【发布时间】:2012-05-02 14:53:23
【问题描述】:

我得到了一个包含 n 部分消息作为字节数组的映射。在最后一块进入地图后,必须将消息连接起来。我找到了两个应该满足要求的解决方案。第一个是使用 System.arraycopy:

public byte[] getMessageBytes() throws IOException {
    byte[] bytes = new byte[0];
    for (final Map.Entry<Short,byte[]> entry : myMap.entrySet()) {
        byte[] entryBytes = entry.getValue();
        byte[] temp = new byte[bytes.length + entryBytes.length];
        System.arraycopy(bytes, 0, temp, 0, bytes.length);
        System.arraycopy(entryBytes, 0, temp, bytes.length, entryBytes.length);
        bytes = temp;
    }
    return bytes;
}

第二个是使用ByteArrayOutputStream:

public byte[] getMessageBytes() throws IOException {
    final ByteArrayOutputStream baos = new ByteArrayOutputStream();
    for (final Map.Entry<Short,byte[]> entry : myMap.entrySet()) {
        baos.write(entry.getValue());
    }
    baos.flush();
    return baos.toByteArray();
}

从性能和内存使用的角度来看,更好的方法是什么? 有没有更好的连接方式?

【问题讨论】:

  • 您是否对这些进行了基准测试并发现它们不够快?
  • 这真的是你的瓶颈吗?
  • @Poindexter 我发现如果地图有很多条目,这需要一些时间在洞的过程中。所以我寻求改进。
  • 你能去掉地图的使用并从头开始连接吗?
  • 通常最好边做边做。你能从一开始就创建一个 ByteArrayOutputStream 而不是一个 Map 吗?顺便说一句,Short 和 Integer 使用相同数量的内存。

标签: java performance bytearray concatenation


【解决方案1】:

由于您可以通过将片段的长度相加来找出消息的大小,所以我会:

  1. 将片段的长度相加,并分配输出数组;
  2. 使用循环将arraycopy() 的每一块放到输出数组中的正确位置。

这可能是内存效率高且速度快。然而,只有剖析才能讲述完整的故事。

【讨论】:

    【解决方案2】:

    这应该比你的第一个版本更好(未测试)

    public byte[] getMessageBytes() throws IOException {
        long amount = 0L;
        long offset = 0L;
        // no reason to use entrySet() if you just use the values
        for (byte[] arr : myMap.values()) {
            amount += arr.length;
        }
        byte[] dest = new byte[amount];
        for (byte[] arr : myMap.values()) {
            System.arraycopy(arr, 0, dest, offset, arr.length);
            offset += arr.length;
        }
        return dest;
    }
    

    (这个答案大致相当于aix的)

    【讨论】:

    • 这是否假设您希望连接数组的顺序是设置值的顺序?已经有一段时间没有写java了,但是我不认为values() Set 有顺序保证吗?
    • @Aidos 是的,但是由于 OP 使用了它,所以我也使用了。虽然不能保证顺序(除非它是 SortedMap),但我知道的所有地图实现都会以相同的顺序迭代多次调用,除非地图发生变化
    【解决方案3】:

    正确的答案是针对您的具体情况进行测试和比较。

    这是一个 SortedMap,比如 TreeMap,还是你实际上是在随机合并字节?

    【讨论】:

      【解决方案4】:

      为每次迭代创建一个新数组的第一个解决方案是 O(n^2),如果您有很多条目,这是一个问题。而且它相当复杂。

      使用 ByteArrayOutputStream 更好有两个原因:它在 O(n) 中运行,而且非常简单。

      最有可能快一点的是:如果先计算总大小,然后使用 System.arraycopy。但我只会在 ByteArrayOutputStream 真的太慢的情况下这样做。

      【讨论】:

        【解决方案5】:

        先计算大小并分配一次结果应该是返回字节数组最快的解决方案。

        如果您稍后将生成的字节数组用作InputStream,并且根据数组的组合大小,最快的方法可能是根本不连接。在这种情况下,您可以创建一个 SequenceInputStream 包装多个 ByteArrayInputStreams。未经测试的示例代码:

        Collection<byte[]> values = map.values();
        List<ByteArrayInputStream> streams = new ArrayList<ByteArrayInputStream>(values.size());
        for (byte[] bytes : values) {
            streams.add(new ByteArrayInputStream(bytes));
        }
        return new SequenceInputStream(Collections.enumeration(streams));
        

        【讨论】:

          【解决方案6】:

          这是本地代码。

              /*
               * public static void arraycopy(Object src, int srcPos, Object dest,
               *      int destPos, int length)
               *
               * The description of this function is long, and describes a multitude
               * of checks and exceptions.
               */
              static void Dalvik_java_lang_System_arraycopy(const u4* args, JValue* pResult)
              {
                  ArrayObject* srcArray = (ArrayObject*) args[0];
                  int srcPos = args[1];
                  ArrayObject* dstArray = (ArrayObject*) args[2];
                  int dstPos = args[3];
                  int length = args[4];
                  /* Check for null pointers. */
                  if (srcArray == NULL) {
                      dvmThrowNullPointerException("src == null");
                      RETURN_VOID();
                  }
                  if (dstArray == NULL) {
                      dvmThrowNullPointerException("dst == null");
                      RETURN_VOID();
                  }
                  /* Make sure source and destination are arrays. */
                  if (!dvmIsArray(srcArray)) {
                      dvmThrowArrayStoreExceptionNotArray(((Object*)srcArray)->clazz, "source");
                      RETURN_VOID();
                  }
                  if (!dvmIsArray(dstArray)) {
                      dvmThrowArrayStoreExceptionNotArray(((Object*)dstArray)->clazz, "destination");
                      RETURN_VOID();
                  }
                  /* avoid int overflow */
                  if (srcPos < 0 || dstPos < 0 || length < 0 ||
                      srcPos > (int) srcArray->length - length ||
                      dstPos > (int) dstArray->length - length)
                  {
                      dvmThrowExceptionFmt(gDvm.exArrayIndexOutOfBoundsException,
                          "src.length=%d srcPos=%d dst.length=%d dstPos=%d length=%d",
                          srcArray->length, srcPos, dstArray->length, dstPos, length);
                      RETURN_VOID();
                  }
                  ClassObject* srcClass = srcArray->clazz;
                  ClassObject* dstClass = dstArray->clazz;
                  char srcType = srcClass->descriptor[1];
                  char dstType = dstClass->descriptor[1];
                  /*
                   * If one of the arrays holds a primitive type, the other array must
                   * hold the same type.
                   */
                  bool srcPrim = (srcType != '[' && srcType != 'L');
                  bool dstPrim = (dstType != '[' && dstType != 'L');
                  if (srcPrim || dstPrim) {
                      if (srcPrim != dstPrim || srcType != dstType) {
                          dvmThrowArrayStoreExceptionIncompatibleArrays(srcClass, dstClass);
                          RETURN_VOID();
                      }
                      if (false) ALOGD("arraycopy prim[%c] dst=%p %d src=%p %d len=%d",
                          srcType, dstArray->contents, dstPos,
                          srcArray->contents, srcPos, length);
                      switch (srcType) {
                      case 'B':
                      case 'Z':
                          /* 1 byte per element */
                          memmove((u1*) dstArray->contents + dstPos,
                              (const u1*) srcArray->contents + srcPos,
                              length);
                          break;
                      case 'C':
                      case 'S':
                          /* 2 bytes per element */
                          move16((u1*) dstArray->contents + dstPos * 2,
                              (const u1*) srcArray->contents + srcPos * 2,
                              length * 2);
                          break;
                      case 'F':
                      case 'I':
                          /* 4 bytes per element */
                          move32((u1*) dstArray->contents + dstPos * 4,
                              (const u1*) srcArray->contents + srcPos * 4,
                              length * 4);
                          break;
                      case 'D':
                      case 'J':
                          /*
                           * 8 bytes per element.  We don't need to guarantee atomicity
                           * of the entire 64-bit word, so we can use the 32-bit copier.
                           */
                          move32((u1*) dstArray->contents + dstPos * 8,
                              (const u1*) srcArray->contents + srcPos * 8,
                              length * 8);
                          break;
                      default:        /* illegal array type */
                          ALOGE("Weird array type '%s'", srcClass->descriptor);
                          dvmAbort();
                      }
                  } else {
                      /*
                       * Neither class is primitive.  See if elements in "src" are instances
                       * of elements in "dst" (e.g. copy String to String or String to
                       * Object).
                       */
                      const int width = sizeof(Object*);
                      if (srcClass->arrayDim == dstClass->arrayDim &&
                          dvmInstanceof(srcClass, dstClass))
                      {
                          /*
                           * "dst" can hold "src"; copy the whole thing.
                           */
                          if (false) ALOGD("arraycopy ref dst=%p %d src=%p %d len=%d",
                              dstArray->contents, dstPos * width,
                              srcArray->contents, srcPos * width,
                              length * width);
                          move32((u1*)dstArray->contents + dstPos * width,
                              (const u1*)srcArray->contents + srcPos * width,
                              length * width);
                          dvmWriteBarrierArray(dstArray, dstPos, dstPos+length);
                      } else {
                          /*
                           * The arrays are not fundamentally compatible.  However, we
                           * may still be able to do this if the destination object is
                           * compatible (e.g. copy Object[] to String[], but the Object
                           * being copied is actually a String).  We need to copy elements
                           * one by one until something goes wrong.
                           *
                           * Because of overlapping moves, what we really want to do
                           * is compare the types and count up how many we can move,
                           * then call move32() to shift the actual data.  If we just
                           * start from the front we could do a smear rather than a move.
                           */
                          Object** srcObj;
                          int copyCount;
                          ClassObject*   clazz = NULL;
                          srcObj = ((Object**)(void*)srcArray->contents) + srcPos;
                          if (length > 0 && srcObj[0] != NULL)
                          {
                              clazz = srcObj[0]->clazz;
                              if (!dvmCanPutArrayElement(clazz, dstClass))
                                  clazz = NULL;
                          }
                          for (copyCount = 0; copyCount < length; copyCount++)
                          {
                              if (srcObj[copyCount] != NULL &&
                                  srcObj[copyCount]->clazz != clazz &&
                                  !dvmCanPutArrayElement(srcObj[copyCount]->clazz, dstClass))
                              {
                                  /* can't put this element into the array */
                                  break;
                              }
                          }
                          if (false) ALOGD("arraycopy iref dst=%p %d src=%p %d count=%d of %d",
                              dstArray->contents, dstPos * width,
                              srcArray->contents, srcPos * width,
                              copyCount, length);
                          move32((u1*)dstArray->contents + dstPos * width,
                              (const u1*)srcArray->contents + srcPos * width,
                              copyCount * width);
                          dvmWriteBarrierArray(dstArray, 0, copyCount);
                          if (copyCount != length) {
                              dvmThrowArrayStoreExceptionIncompatibleArrayElement(srcPos + copyCount,
                                      srcObj[copyCount]->clazz, dstClass);
                              RETURN_VOID();
                          }
                      }
                  }
                  RETURN_VOID();
              }
          

          【讨论】:

            猜你喜欢
            • 2022-01-09
            • 1970-01-01
            • 2018-04-26
            • 1970-01-01
            • 2021-07-09
            • 2012-02-04
            • 2013-06-25
            • 2015-11-28
            • 1970-01-01
            相关资源
            最近更新 更多