我正在尝试衡量Database Insert
的效果。因此,我编写了一个StopWatch
类,它将在executeUpdate
方法之前重置计数器,并计算executeUpdate
方法完成后的时间。
我正在尝试查看每个帖子花了多少时间,所以我将这些数字保存在ConcurrentHashMap
中。
以下是我的主要课程 -
public static void main(String[] args) {
final int noOfThreads = 4;
final int noOfTasks = 100;
final AtomicInteger id = new AtomicInteger(1);
ExecutorService service = Executors.newFixedThreadPool(noOfThreads);
for (int i = 0; i < noOfTasks * noOfThreads; i++) {
service.submit(new Task(id));
}
while (!service.isTerminated()) {
}
//printing the histogram
System.out.println(Task.histogram);
}
下面是实现Runnable的类,我试图在插入数据库时测量每个线程的性能,这意味着每个线程花费多少时间插入数据库 -
class Task implements Runnable {
private final AtomicInteger id;
private StopWatch totalExecTimer = new StopWatch(Task.class.getSimpleName() + ".totalExec");
public static ConcurrentHashMap<Long, AtomicLong> histogram = new ConcurrentHashMap<Long, AtomicLong>();
public Task(AtomicInteger id) {
this.id = id;
}
@Override
public void run() {
dbConnection = getDBConnection();
preparedStatement = dbConnection.prepareStatement(Constants.INSERT_ORACLE_SQL);
//other preparedStatement
totalExecTimer.resetLap();
preparedStatement.executeUpdate();
totalExecTimer.accumulateLap();
final AtomicLong before = histogram.putIfAbsent(totalExecTimer.getCumulativeTime() / 1000, new AtomicLong(1L));
if (before != null) {
before.incrementAndGet();
}
}
}
以下是StopWatch class
/**
* A simple stop watch.
*/
protected static class StopWatch {
private final String name;
private long lapStart;
private long cumulativeTime;
public StopWatch(String _name) {
name = _name;
}
/**
* Resets lap start time.
*/
public void resetLap() {
lapStart = System.currentTimeMillis();
}
/**
* Accumulates the lap time and return the current lap time.
*
* @return the current lap time.
*/
public long accumulateLap() {
long lapTime = System.currentTimeMillis() - lapStart;
cumulativeTime += lapTime;
return lapTime;
}
/**
* Gets the current cumulative lap time.
*
* @return
*/
public long getCumulativeTime() {
return cumulativeTime;
}
public String getName() {
return name;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(name);
sb.append("=");
sb.append((cumulativeTime / 1000));
sb.append("s");
return sb.toString();
}
}
运行上面的程序后,我可以看到已插入400行。当它打印直方图时,我只是看到这样 -
{0=400}
这意味着在0秒内有400个电话回来了吗?这是不可能的。
我只是想看看每个帖子花了多少时间插入记录,然后将这些数字存储在Map
中并从主线程打印该地图。
我认为我假设这个问题正在发生,因为这里的线程安全,这就是为什么每当它resetlap
为零时我都会设置为Map。
如果是,我该如何避免这个问题?还需要将histogram map
从主线程传递给Task的构造函数吗?因为我需要在完成所有线程后打印该Map以查看有哪些数字。
更新: -
如果我删除divide by 1000
的东西以将数字存储为毫秒,那么我可以看到除zero
之外的一些数字。所以看起来不错。
但我发现更多的是数字不一致,如果我总结每个线程的时间,我会得到一些数字。而且我也在打印整个程序完成的时间。所以我比较这两个数字,它们的差异很大
答案 0 :(得分:1)
为避免使用秒表出现并发问题,最好在run
的{{1}}方法中创建一个新变量作为局部变量。这样每个线程都有自己的秒表。
至于你所看到的时间,我绝对希望在一秒钟内完成一个简单的记录插入。看到400个插件都在不到一秒的时间内发生,这些都不会让我感到惊讶。通过使用秒表的毫秒值作为HashMap键,可以获得更好的结果。
<强>更新强>
对于秒表并发问题,我建议这样的事情:
Runnable
通过这种方式,每个线程(实际上每个Task)都有自己的副本,而且您不必担心并发。使StopWatch线程安全,可能比它的价值更麻烦。
更新2
话虽如此,你在评论中提到的方法可能会给出更好的结果,因为计时机制的开销较少。
为了回答你关于累积线程时间和程序运行时间差异的问题,我会轻率地说:“你期待什么?”。
这里有两个问题。一个是你没有测量每个线程的总运行时间,只是你正在进行数据库插入的位。
另一个是测量整个应用程序的运行时间没有考虑线程执行时间的任何重叠。即使您正在测量每个任务的总时间,并假设您在多核计算机上运行,我预计累计时间将超过程序执行的时间。这就是并行编程的好处。
答案 1 :(得分:1)
作为补充说明,System.currentTimeMillis()是伪时间并且具有一定程度的无懈可击。使用System.nanoTime()是一种更准确的方法
long start = System.nanoTime();
long end = System.nanoTime();
long timeInSeconds = TimeUnit.NANOSECONDS.convert(end-start, TimeUnit.SECONDS);
答案 2 :(得分:0)
由于多种原因,currentTimeMillis在每次调用时都不会“刷新”其值。您应该使用nanoTime进行高分辨率测量。
你的代码丢掉了几分之一秒。您的toString方法应使用sb.append((cumulativeTime / 1000.0));
,以便获得小数秒。
但是你的计时机制的开销是巨大的,如果你曾经测量过一些东西,那么很大一部分时间只是时间开销。测量一些操作而不仅仅是一个操作要好得多。