java字符串优化-就地加载算法

java字符串优化-就地加载算法,java,string,optimization,java-native-interface,Java,String,Optimization,Java Native Interface,我需要优化csv文件(字符串)的实际加载/解析。我知道最好的方法是负载就位算法,我成功地使用JNI和C++ DLL,它直接从解析的CSV数据中加载数据。p> 如果它停在那里就好了,但使用该方案只会使它加快15%(不再解析数据)。它不如我最初想象的那么快的原因之一是因为java客户端使用jstring,所以我需要再次将实际数据从char*转换为jstring 最好是忽略该转换步骤,直接将数据就地加载到jstring对象中(不再进行转换)。因此,jstring将直接指向内存块,而不是基于加载的就地数

我需要优化csv文件(字符串)的实际加载/解析。我知道最好的方法是负载就位算法,我成功地使用JNI和C++ DLL,它直接从解析的CSV数据中加载数据。p> 如果它停在那里就好了,但使用该方案只会使它加快15%(不再解析数据)。它不如我最初想象的那么快的原因之一是因为java客户端使用jstring,所以我需要再次将实际数据从char*转换为jstring

最好是忽略该转换步骤,直接将数据就地加载到jstring对象中(不再进行转换)。因此,jstring将直接指向内存块,而不是基于加载的就地数据复制数据(注意,数据将由jchars而不是chars组成)。真正糟糕的是,我们需要确保垃圾收集器不收集这些数据(可能通过保留对它的引用?),但这应该是可行的。。没有

我想我有两个选择:

1-以java(不再使用jni)加载数据,并使用指向加载数据的字符来创建字符串。。但是我需要找到一种方法来防止在创建字符串时重复数据

2-继续使用jni“手动”创建和设置jstring变量,并确保垃圾收集器选项设置正确,以防止对其执行任何操作。例如:

jstring str; 
str.data = loadedinplacedata;  // assign data pointer
return str;
不确定这是否可行,但我不介意直接将jstring保存到文件中,然后像这样重新加载:

jstring * str = (jstring *)&loadedinplacedata[someoffset];
return * str;
我知道这不是通常的Java,但我非常确定Java的可扩展性足以做到这一点。在这件事上我并没有选择的余地。。。这个项目已经有3年的历史了,它需要工作=

这是JNI代码(C++):


注意:上面的代码通过使用unicode数据而不是UTF8(NewString而不是NewStringUTF)使速度提高了20%(而不是15)。这表明,如果我可以删除或优化该步骤,我将获得相当好的性能提升。

我从未使用过JNI,但是。。。让它返回一个实现CharSequence的自定义类,或者一些其他接口,比如Comparable,而不是字符串,这有什么意义吗?看起来你不太可能会有数据损坏问题。

< P>我想你首先必须理解为什么C++版本运行得快15%,为什么性能改进不能直接转化成java。为什么不能用Java编写快15%的代码

让我们看看你的问题。您已经通过使用C++ DLL消除了解析。(为什么不能在Java中实现这一点?)。据我所知:

  • 您建议直接操作JString的内容
  • 您希望防止垃圾收集器接触这些修改过的JString(通过保留对它们的引用),从而可能修改JVM的行为,并在垃圾收集器最终进行垃圾收集时使用垃圾收集器
  • 在允许垃圾收集这些引用之前,您会“修复”它们吗

    如果你建议自己做内存管理,为什么要使用java呢?为什么不在纯C++中做呢?
    假设您希望在Java中继续,当您创建一个字符串时,字符串本身是一个新对象,但它所指向的数据不一定是新对象。您可以通过调用String.intern()来测试这一点。使用以下代码:

    public static void main(String[] args) {
        String s3 = "foofoo";
    
        String s1 = call("foo");
        String s2 = call("foo");
    
        System.out.println("s1 == s2=" + (s1 == s2));
        System.out.println("s1.intern() == s2.intern()=" + (s1.intern() == s2.intern()));
        System.out.println("s1.intern() == s3.intern()=" + (s1.intern() == s3.intern()));
    
        System.out.println("s1.substring(3) == s2.substring(3)=" + (s1.substring(3) == s2.substring(3)));
        System.out.println("s1.substring(3).intern() == s2.substring(3).intern()=" + (s1.substring(3).intern() == s2.substring(3).intern()));
    }
    
    public static String call(String s) {
        return s + "foo";        
    }
    
    这将产生:

    s1 == s2=false
    s1.intern() == s2.intern()=true
    s1.intern() == s3.intern()=true
    s1.substring(3) == s2.substring(3)=false
    s1.substring(3).intern() == s2.substring(3).intern()=true
    
    因此,您可以看到,尽管字符串对象不同,但数据和实际字节却不同。因此,您的修改实际上可能没有那么重要,JVM可能已经在为您进行修改了。值得一提的是,如果您开始修改jstring的内部结构,这可能会把事情搞砸


    我的建议是找出你能在算法方面做些什么。使用纯java开发总是比java和JNI结合起来更快。使用纯Java,您有更好的机会找到更好的解决方案。

    好吧。。。似乎我想做的事情不受Java的“支持”,除非我破解它。。我相信可以通过使用GetStringCritical来获得实际的字符数组地址,然后找出字符数等等,但这远远超出了“安全”编程的范围

    我发现最好的解决方法是用java创建一个哈希表,并在创建数据文件时使用一个唯一的标识符(类似于.intern()。如果字符串不在哈希表中,它将通过dll查询该字符串并将其保存在哈希表中

    数据文件: numrow,numcols, 对于每个单元格,添加一个整数值(在我的例子中是内存中指向字符串的偏移量) 对于每个单元格,添加以\0结尾的字符串


    通过使用偏移量值,我可以在某种程度上最小化字符串创建和字符串查询的数量。我尝试使用globalref将字符串保留在dll中,但这使其速度慢了4倍

    这是从什么地方装载的?如果复制的时间比磁盘IO的时间长,我会感到惊讶,假设没有什么真正愚蠢的事情发生。-1)您需要同时在内存中保存整个文件吗?-2) 为什么要使用JNI?它通常是从.jar文件加载的,在dev环境中直接从文件加载。因此,您可以假设几乎没有磁盘IO,因为它应该已经加载了。我不需要同时在内存中保存所有文件,但是文件应该已经存在了,因为它是jar文件的一部分。JNI允许我使用指针。。。但是由于我有更多的C++,我可能误解了java的一些特性。是否有任何方法可以防止数据的复制(比如根据内存中的位置重用引用?)。例如,如果我能创建一个类,让我重用
    s1 == s2=false
    s1.intern() == s2.intern()=true
    s1.intern() == s3.intern()=true
    s1.substring(3) == s2.substring(3)=false
    s1.substring(3).intern() == s2.substring(3).intern()=true