将Add()与addAll()插入Java优先级堆

将Add()与addAll()插入Java优先级堆,java,hadoop,heap,priority-queue,Java,Hadoop,Heap,Priority Queue,我正在研究在Java中在堆中添加值的各种可能性。我正在使用PriorityHeap类。当我注意到我的应用程序运行缓慢时,我决定看看这个。我添加了数千个,有时甚至数百万个自定义条目(我有一个自定义类,它有3个字段:int、LongWritable和Text,都来自hadoop.io;instrumentation agent说我的记录平均有200字节) 很明显,使用addAll()而不是add()方法将条目放入堆中会提高性能,仅仅因为这样可以避免一些heapify操作吗? 我使用以下新示例尝试了不

我正在研究在Java中在堆中添加值的各种可能性。我正在使用
PriorityHeap
类。当我注意到我的应用程序运行缓慢时,我决定看看这个。我添加了数千个,有时甚至数百万个自定义条目(我有一个自定义类,它有3个字段:int、LongWritable和Text,都来自hadoop.io;instrumentation agent说我的记录平均有200字节)

很明显,使用
addAll()
而不是
add()
方法将条目放入堆中会提高性能,仅仅因为这样可以避免一些
heapify
操作吗?

我使用以下示例尝试了不同的策略:

package Sorting;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.PriorityQueue;

public class Main {

private static final int HEAP_SIZE = 1000000;
private static final int BULK_LIST_SIZE = HEAP_SIZE / 10;

private static String normal;
private static String bulk;
private static String fullBulk;

public static void main(String[] args) throws IOException {
normal = "";
bulk = "";
fullBulk = "";
long time = 0;

warmup();

normal = "";
bulk = "";
fullBulk = "";

for (int i = 0; i < 100; i++) {

    // Normal add time
    System.out.println("Normal starts...");
    time = normalExecution();
    System.out.println("Normal add time " + time);

    // Bulk add time
    System.out.println("Bulk starts...");
    time = bulk();
    System.out.println("Bulk add time " + time);

    // Bulk add time with list and heap with same size
    System.out.println("Full Bulk starts...");
    time = fullBulk();
    System.out.println("Full Bulk add time " + time);
}
System.out.println(normal);
System.out.println(bulk);
System.out.println(fullBulk);

}

private static long fullBulk() {
long time;
long start;
List<Double> fullBulkList = new ArrayList<Double>(HEAP_SIZE);
PriorityQueue<Double> fullBulkHeap = new PriorityQueue<Double>(HEAP_SIZE);

start = System.nanoTime();
for (int j = 0; j < HEAP_SIZE; j++) {
    if (fullBulkList.size() == HEAP_SIZE) {
    fullBulkHeap.addAll(fullBulkList);
    fullBulkList.clear();
    }
}
fullBulkHeap.addAll(fullBulkList);
time = System.nanoTime() - start;

fullBulk = fullBulk + "\t" + time;
fullBulkList = null;
fullBulkHeap = null;
return time;
}

private static long bulk() {
long time;
long start;
List<Double> bulkList = new ArrayList<Double>(BULK_LIST_SIZE);
PriorityQueue<Double> bulkHeap = new PriorityQueue<Double>(HEAP_SIZE);
start = System.nanoTime();
for (int j = 0; j < HEAP_SIZE; j++) {
    if (bulkList.size() == BULK_LIST_SIZE) {
    bulkHeap.addAll(bulkList);
    bulkList.clear();
    }
}
bulkHeap.addAll(bulkList);
time = System.nanoTime() - start;
bulk = bulk + "\t" + time;
bulkList = null;
bulkHeap = null;
return time;
}

private static long normalExecution() {

long time;
long start;
PriorityQueue<Double> normalHeap = new PriorityQueue<Double>(HEAP_SIZE);
start = System.nanoTime();
for (int j = 0; j < HEAP_SIZE; j++) {
    normalHeap.add(Double.MAX_VALUE);
}
time = System.nanoTime() - start;
normal = normal + "\t" + time;
normalHeap = null;
return time;
}

private static void warmup() {
System.out.println("Starting warmup");
for (int i = 0; i < 1000; i++) {
    normalExecution();
    bulk();
    fullBulk();
}
for (int i = 0; i < 1000; i++) {

    bulk();
    fullBulk();
    normalExecution();
}
for (int i = 0; i < 1000; i++) {

    fullBulk();
    normalExecution();
    bulk();
}
System.out.println("Warmup finished");
}

}
包裹分拣;
导入java.io.IOException;
导入java.util.ArrayList;
导入java.util.List;
导入java.util.PriorityQueue;
公共班机{
私有静态最终整数堆大小=1000000;
私有静态最终整数批量列表大小=堆大小/10;
私有静态字符串正常;
私有静态字符串块;
私有静态字符串fullBulk;
公共静态void main(字符串[]args)引发IOException{
正常=”;
批量=”;
fullBulk=“”;
长时间=0;
热身();
正常=”;
批量=”;
fullBulk=“”;
对于(int i=0;i<100;i++){
//正常添加时间
System.out.println(“正常启动…”);
时间=正常执行();
System.out.println(“正常添加时间”+时间);
//批量添加时间
System.out.println(“批量启动…”);
时间=批量();
System.out.println(“批量添加时间”+时间);
//使用相同大小的列表和堆大容量添加时间
System.out.println(“完全批量启动…”);
时间=全批量();
System.out.println(“完整批量添加时间”+时间);
}
系统输出打印项次(正常);
系统输出打印项次(批量);
系统输出打印项次(全批量);
}
私有静态长fullBulk(){
长时间;
长起点;
List fullBulkList=新的ArrayList(堆大小);
PriorityQueue fullBulkHeap=新的PriorityQueue(堆大小);
start=System.nanoTime();
对于(int j=0;j
结果如下:

正常add方法第11次迭代中的巨大峰值可以通过一个GC调用来解释:
[gc1347684k->31354K(1446400K),0.0331610秒]


Mediam值分别为16049669、783724和800276。ST dev是3512492.89、244374.17和33344.17。

PriorityQueue
不会覆盖从
AbstractQueue
继承的方法
addAll

AbstractQueue
中,此方法如下所示

public boolean addAll(Collection<? extends E> c) {
    if (c == null)
        throw new NullPointerException();
    if (c == this)
        throw new IllegalArgumentException();
    boolean modified = false;
    for (E e : c)
        if (add(e))
            modified = true;
    return modified;
}

public boolean addAll(collection仅从算法的角度来看,即使有堆的批插入,它也需要筛选每个新添加的元素以保留heap属性。您的基准测试也有缺陷(您的瓶颈不是堆),请看“非常感谢”链接。我同意你的看法,但是这个内部添加元素循环会不会比在对象外部时导致更少的上下文切换?我按照问题注释中链接的一些建议重新运行测试,结果很清楚…@PedroDusso你知道上下文切换是什么吗?你的基准测试是假的,因为您的代码中有IO访问权限。第一次调用可能从磁盘读取,而其他调用是缓存的。这就是区别的来源。@ThomasJungblut是的,我有。这是一个错误的表达式。我的意思是,可能是缓存局部性问题。从类内的addall调用add是cl但我同意你的算法观点。