2011-09-28 39 views
1

我需要优化csv文件(字符串)的实际加载/解析。我知道的最好的方法是就地加载算法,并且我成功地使用了JNI和一个C++ dll,它直接从一个由解析的csv数据构成的文件中加载数据。java字符串优化 - 就地加载算法

如果它停在那里,它会好起来的,但是使用该方案只会使速度提高15%(不再解析数据)。其中一个原因并不像我第一次想到的那样快,因为java客户端使用jstring,所以我需要再次将实际数据从char *转换为jstring。

最好的办法是忽略该转换步骤并将数据直接加载到jstring对象中(不再进行转换)。因此,不是基于现场加载的数据来复制数据,而是将jstring直接指向内存块(请注意,数据将由jchars而不是字符组成)。真正的坏处是,我们需要确保垃圾收集器不会收集数据(通过保持对它的引用可能?),但它应该是可行的。

我想我有两个选择这样做:

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年了,需要运作。 = S

这在JNI代码(C++):

const jchar * data = GetData(id, row, col); // get pointer of the string ends w/ \0 
unsigned int len = wcslen((wchar_t*)data); 
// The best would be to prevent this function to duplicate the data. 
jstring str = env->NewString(data, len); 
return str; 

注:上面的代码使它更快(而不是15)20%通过使用Unicode数据代替UTF8(NewString代替NewStringUTF) 。这表明,如果我可以删除该步骤或优化它,我会获得相当不错的性能提升。

+1

这是从哪里来的?如果拷贝花费的时间比磁盘IO长,假设没有什么真正的愚蠢行为,我会感到惊讶。 –

+0

- 1)您是否需要将整个文件同时存储在内存中? - 2)为什么要使用JNI? – claymore1977

+0

它通常是在开发环境中从.jar文件和文件直接加载的。所以你可以假设几乎没有磁盘IO,因为它应该已经被加载了。 我不需要它在内存中的所有文件,但文件应该已经存在,因为它是jar文件的一部分。 JNI让我使用指针...但是由于我用C++有更多的xp,我可能会误解一些Java的特性。有什么办法可以防止数据的复制(如根据内存中的位置重新使用引用?) – MasterPlanMan

回答

0

呃......好像我想做的事情不是被Java“支持”,除非我破解它..我相信有可能通过使用GetStringCritical来获得实际的char数组地址,然后找到超出字符的数量等等,但这远远超出了“安全”编程。

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

数据文件: numRow行,数numCols, 对于每个小区,添加一个整数值(在我的情况下的偏移量在存储器指向字符串) 针对每个小区,添加字符串\ 0

通过结束使用偏移量值,我可以稍微减少字符串创建和字符串查询的数量。我尝试使用globalref来保持DLL中的字符串,但它使它慢了4倍。

0

我从来没有使用过JNI,但是......让它返回一个实现CharSequence的自定义类,也许还有一些其他接口,比如CharSequence>而不是字符串?看起来你不太可能有这样的数据损坏问题。

+0

这可能是值得尝试的。例如,如果我可以创建一个类,使我可以重用加载的数据(来自内存流或字节数组之类的东西),而不必诉诸于复制数据。但是这迫使我重做大部分的String类函数。将该类与哈希表兼容也很容易吗? – MasterPlanMan

+0

@Adam - 当然 - 只是实现hashcode()和equals()。它们的尺寸很小,但仍需要小心。使用JDK字符串来源作为参考。你可能也会想toString()。 –

+0

是否可以定义字符串类的子代?因为有很多需要字符串对象的地方..所以创建这个类只会推迟数据的重复。= S否则,我将不得不改变代码,在它请求一个字符串并更改它,因此它请求一个charsequence 。 – MasterPlanMan

0

我认为首先您必须了解为什么C++版本运行速度提高15%,以及为什么性能改进不能直接转换为Java。为什么不能在Java中将代码编写速度提高15%?

让我们看看你的问题。你已经通过使用C++ dll消除了解析。 (为什么不能在Java中完成?)。然后,我的理解是:

  1. 你提出直接
  2. 你想防止垃圾回收器触摸这些修改jstrings(通过保持对其引用)来操纵jstrings的内容,因此可能会修改JVM的行为,并在垃圾收集器最终进行垃圾收集时搞砸垃圾收集器。

在你允许它们被垃圾收集之前,你会“修复”这些引用吗?

如果你建议做自己的内存管理,为什么你使用java呢?为什么不用纯粹的C++来做呢?

假设您希望继续使用Java,当您创建一个String时,它本身就是一个新的Object,但它指向的数据不一定。你可以通过调用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 

所以你可以看到,尽管String对象是不同的数据,实际的字节都没有。因此,您的修改可能实际上并不相关,JVM可能已经在为您做这些工作。值得一提的是,如果你开始修改jstrings的内部结构,这很可能会导致这种情况发生。

我的建议是找出你可以在算法方面做些什么。使用纯Java进行开发总是比较快速,因为它可以结合使用Java。使用纯Java可以找到更好的解决方案。

+0

该项目是在Java(这是一个3岁),所以我不能真正改变为C++,除非它是通过jni的某些特定情况。我宁愿保持java而不是使用C++。 我的目标是不要让代码快15% - 我需要它更快。所以就地加载是一个很好的方法来尝试做到这一点 - 虽然我接受新的建议。 =) 现在,我不想做我自己的内存管理,但我认为这将防止任何问题..但从我可以看到,我们可以使用外部缓冲区设置字符串数据?那么可以防止任何重复的数据? – MasterPlanMan

+0

此外,还有一些方法可以通过使用不同的“格式”和包含字符串位置的索引来减少通过java进行解析的数量......但最好不会解析。在C++中这很容易,但是在java中很难做到(至少对我来说)..因此我的问题给你们。 – MasterPlanMan

+0

“,但它指向的数据不一定是” - 这正是我试图做的事实。如何指定数据来自char [],byte [],jni等? (不是已经定义好的字符串) 如果可以这样做的话,我只需要用Java加载数据(没有更多的jni!),并且直接指定字符串数据而不重复。 – MasterPlanMan