数组访问可以优化吗?

2024-01-03

也许我被我的分析器(Netbeans)误导了,但我看到了一些奇怪的行为,希望这里有人可以帮助我理解它。

我正在开发一个应用程序,它大量使用相当大的哈希表(键是长整型,值是对象)。内置的 java 哈希表(特别是 HashMap)的性能非常差,在尝试了一些替代方案(Trove、Fastutils、Colt、Carrot)后,我开始自己工作。

该代码非常基本,使用双重哈希策略。这工作得很好,并且显示了迄今为止我尝试过的所有其他选项的最佳性能。

问题是,根据探查器,对哈希表的查找是整个应用程序中最昂贵的方法——尽管事实上调用了其他方法many更多次,和/或做a lot更多逻辑。

真正让我困惑的是,这些查找仅由一个类调用;调用方法进行查找并处理结果。两者被调用的次数几乎相同,并且调用查找的方法中有很多逻辑来处理查找结果,但速度大约快 100 倍。

下面是哈希查找的代码。它基本上只是对数组的两次访问(根据分析,计算哈希码的函数实际上是免费的)。我不明白这段代码怎么会这么慢,因为它只是数组访问,而且我没有看到任何让它更快的方法。

请注意,代码只是返回与键匹配的存储桶,调用者应该处理该存储桶。 'size'是hash.length/2,hash1在哈希表的前半部分查找,hash2在后半部分查找。 key_index 是传递给构造函数的哈希表上的最终 int 字段,Entry 对象上的值数组是一个小型 long 数组,通常长度为 10 或更小。

人们对此有任何想法都非常感激。

Thanks.

public final Entry get(final long theKey) {
    Entry aEntry = hash[hash1(theKey, size)];

    if (aEntry != null && aEntry.values[key_index] != theKey) {
        aEntry = hash[hash2(theKey, size)];

        if (aEntry != null && aEntry.values[key_index] != theKey) {
            return null;
        }
    }

    return aEntry;
}

编辑hash1和hash2的代码

private static int hash1(final long key, final int hashTableSize) { 
    return (int)(key&(hashTableSize-1)); 
}
private static int hash2(final long key, final int hashTableSize) { 
    return (int)(hashTableSize+((key^(key>>3))&(hashTableSize-1))); 
}

你的里面什么都没有执行我觉得效率特别低。我承认我并没有真正遵循你的哈希/查找strategy,但如果你说它在你的情况下表现良好,我会相信你。

我唯一期望的可能是some区别在于将键移出值数组Entry.

而不是这样:

class Entry {
    long[] values;
}

