1

我正在尝试衡量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}

这意味着 400 个电话在 0 秒内返回?这是不可能的。

我只是想看看每个线程需要多少时间来插入记录,然后将这些数字存储在 a 中Map并从主线程打印该映射。

我认为我假设它正在发生的问题是因为这里的线程安全,这就是resetlap我猜它在做零时设置为 Map 的原因。

如果是,我该如何避免这个问题?并且还需要histogram map从主线程传递到Task的构造函数?因为我需要在所有线程完成后打印该 Map 以查看那里的数字。

更新:- 如果我删除divide by 1000将数字存储为毫秒的东西,那么我可以看到除zero. 所以看起来不错。

但是我发现的另一件事是数字不一致,如果我总结每个线程的时间,我会得到一些数字。而且我还打印了整个程序完成的时间。所以我比较了这两个数字,它们相差很大

4

3 回答 3

1

为避免秒表出现并发问题,您最好runRunnable. 这样每个线程都有自己的秒表。

至于你看到的时间,我绝对希望一个简单的记录插入会在一秒钟内发生。看到 400 个插入都在不到一秒的时间内发生,我一点也不感到惊讶。通过使用秒表中的毫秒值作为 HashMap 键,您可以获得更好的结果。

更新

对于秒表并发问题,我建议如下:

class Task implements Runnable {

    private final AtomicInteger id;
    // Remove the stopwatch from here
    //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() {

        // And add it here
        StopWatch totalExecTimer = new StopWatch(Task.class.getSimpleName() + ".totalExec");

        dbConnection = getDBConnection();

通过这种方式,每个线程,实际上是每个任务,都会获得自己的副本,而您不必担心并发性。使 StopWatch 线程安全原样可能比它的价值更麻烦。

更新 2

话虽如此,那么您在评论中提到的方法可能会产生更好的结果,因为计时机制的开销更少。

为了回答您关于累积线程时间和程序总运行时间差异的问题,我会轻率地说:“您期望什么?”。

这里有两个问题。一是你没有测量每个线程的总运行时间,只是你正在做数据库插入的那一点。

另一个是测量整个应用程序的运行时间没有考虑线程执行时间的任何重叠。即使您正在测量每个任务的总时间,并假设您在多核机器上运行,我预计累积时间会超过程序执行的经过时间。这就是并行编程的好处。

于 2013-02-08T01:30:06.157 回答
1

作为附加说明, System.currentTimeMillis() 是伪时间并且具有一定程度的不准确性。使用 System.nanoTime() 是一种更准确的方法

long start = System.nanoTime();

long end = System.nanoTime();

long timeInSeconds = TimeUnit.NANOSECONDS.convert(end-start, TimeUnit.SECONDS);
于 2013-02-08T03:05:05.117 回答
0

由于多种原因,currentTimeMillis 倾向于不在每次调用时“刷新”其值。您应该使用 nanoTime 进行高分辨率测量。

而且您的代码浪费了几分之一秒。您的 toString 方法应该使用sb.append((cumulativeTime / 1000.0));,以便您获得小数秒。

但是你的计时机制的开销是巨大的,如果你曾经测量过一些东西,很大一部分时间将只是计时开销。测量多个操作而不是一个操作要好得多。

于 2013-02-08T01:45:44.063 回答