- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
我花了几个小时试图弄清楚为什么我的排序算法的 Java 版本比递归合并排序慢两倍,因为 C 和 C++ 版本要快 40-50%。我一直在删除越来越多的代码,直到我将所有内容都剥离成一个简单的循环并合并,但它仍然慢了一倍。为什么这只在 Java 中这么慢?
作为引用,自下而上的合并排序可能如下所示:
public static <T> void sort(T[] a, T[] aux, Comparator<T> comp) {
int N = a.length;
for (int n = 1; n < N; n = n+n)
for (int i = 0; i < N-n; i += n+n)
merge(a, aux, i, i+n-1, Math.min(i+n+n-1, N-1), comp);
}
这是递归版本:
public static <T> void sort(T[] a, T[] aux, int lo, int hi, Comparator<T> comp) {
int mid = lo + (hi - lo) / 2;
sort(a, aux, lo, mid, comp);
sort(a, aux, mid + 1, hi, comp);
merge(a, aux, lo, mid, hi, comp);
}
这些基本上只是从算法中复制的 on this website .作为最后的手段,我想我会从网上复制和粘贴一些东西,但它的速度也是递归版本的两倍。
Java 是否有我遗漏的“特殊”之处?
编辑:根据要求,这里有一些代码:
import java.util.*;
import java.lang.*;
import java.io.*;
class Test {
public int value;
public int index;
}
class TestComparator implements Comparator<Test> {
public int compare(Test a, Test b) {
if (a.value < b.value) return -1;
if (a.value > b.value) return 1;
return 0;
}
}
class Merge<T> {
private static <T> void Merge(T[] array, int start, int mid, int end, Comparator<T> comp, T[] buffer) {
java.lang.System.arraycopy(array, start, buffer, 0, (mid - start));
int A_count = 0, B_count = 0, insert = 0;
while (A_count < (mid - start) && B_count < (end - mid)) {
if (comp.compare(array[mid + B_count], buffer[A_count]) >= 0)
array[start + insert++] = buffer[A_count++];
else
array[start + insert++] = array[mid + B_count++];
}
java.lang.System.arraycopy(buffer, A_count, array, start + insert, (mid - start) - A_count);
}
private static <T> void SortR(T[] array, int start, int end, T[] buffer, Comparator<T> comp) {
if (end - start <= 2) {
if (end - start == 2) {
if (comp.compare(array[start], array[end - 1]) > 0) {
T swap = array[start];
array[start] = array[end - 1];
array[end - 1] = swap;
}
}
return;
}
int mid = start + (end - start)/2;
SortR(array, start, mid, buffer, comp);
SortR(array, mid, end, buffer, comp);
Merge(array, start, mid, end, comp, buffer);
}
public static <T> void Recursive(T[] array, Comparator<T> comp) {
@SuppressWarnings("unchecked")
T[] buffer = (T[]) new Object[array.length];
SortR(array, 0, array.length, buffer, comp);
}
public static <T> void BottomUp(T[] array, Comparator<T> comp) {
@SuppressWarnings("unchecked")
T[] buffer = (T[]) new Object[array.length];
int size = array.length;
for (int index = 0; index < size - 1; index += 2) {
if (comp.compare(array[index], array[index + 1]) > 0) {
T swap = array[index];
array[index] = array[index + 1];
array[index + 1] = swap;
}
}
for (int length = 2; length < size; length += length)
for (int index = 0; index < size - length; index += length + length)
Merge(array, index, index + length, Math.min(index + length + length, size), comp, buffer);
}
}
class SortRandom {
public static Random rand;
public static int nextInt(int max) {
// set the seed on the random number generator
if (rand == null) rand = new Random();
return rand.nextInt(max);
}
public static int nextInt() {
return nextInt(2147483647);
}
}
class Sorter {
public static void main (String[] args) throws java.lang.Exception {
int max_size = 1500000;
TestComparator comp = new TestComparator();
for (int total = 0; total < max_size; total += 2048 * 16) {
Test[] array1 = new Test[total];
Test[] array2 = new Test[total];
for (int index = 0; index < total; index++) {
Test item = new Test();
item.value = SortRandom.nextInt();
item.index = index;
array1[index] = item;
array2[index] = item;
}
double time1 = System.currentTimeMillis();
Merge.BottomUp(array1, comp);
time1 = System.currentTimeMillis() - time1;
double time2 = System.currentTimeMillis();
Merge.Recursive(array2, comp);
time2 = System.currentTimeMillis() - time2;
if (time1 >= time2)
System.out.format("%f%% as fast\n", time2/time1 * 100.0);
else
System.out.format("%f%% faster\n", time2/time1 * 100.0 - 100.0);
System.out.println("verifying...");
for (int index = 0; index < total; index++) {
if (comp.compare(array1[index], array2[index]) != 0) throw new Exception();
if (array2[index].index != array1[index].index) throw new Exception();
}
System.out.println("correct!");
}
}
}
这是一个 C++ 版本:
#include <iostream>
#include <cassert>
#include <cstring>
#include <ctime>
class Test {
public:
size_t value, index;
};
bool TestCompare(Test item1, Test item2) {
return (item1.value < item2.value);
}
namespace Merge {
template <typename T, typename Comparison>
void Merge(T array[], int start, int mid, int end, Comparison compare, T buffer[]) {
std::copy(&array[start], &array[mid], &buffer[0]);
int A_count = 0, B_count = 0, insert = 0;
while (A_count < (mid - start) && B_count < (end - mid)) {
if (!compare(array[mid + B_count], buffer[A_count]))
array[start + insert++] = buffer[A_count++];
else
array[start + insert++] = array[mid + B_count++];
}
std::copy(&buffer[A_count], &buffer[mid - start], &array[start + insert]);
}
template <typename T, typename Comparison>
void SortR(T array[], int start, int end, T buffer[], Comparison compare) {
if (end - start <= 2) {
if (end - start == 2)
if (compare(array[end - 1], array[start]))
std::swap(array[start], array[end - 1]);
return;
}
int mid = start + (end - start)/2;
SortR(array, start, mid, buffer, compare);
SortR(array, mid, end, buffer, compare);
Merge(array, start, mid, end, compare, buffer);
}
template <typename T, typename Comparison>
void Recursive(T array[], int size, Comparison compare) {
T *buffer = new T[size];
SortR(array, 0, size, buffer, compare);
delete[] buffer;
}
template <typename T, typename Comparison>
void BottomUp(T array[], int size, Comparison compare) {
T *buffer = new T[size];
for (int index = 0; index < size - 1; index += 2) {
if (compare(array[index + 1], array[index]))
std::swap(array[index], array[index + 1]);
}
for (int length = 2; length < size; length += length)
for (int index = 0; index < size - length; index += length + length)
Merge(array, index, index + length, std::min(index + length + length, size), compare, buffer);
delete[] buffer;
}
}
int main() {
srand(time(NULL));
int max_size = 1500000;
for (int total = 0; total < max_size; total += 2048 * 16) {
Test *array1 = new Test[total];
Test *array2 = new Test[total];
for (int index = 0; index < total; index++) {
Test item;
item.value = rand();
item.index = index;
array1[index] = item;
array2[index] = item;
}
double time1 = clock() * 1.0/CLOCKS_PER_SEC;
Merge::BottomUp(array1, total, TestCompare);
time1 = clock() * 1.0/CLOCKS_PER_SEC;
double time2 = clock() * 1.0/CLOCKS_PER_SEC;
Merge::Recursive(array2, total, TestCompare);
time2 = clock() * 1.0/CLOCKS_PER_SEC;
if (time1 >= time2)
std::cout << time2/time1 * 100.0 << "% as fast" << std::endl;
else
std::cout << time2/time1 * 100.0 - 100.0 << "% faster" << std::endl;
std::cout << "verifying... ";
for (int index = 0; index < total; index++) {
assert(array1[index].value == array2[index].value);
assert(array2[index].index == array1[index].index);
}
std::cout << "correct!" << std::endl;
delete[] array1;
delete[] array2;
}
return 0;
}
差异并不像原始版本那么大,但 C++ 迭代版本更快,而 Java 迭代版本更慢。
(是的,我意识到这些版本有点糟糕并且分配的内存比使用的更多)
更新 2: 当我将自下而上的合并排序切换为后序遍历时,它与递归版本中的数组访问顺序非常匹配,它最终开始比递归版本快 10%递归版本。所以看起来它与缓存未命中有关,而不是微基准测试或不可预测的 JVM。
它只影响Java版本的原因可能是因为Java缺少C++版本中使用的自定义值类型。我将在 C++ 版本中单独分配所有测试类,看看性能会发生什么变化。我正在研究的排序算法不能轻易适应这种类型的遍历,但如果 C++ 版本的性能也很差,我可能没有太多选择。
更新 3: 不,将 C++ 版本切换到分配的类似乎对其性能没有任何明显影响。它看起来确实是由 Java 引起的。
最佳答案
有趣的问题。我不明白为什么 bottomUp 版本比递归慢,而数组大小为 2 的幂时它们的工作方式相同。
至少 bottomUp 慢了一点点,而不是两倍。
Benchmark Mode Mean Mean error Units
RecursiveVsBottomUpSort.bottomUp avgt 64.436 0.376 us/op
RecursiveVsBottomUpSort.recursive avgt 58.902 0.552 us/op
代码:
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@BenchmarkMode(Mode.AverageTime)
@Warmup(iterations = 5, time = 1)
@Measurement(iterations = 10, time = 1)
@State(Scope.Thread)
@Threads(1)
@Fork(1)
public class RecursiveVsBottomUpSort {
static final int N = 1024;
int[] a = new int[N];
int[] aux = new int[N];
@Setup(Level.Invocation)
public void fill() {
Random r = ThreadLocalRandom.current();
for (int i = 0; i < N; i++) {
a[i] = r.nextInt();
}
}
@GenerateMicroBenchmark
public static int bottomUp(RecursiveVsBottomUpSort st) {
int[] a = st.a, aux = st.aux;
int N = a.length;
for (int n = 1; n < N; n = n + n) {
for (int i = 0; i < N - n; i += n + n) {
merge(a, aux, i, i + n - 1, Math.min(i + n + n - 1, N - 1));
}
}
return a[N - 1];
}
@GenerateMicroBenchmark
public static int recursive(RecursiveVsBottomUpSort st) {
sort(st.a, st.aux, 0, N - 1);
return st.a[N - 1];
}
static void sort(int[] a, int[] aux, int lo, int hi) {
if (lo == hi)
return;
int mid = lo + (hi - lo) / 2;
sort(a, aux, lo, mid);
sort(a, aux, mid + 1, hi);
merge(a, aux, lo, mid, hi);
}
static void merge(int[] a, int[] aux, int lo, int mid, int hi) {
System.arraycopy(a, lo, aux, lo, mid + 1 - lo);
for (int j = mid+1; j <= hi; j++)
aux[j] = a[hi-j+mid+1];
int i = lo, j = hi;
for (int k = lo; k <= hi; k++)
if (aux[j] < aux[i]) a[k] = aux[j--];
else a[k] = aux[i++];
}
}
关于java - 为什么我的自下而上合并排序在 Java 中这么慢?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23121831/
我正在尝试对每个条目有多个值的关联数组进行排序。 例如 [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
我是一名优秀的程序员,十分优秀!