//...
if ( entry.values[key_index] == key ) { //...

尝试这个:

class Entry {
    long key;
    long values[];
}

//...
if ( entry.key == key ) { //...

您不应该产生访问成员的成本,加上进行边界检查,然后获取数组的值,而应该只产生访问成员的成本。

是否有比数组更快的随机访问数据类型?

我对这个问题的答案很感兴趣,所以搭建了一个测试环境。这是我的数组界面:

interface Array {
    long get(int i);
    void set(int i, long v);
}

当索引超出范围时,此“数组”具有未定义的行为。我将明显的实现放在一起:

class NormalArray implements Array {
    private long[] data;

    public NormalArray(int size) {
        data = new long[size];
    }

    @Override
    public long get(int i) {
        return data[i];
    }

    @Override
    public void set(int i, long v) {
        data[i] = v;
    }
}

然后是一个控件:

class NoOpArray implements Array {
    @Override
    public long get(int i) {
        return 0;
    }
    @Override
    public void set(int i, long v) {
    }
}

最后,我设计了一个“数组”,其中前 10 个索引是硬编码成员。成员通过开关设置/选择:

class TenArray implements Array {
    private long v0;
    private long v1;
    private long v2;
    private long v3;
    private long v4;
    private long v5;
    private long v6;
    private long v7;
    private long v8;
    private long v9;
    private long[] extras;

    public TenArray(int size) {
        if (size > 10) {
            extras = new long[size - 10];
        }
    }

    @Override
    public long get(final int i) {
        switch (i) {
        case 0:
            return v0;
        case 1:
            return v1;
        case 2:
            return v2;
        case 3:
            return v3;
        case 4:
            return v4;
        case 5:
            return v5;
        case 6:
            return v6;
        case 7:
            return v7;
        case 8:
            return v8;
        case 9:
            return v9;
        default:
            return extras[i - 10];
        }
    }

    @Override
    public void set(final int i, final long v) {
        switch (i) {
        case 0:
            v0 = v; break;
        case 1:
            v1 = v; break;
        case 2:
            v2 = v; break;
        case 3:
            v3 = v; break;
        case 4:
            v4 = v; break;
        case 5:
            v5 = v; break;
        case 6:
            v6 = v; break;
        case 7:
            v7 = v; break;
        case 8:
            v8 = v; break;
        case 9:
            v9 = v; break;
        default:
            extras[i - 10] = v;
        }
    }
}

我用这个线束测试了它:

import java.util.Random;

public class ArrayOptimization {
    public static void main(String[] args) {
        int size = 10;
        long[] data = new long[size];
        Random r = new Random();
        for ( int i = 0; i < data.length; i++ ) {
            data[i] = r.nextLong();
        }

        Array[] a = new Array[] {
                new NoOpArray(),
                new NormalArray(size),
                new TenArray(size)
        };

        for (;;) {
            for ( int i = 0; i < a.length; i++ ) {
                testSet(a[i], data, 10000000);
                testGet(a[i], data, 10000000);
            }
        }
    }

    private static void testGet(Array a, long[] data, int iterations) {
            long nanos = System.nanoTime();
        for ( int i = 0; i < iterations; i++ ) {
            for ( int j = 0; j < data.length; j++ ) {
                data[j] = a.get(j);
            }
        }
        long stop = System.nanoTime();
        System.out.printf("%s/get took %fms%n", a.getClass().getName(), 
                (stop - nanos) / 1000000.0);
    }

    private static void testSet(Array a, long[] data, int iterations) {
        long nanos = System.nanoTime();
        for ( int i = 0; i < iterations; i++ ) {
            for ( int j = 0; j < data.length; j++ ) {
                a.set(j, data[j]);
            }
        }
        long stop = System.nanoTime();
        System.out.printf("%s/set took %fms%n", a.getClass().getName(), 
                (stop - nanos) / 1000000.0);

    }
}

结果有些令人惊讶。 TenArray 的执行速度比 NormalArray 快得多(对于大小 is可能超过阵列的速度。我想 switch 使用比数组更少的边界检查或更有效的边界检查。

NoOpArray/set took 953.272654ms
NoOpArray/get took 891.514622ms
NormalArray/set took 1235.694953ms
NormalArray/get took 1148.091061ms
TenArray/set took 1149.833109ms
TenArray/get took 1054.040459ms
NoOpArray/set took 948.458667ms
NoOpArray/get took 888.618223ms
NormalArray/set took 1232.554749ms
NormalArray/get took 1120.333771ms
TenArray/set took 1153.505578ms
TenArray/get took 1056.665337ms
NoOpArray/set took 955.812843ms
NoOpArray/get took 893.398847ms
NormalArray/set took 1237.358472ms
NormalArray/get took 1125.100537ms
TenArray/set took 1150.901231ms
TenArray/get took 1057.867936ms

现在我不确定你在实践中是否可以获得比阵列更快的速度;显然,这种方式会产生与接口/类/方法相关的任何开销。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

数组访问可以优化吗? 的相关文章

随机推荐

  • 在 smarty 模板中创建数组? [复制]

    这个问题在这里已经有答案了 我需要从 smarty 模板中的其他一维数组创建一个新数组 那么 在模板文件中创建数组的最佳可能性是什么 谢谢 萨钦 Smarty3 让您 var foo gt bar sub gt 1 2 3 and var
  • Ruby 中 $$ 的含义是什么?

    irb main 002 0 gt gt 5052 是什么意思 在 Ruby 中以及如何 在哪里使用它 is the 进程号 http www opengroup org onlinepubs 9699919799 functions ge
  • HeapTaskDaemon 线程阻塞的 ANR

    我的 Android 应用程序出现 ANR 错误 跟踪显示只有一个线程处于阻塞状态 所有其他线程都处于等待 睡眠 本机状态 因此它似乎并未处于死锁状态 我手动 直接 启动了两个线程 因此我大致知道 ANR 发生在应用程序的哪个部分 不幸的是
  • 从Python文件中读取单个字符?

    我的问题是 除了下面之外 是否还有其他方法可以一次一个字符地遍历文件 with open filename as f while True c f read 1 if not c print End of file break print
  • 使 tkinter 文本小部件适合窗口

    我正在制作一个文本编辑器 其主要小部件是一个文本小部件 供用户实际输入文本 当用户调整窗格大小时 我需要使文本小部件适合窗口 我通过使小部件变大来有点作弊 但这只是一个临时解决方案 让我在寻找解决方案时可以处理其他部分 如何使文本小部件自动
  • 如何在 Rails 2.3.5 中安装/使用 Devise?

    我尝试从 Github 上 Devise 的 v 1 2 oauth 分支进行安装 但仍然出现错误 如何在 Rails 2 3 5 应用程序上安装 devise gem 我特别想要一个可以与omniauth一起使用的 gem install
  • Mac App Store:放弃 32 位支持转而支持 ARC,32 位版本的现有用户会看到更新消息吗?

    我正在考虑放弃 32 位支持 转而支持自动引用计数 仅支持 64 位二进制文 件 我想在 Mac App Store 中避免出现这两种情况 For a 旧 32 位 Mac 用户 谁购买了支持 32 位的先前版本 他们会在 Mac App
  • Python 中是否有用于纯文本文件的本机模板系统?

    我正在寻找用于将输出格式化为简单文本的 Python 技术或模板系统 我需要的是它将能够迭代多个列表或字典 如果我能够将模板定义到单独的文件 如output templ 中而不是将其硬编码到源代码中 那就太好了 作为我想要实现的简单示例 我
  • 如何从9GAG获取数据json

    也许你认为这是一个愚蠢的问题 但我希望你能给我一些建议 我的问题 当我查看 9gag com 的源代码时 我意识到他们有一些行代码来加载更多内容 div class loading a class btn badge load more p
  • PyYAML 中的数组没有缩进或空格

    在下面的代码中我创建了net plan dict变量字典并将其转换为YAML格式文件 在字典里我有一个叫做addresses这是一个由三个元素组成的数组 创建YAML文件后 这三个数组元素没有放置在addresses field impor
  • JPA针对不同数据库的不同列类型

    是否可以根据使用的数据库使用 JPA 定义不同的列类型 我需要将 id 存储为 uuid 并且它必须是可移植的 那就是问题所在 PostgreSQL有 uuid MSSQL有 uniqueidentifier 而Oracle什么都没有 我想
  • android中textview的圆角

    我有一个文本视图 希望它的角是圆形的 我已经知道可以使用android background drawable somefile 就我而言 该标签已包含在内 因此无法再次使用 例如android background drawable my
  • Rails 更改 form_for 中提交的路由

    我有一个模型 文章 和一个嵌套在文章中的模型 评级 文章 123 评级 我想更改 ratings form html erb 中 f submit 的路由 现在是这样 按提交后 我的申请路由到 评分 111 但我想将其路由到 文章 123
  • WCF 服务应该返回 EntityObject 还是 POCO/DTO 类?

    我一直在查看很多使用 EntityFramework 的 WCF 示例 其中大多数似乎都会向客户端返回某种 POCO 或 DTO 类 我想知道为什么这是默认的EntityObject包括 DataContract 属性和工具INotifyP
  • Angula2 Karma 无法加载“webpack”!

    我已经在 Angular2 项目 Webpack Karma 上工作了几个月 该项目基于此入门程序的稍旧版本 https github com preboot angular2 webpack https github com preboo
  • 带注入的定制 Serilog 水槽?

    我创建了一个简单的 Serilog 接收器项目 如下所示 namespace MyApp Cloud Serilog MQSink public class MessageQueueSink ILogEventSink private re
  • 无法使用@Value在Spring应用程序中获取maven project.version属性

    如何使用 Value注释在Spring Boot应用程序中获取maven project version属性 经过一些关于如何在 SpringBoot 应用程序中获取 Maven 项目版本的研究和试验后 我找不到任何适合我的东西 由于类加载
  • 为 Goldschmidt 部门挑选良好的初步估计

    我正在计算 Q22 10 中的定点倒数戈德施密特师 http en wikipedia org wiki Division digital Goldschmidt division用于我的 ARM 上的软件光栅器 只需将分子设置为 1 即可
  • 实体 .ToList() 生成 System.OutOfMemoryException

    我有一个包含 50 万行的表 我需要更新每一行 但 ToList 失败 List
  • 数组访问可以优化吗?

    也许我被我的分析器 Netbeans 误导了 但我看到了一些奇怪的行为 希望这里有人可以帮助我理解它 我正在开发一个应用程序 它大量使用相当大的哈希表 键是长整型 值是对象 内置的 java 哈希表 特别是 HashMap 的性能非常差 在