我正在迅速对SQL查询进行原型设计,而不是以正确的方式进行,我只是决定用一堆字符串连接来抨击它,整个时间都认为这会非常慢,但它没有因为我只是在测试查询。令我惊讶的是,Java说这段代码需要0毫秒才能完成?使用+
而不是StringBuilder
或类似内容时,是否需要花费更多时间?
long t = System.currentTimeMillis();
String load = "";
for (String s : loadFields)
load += s + ", ";
String sql = "SELECT ";
sql += load + "sum(relevance) AS 'score' " +
"FROM ( ";
for (int i = 0; i < searchFields.length; i++) {
sql += "SELECT ";
sql += load;
sql += rels[i] + " AS relevance FROM articles WHERE " +
searchFields[i];
sql += " LIKE '%" + terms[0] + "%' ";
for (int z = 1; z < terms.length; z++)
sql += "AND " + searchFields[i] + " LIKE '%" + terms[z] + "%' ";
if (i != searchFields.length - 1) sql += " UNION ALL ";
}
sql += ") results GROUP BY " + load.substring(0, load.length() - 2) + " ";
sql += "ORDER BY score desc, date desc";
System.out.println("Build Time: " + (System.currentTimeMillis() - t) + " ms");
是的,这非常难看,但问题不在于预测SQL,而是告诉我为什么这么快。
Build Time: 0 ms
编辑:我用20个术语运行了10000次测试,大约花了10秒钟,大约是1/10毫秒。现在我考虑一下,很明显,除非我开始得到很长的字符串,否则计算并不多。
答案 0 :(得分:9)
你只做了29个连接 - 我希望这个连接时间不到一毫秒。
如果要针对StringBuilder实现测试此代码,则应该迭代10,000次左右(并进行适当的JVM预热)。
我很想知道这种情况下的确切区别是什么,因此我将您的代码转换为使用.concat()
和StringBuilder
并以10,000次迭代(2,000次预热)进行基准测试,其中包含5个字段和20个字段术语,所有这些术语随机生成32个字符串。
结果(以毫秒为单位):
plus: 19656 (0.5/ms)
concat: 5656 (1.77/ms)
builder: 578 (17.3/ms)
答案 1 :(得分:2)
你连接的字符串非常短。尝试使用长度为100,000或更多的字符串(编写一些代码来生成大字符串),然后执行数百次。使用短字符串和只有少数连接,差异太小而无法测量。
答案 2 :(得分:1)
您正在使用毫秒时钟测量时间。
您可以测量的绝对最小间隔为1毫秒。这通常是硬件时钟超过一百万个周期。当前一代CPU可以在那段时间内进行大量计算。
毫秒的实际粒度可能是毫秒数。 (与当前一代操作系统不太一样,但我记得时钟刻度为20毫秒的日子。)
无论哪种方式,测量时间通常为零都不足为奇。
要获得有意义的测量,您需要围绕该代码循环并运行数十万次。然后再绕一圈来重复运行测试......直到测量的时间稳定下来。
最后,这将是分配密集型的,您需要包括摊销的GC时间。 (使用StringBuilder的优化版本将部分地通过NOT分配密集来获胜。)
答案 3 :(得分:1)
我的理解是,执行+
意味着java正在创建原始字符串的副本,并且每次都将新部件添加到副本中。我认为这会慢一点,但无论如何它肯定会占用比StringBuilder
更多的内存。在您添加内容后,使用StringBuilder
并且仅添加.toString
或String.valueOf()
似乎更有效率。