- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
List<Point> pixels = new ArrayList<>(width * height); // 1280*960
for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++)
pixels.add(new Point(x, y));
// Java 7 sorting
Collections.sort(pixels, comparator);
// Java 8 sorting
pixels = pixels.stream().parallel().sorted(comparator).collect(Collectors.toList());
Java 7 collection sorter
0.258992413
0.265509443
0.536536068
0.117830618
0.136303916
0.111004611
0.134771877
0.108078261
Java 8 stream sorter
0.631757108
0.868032669
0.076455248
0.087101852
0.070401965
0.056989645
0.072018371
0.078908912
0.074237648
最佳答案
看来你关心的是预热阶段的性能(即JVM启动后的第一次和第二次排序)。所以可能标准的 JMH 基准不适合你。没关系,让我们手动编写基准测试。正如我们所说的几十毫秒,使用 System.nanoTime()
的简单基准测试将提供足够的精度。
您没有在问题中提供您的 Comparator
。简单的比较器(如 Comparator.comparingInt(p -> p.x)
)可以更快地对数据进行排序,因此我假设您有更复杂的比较器:
final Comparator<Point> comparator = Comparator.comparingInt(p -> p.x*p.x + p.y*p.y);
(0, 0)
的欧几里得距离来比较点(不需要取平方根,因为它是单调函数,所以顺序不会改变)。
private Point[] prepareData() {
Point[] pixels = new Point[width*height];
int idx = 0;
for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++)
pixels[idx++] = new Point(x, y);
return pixels;
}
List
来直接测试
Arrays.parallelSort
。普通的旧排序是这样的:
public List<Point> sortPlain(Point[] data) {
List<Point> list = Arrays.asList(data);
Collections.sort(list, comparator);
return list;
}
public List<Point> sortParallelStream(Point[] data) {
return Stream.of(data).parallel().sorted(comparator).collect(Collectors.toList());
}
public List<Point> sortStream(Point[] data) {
return Stream.of(data).sorted(comparator).collect(Collectors.toList());
}
parallelSort
:
public List<Point> sortParallel(Point[] data) {
Arrays.parallelSort(data, comparator);
return Arrays.asList(data);
}
Iter Plain Parallel Stream ParallelStream
#01: 0.38362s 0.37364s 0.28255s 0.47821s
#02: 0.23021s 0.25754s 0.18533s 0.72231s
#03: 0.18862s 0.08887s 0.21329s 0.18024s
#04: 0.19810s 0.06158s 0.68004s 0.12166s
#05: 0.19671s 0.06461s 0.17066s 0.08380s
#06: 0.14796s 0.05484s 0.18283s 0.12931s
#07: 0.16588s 0.04920s 0.21481s 0.13379s
#08: 0.21988s 0.05932s 0.19111s 0.12903s
#09: 0.14434s 0.05123s 0.14191s 0.11674s
#10: 0.18771s 0.06174s 0.14977s 0.07237s
#11: 0.15674s 0.05105s 0.21275s 0.06975s
#12: 0.17634s 0.06353s 0.14343s 0.07882s
#13: 0.15085s 0.05318s 0.16004s 0.11029s
#14: 0.18555s 0.05278s 0.19105s 0.12123s
#15: 0.14728s 0.05916s 0.14426s 0.07235s
#16: 0.18781s 0.05708s 0.21455s 0.07884s
#17: 0.14493s 0.12377s 0.14415s 0.11170s
#18: 0.14395s 0.05100s 0.18201s 0.07878s
#19: 0.14849s 0.05437s 0.14484s 0.08364s
#20: 0.14143s 0.12073s 0.18542s 0.11257s
Plain
和
ParallelStream
测试的结果与您的有些相似:
ParallelStream
的前两次迭代(尤其是第二次)要慢得多。您还可以注意到,直接执行
Arrays.parallelSort
没有这种效果。最后,非并行流是最慢的。那是因为 Stream API 总是使用中间缓冲区进行排序,因此需要更多的空间和时间对缓冲区执行额外的复制、排序,然后执行复制到结果列表。
ParallelStream
的前两次迭代如此之慢(尤其是第二次)?仅仅因为你有相当小的起始堆来方便地放置所有的中间缓冲区,所以在前两次迭代期间发生了几个 full-gc 事件,最终会出现明显的延迟。如果您使用
-verbose:gc
运行测试,您将看到
ParallelStream
:
[GC (Allocation Failure) 16384K->14368K(62976K), 0.0172833 secs]
[GC (Allocation Failure) 30752K->30776K(79360K), 0.0800204 secs]
[Full GC (Ergonomics) 30776K->30629K(111104K), 0.4487876 secs]
[GC (Allocation Failure) 63394K->74300K(111104K), 0.0215347 secs]
[Full GC (Ergonomics) 74300K->45460K(167936K), 0.1536388 secs]
[GC (Allocation Failure) 76592K->57710K(179712K), 0.0064693 secs]
#01: 0.41506s
[GC (Allocation Failure) 101713K->103534K(180224K), 0.0567087 secs]
[Full GC (Ergonomics) 103534K->39365K(203776K), 0.5636835 secs]
[GC (Allocation Failure) 84021K->53689K(266752K), 0.0103750 secs]
#02: 0.71832s
Plain
启动比较:
[GC (Allocation Failure) 16384K->14400K(62976K), 0.0162299 secs]
[GC (Allocation Failure) 30784K->30784K(79360K), 0.0762906 secs]
[Full GC (Ergonomics) 30784K->30629K(111616K), 0.4548198 secs]
#01: 0.43610s
[GC (Allocation Failure) 63397K->58989K(111616K), 0.0330308 secs]
[Full GC (Ergonomics) 58989K->25278K(133120K), 0.2479148 secs]
#02: 0.20753s
-Xms1G
将初始堆大小设置为 1Gb 以降低 GC 压力。现在我们得到了完全不同的结果:
Iter Plain Parallel Stream ParallelStream
#01: 0.38349s 0.33331s 0.23834s 0.24078s
#02: 0.18514s 0.20530s 0.16650s 0.07802s
#03: 0.16642s 0.10417s 0.16267s 0.11826s
#04: 0.16409s 0.05015s 0.19890s 0.06926s
#05: 0.14475s 0.05241s 0.15041s 0.06932s
#06: 0.14358s 0.05584s 0.14611s 0.06684s
#07: 0.17644s 0.04913s 0.14619s 0.06716s
#08: 0.14252s 0.04642s 0.19333s 0.10813s
#09: 0.14427s 0.04547s 0.14673s 0.06900s
#10: 0.14696s 0.04634s 0.14927s 0.06712s
#11: 0.14254s 0.04682s 0.15107s 0.07874s
#12: 0.15455s 0.09560s 0.19370s 0.06663s
#13: 0.15544s 0.05133s 0.15110s 0.13052s
#14: 0.18636s 0.04788s 0.15928s 0.06688s
#15: 0.14824s 0.04833s 0.15218s 0.06624s
#16: 0.15068s 0.04949s 0.19183s 0.13925s
#17: 0.14605s 0.04695s 0.14770s 0.12714s
#18: 0.14130s 0.04660s 0.14903s 0.15428s
#19: 0.14695s 0.05491s 0.14389s 0.07467s
#20: 0.15050s 0.04700s 0.18919s 0.07662s
Plain
的结果也更加稳定(因为我们的 GC 暂停更少)并且
ParallelStream
现在总是比
Plain
更好(尽管它仍然产生更多的对象,但当你有更大的堆时更容易分配它们并收集垃圾)。对于所有四个测试,
-Xms1G
没有观察到完整的 gc 事件
Arrays.parallelSort
,因为它就地排序。特别是如果您事先知道数据集的大小。 Collections.sort(list, comparator)
称为“Java 7 集合排序器”时,它的运行速度要慢 8-10%,因为
Collections.sort
实现已更改。
关于java - 为什么更新/更快的 Java 8 排序方式更糟糕?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35163289/
我正在尝试对每个条目有多个值的关联数组进行排序。 例如 [0] => stdClass Object ( [type] => node [sid] => 158 [score] => 0.059600
我在 mysql 中有“日期”列以这种格式保存日期 2014 年 9 月 17 日(日-月-年) 我需要对它们进行升序排序,所以我使用了这个命令: SELECT * FROM table ORDER
我目前正在将 MySQL 存储过程重写为 MS SQL 存储过程,但遇到了问题。 在 MySQL 存储过程中,有一个游标,它根据最近的日期 (effdate) 选择一个值并将其放入变量 (thestt
我想要 gwt r.QuestionId- 排序。但是我得到未排序的 QuestionId 尽管我提到了 QuestionId ASC 的顺序。 SELECT r.QuestionId,
我有一个关于在 scandir 函数中排序的基本问题。到目前为止,我阅读了 POSIX readdir 的手册页,但没有找到有关订购保证的具体信息。 但是当我遍历大目录(无法更改,只读)时,我在多个系
基本上我必须从 SQL 数据库中构建项目列表,但是用户可以选择对 7 个过滤器的任意组合进行过滤,也可以选择要排序的列以及按方向排序。 正如您可以想象的那样,这会以大量不同的组合进行编码,并且数据集非
我有两张 table 。想象第一个是一个目录,包含很多文件(第二个表)。 第二个表(文件)包含修改日期。 现在,我想选择所有目录并按修改日期 ASC 对它们进行排序(因此,最新的修改最上面)。我不想显
我想先根据用户的状态然后根据用户名来排序我的 sql 请求。该状态由 user_type 列设置: 1=活跃,2=不活跃,3=创始人。 我会使用此请求来执行此操作,但它不起作用,因为我想在“活跃”成员
在 C++ 中,我必须实现一个“类似 Excel/Access”(引用)的查询生成器,以允许对数据集进行自定义排序。如果您在 Excel 中使用查询构建器或 SQL 中的“ORDER BY a, b,
我面临这样的挑战: 检索按字段 A 排序的文档 如果字段 B 存在/不为空 . 否则 按字段排序 C. 在 SQL 世界中,我会做两个查询并创建一个 UNION SELECT,但我不知道如何从 Mon
我想对源列表执行以下操作: map 列表 排序 折叠 排序 展开 列表 其中一些方法(例如map和toList)是可链接的,因为它们返回非空对象。但是,sort 方法返回 void,因为它对 List
我制作了一个用于分析 Windows 日志消息编号的脚本。 uniq -c 数字的输出很难预测,因为根据数字的大小会有不同的空白。此时,我手动删除了空白。 这是对消息进行排序和计数的命令: cat n
我有以下词典: mydict1 = {1: 11, 2: 4, 5: 1, 6: 1} mydict2 = {1: 1, 5: 1} 对于它们中的每一个,我想首先按值(降序)排序,然后按键(升序)排序
我刚刚开始使用泛型,目前在对多个字段进行排序时遇到问题。 案例: 我有一个 PeopleList 作为 TObjectList我希望能够通过一次选择一个排序字段,但尽可能保留以前的排序来制作类似 Ex
有没有办法在 sql 中组合 ORDER BY 和 IS NULL 以便我可以在列不为空时按列排序,但如果它为null,按另一列排序? 最佳答案 类似于: ORDER BY CASE WHEN
我有一个包含 2 列“id”和“name”的表。 id 是常规的自动增量索引,name 只是 varchar。 id name 1 john 2 mary 3 pop 4 mary 5 j
场景 网站页面有一个带有分页、过滤、排序功能的表格 View 。 表中的数据是从REST API服务器获取的,数据包含数百万条记录。 数据库 REST API 服务器 Web 服务器 浏览器 问
假设我有一本字典,其中的键(单词)和值(分数)如下: GOD 8 DONG 16 DOG 8 XI 21 我想创建一个字典键(单词)的 NSArray,首先按分数排序,然后按字
如何在 sphinx 上通过 sql 命令选择前 20 行按标题 WEIGHT 排序,接下来 20 行按标题 ASC 排序(总共 40 个结果),但不要给出重复的标题输出。 我尝试了这个 sql 命令
我有一个奇怪的问题,当从 SQLite 数据库中选择信息并根据日期排序时,返回的结果无效。 我的SQL语句是这样的: Select pk from usersDates order by dateti
我是一名优秀的程序员,十分优秀!