1

のパフォーマンスを測定しようとしていますDatabase Insert。そのために、メソッドの前にカウンターをリセットし、メソッドが完了した後の時間を計算StopWatchするクラスを作成しました。executeUpdateexecuteUpdate

そして、各スレッドにかかる時間を確認しようとしているので、それらの数値を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、メインスレッドからタスクのコンストラクターに渡す必要がありますか? すべてのスレッドが終了した後にそのマップを印刷して、そこにある数字を確認する必要があるためです。

更新:-divide by 1000数値をミリ秒として保存するもの を削除すると、 以外の数値を表示できzeroます。それで良さそうです。

しかし、もう1つわかったことは、数値が一貫していないことです。各スレッドの時間を合計すると、その数値が得られます。また、プログラム全体が終了する時間を印刷しています。だから私はこれらの2つの数字を比較すると、大きな差があります

4

3 に答える 3

1

ストップウォッチでの同時実行の問題を回避するには、のrunメソッド内でローカル変数として新しいストップウォッチを作成することをお勧めしますRunnable。そうすれば、各スレッドに独自のストップウォッチがあります。

あなたが見ているタイミングに関しては、私は絶対に簡単なレコード挿入が1秒以内に起こることを望んでいます。すべてが1秒未満で発生する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

そうは言っても、タイミングメカニズムのオーバーヘッドが少ないため、コメントで言及したアプローチの方がおそらくより良い結果が得られます。

プログラムの累積スレッド時間と合計実行時間の違いについての質問に答えるために、私は「何を期待しましたか?」とざっと言います。

ここには2つの問題があります。1つは、各スレッドの合計実行時間を測定しているのではなく、DB挿入を実行している部分だけを測定していることです。

もう1つは、アプリケーション全体の実行時間を測定しても、スレッドの実行時間の重複は考慮されないということです。各タスクの合計時間を測定していて、マルチコアマシンで実行していると仮定した場合でも、累積時間はプログラム実行の経過時間よりも長くなると思います。それが並列プログラミングの利点です。

于 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));、小数秒を取得するために使用する必要があります。

しかし、タイミング メカニズムのオーバーヘッドは相当なものであり、何かを測定する場合、時間の大部分はタイミング オーバーヘッドになります。1 つの操作だけではなく、複数の操作を測定する方がはるかに優れています。

于 2013-02-08T01:45:44.063 に答える