我很好奇,以下代码段的优化程度将会有多远。
据我所知,无论何时扩展StringBuffer的容量,都需要一些CPU工作,因为需要重新分配其内容。但是,我猜Java编译器优化可以预先计算所需的容量,而不是进行多次重新分配。
问题是:是否会优化以下代码片段?
public static String getGetRequestURL(String baseURL, Map<String, String> parameters) {
StringBuilder stringBuilder = new StringBuilder();
parameters.forEach(
(key, value) -> stringBuilder.append(key).append("=").append(value).append("&"));
return baseURL + "?" + stringBuilder.delete(stringBuilder.length(),1);
}
答案 0 :(得分:2)
在Java中,大多数优化都是由运行时的编译器执行的,所以javac优化通常不重要。
因此,不需要Java编译器来优化字符串连接,尽管只要不涉及循环,所有人都倾向于这样做。您可以使用javap(JDK附带的java反编译器)来检查这种编译时优化的程度。
那么,javac可以想象优化这个吗?要确定字符串构建器的长度,必须迭代映射两次。由于java不具有const引用,并且编译器对Map
没有特殊处理,因此编译器无法确定此重写是否会保留代码的含义。即使可能,但根本不清楚增益是否值得迭代两次的成本。毕竟,现代处理器可以在单个cpu指令中复制4到8个字符。由于内存访问是顺序的,因此在增长缓冲区时不会丢失任何缓存。另一方面,第二次迭代地图可能会导致额外的缓存未命中,因为Map条目(以及它们引用的字符串)可以分散在整个主存储器中。
无论如何,我不担心这段代码的效率。即使您的URL长度为1000个字符,调整缓冲区大小也需要0.1微秒。除非你有证据证明这确实是一个性能热点,否则你的时间可能会更好地花在其他地方。
答案 1 :(得分:2)
首先:
您可以通过使用javap工具查看字节码来了解(javac)编译时优化的发生。
您可以通过让JVM转储本机代码来了解执行哪些JIT编译器优化。
因此,如果您出于实际原因需要知道您的代码是如何优化的(在特定平台上),那么您应该检查。
实际上,javac的优化非常简单,并且不会达到预先计算缓冲区大小的程度。我还没有检查过,但我希望JIT编译器也是如此。我怀疑是否会尝试使用&#34;最佳&#34;来预先分配StringBuilder
。大小
为什么?
原因如下:
答案 2 :(得分:0)
一种优化方法是在stringBuilder中设置baseURL和&符号,而不是使用结尾处的String串联,例如:
public static String getGetRequestURL(String baseURL, Map<String, String> parameters) {
StringBuilder stringBuilder = new StringBuilder(baseURL);
stringBuilder.append("&");
parameters.forEach((key, value) -> stringBuilder.append(key).append("=").append(value).append("&"));
stringBuilder.setLength(stringBuilder.length() - 1);
return stringBuilder.toString();
}
如果您想提高速度,并且由于javac或JIT不会根据潜在的字符串大小进行优化,则可以自己跟踪它而不会产生太多开销,但是可以添加一个最大大小的跟踪器,例如:
protected static URL_SIZE = 256;
public static String getGetRequestURL(String baseURL, Map<String, String> parameters) {
StringBuilder stringBuilder = new StringBuilder(URL_SIZE);
stringBuilder.append(baseURL);
stringBuilder.append("&");
parameters.forEach((key, value) -> stringBuilder.append(key).append("=").append(value).append("&"));
int size = stringBuilder.length();
if (size > URL_SIZE) {
URL_SIZE = size;
}
stringBuilder.setLength(size - 1);
return stringBuilder.toString();
}
也就是说,在对100万个呼叫进行了一些测试之后,我发现不同版本的执行时间为(以毫秒为单位):