1

我正在对客户端代码中的几个方法进行基准测试,以查看这些方法花费了多少时间。所以我编写了一个多线程程序,它将产生多个线程,然后我将测量这些方法在客户端代码和服务器端代码中花费的时间。

我有一个ConcurrentHashMap声明为

public static ConcurrentHashMap<Long, Long> map = new ConcurrentHashMap<Long, Long>();

现在我试图找出在 X 毫秒内返回了多少呼叫,所以我将这些数字存储在上面的地图中。所以上面的地图会存储这样的东西——

KEY will be Milliseconds
and VALUE will be Number of calls came back in those milliseconds.

下面是代码,我有。

final long start = System.nanoTime();

method.getInformation();

final long end = System.nanoTime() - start;
final long key = end / 1000000L;
boolean done = false;
while(!done) {
    Long oldValue = map.putIfAbsent(key, 1L);
    if(oldValue != null) {
        done = map.replace(key, oldValue, oldValue + 1);
    } else {
        done = true;
    }
}

我想看看上面的代码有没有问题?

为什么我要问的是,如果我正在运行我的Multithreading program,那么server CPU usage通常会转到周围80-90%。但是如果我从服务器端代码中删除上述基准测试代码,那么CPU usage不要去80-90%. 所以这就是原因,我想看看是否有更好的方法来编写上述基准测试代码来实现上述相同的场景?

谢谢您的帮助。

更新:-

我正在使用 unix 中的 TOP 命令监视服务器端的 CPU 和内存使用情况-

top - 17:13:18 up 25 days, 23:24,  4 users,  load average: 1.72, 1.43, 1.04
Tasks: 114 total,   1 running, 111 sleeping,   0 stopped,   2 zombie
Cpu0  : 79.2%us,  5.8%sy,  0.0%ni, 23.1%id,  0.0%wa,  0.0%hi,  1.9%si,  0.0%st
Cpu1  : 83.7%us,  3.7%sy,  0.0%ni, 40.7%id,  0.0%wa,  0.0%hi,  1.9%si,  0.0%st
Mem:   6127684k total,  5122736k used,  1004948k free,   240436k buffers
Swap:  1331196k total,        0k used,  1331196k free,  2485984k cached

下面是它运行时的快照,我刚刚捕获

4

3 回答 3

0

所以我用你的时间检查代码做了一个快速测试,并能够在我的四核 2013 Macbook Pro 上在 8.8 秒内运行 100万个。这意味着每次调用的成本低于 8ns(因为我的时间是挂钟并且考虑了线程的东西)。这是相当便宜的国际海事组织。我使用 100 个线程将 0 到 1000000 的随机值固定在一起,最终得到了 631k 个条目的堆大小,这似乎占用了不到 20-30mb 的核心。

在我看来,您的通话时间代码并不是一个大的性能问题。我想知道是否每个线程都在使用大量内存,而您的代码唯一要做的就是遇到内存障碍。将代码替换为volatile字段的单个更新或强制设置障碍以查看您是否获得相同的行为可能会很有趣。


我没有立即看到您的代码有任何问题,而且我认为这不会是一个大的性能问题。

我确实想知道那张地图有多大。似乎是如果您长时间运行它并在地图中有大量条目,那么内存需求将会很大。您可以尝试使用-Xmx参数增加内存。

如果是内存,那么 CPU 问题可能与 GC 相关。我会使用 Jconsole 来查看您是否内存不足。

于 2013-04-28T00:11:28.070 回答
0

这里有一些潜在的性能问题。

  1. System.nanoTime()您的基准测试代码为每个请求添加了两个调用。根据请求中涉及的实际工作量,这些调用可能很重要。

  2. 虽然您试图通过使用来减少并发瓶颈ConcurrentHashMap,但这并不能完全消除它。实际上,如果您有大量请求需要相同的毫秒数,那么特定计数器就会出现瓶颈。如果在特定计数器上存在争用,这将导致“旋转”,因为不同的线程竞争更新它。

  3. 如果(假设)地图变得非常大,您可能会开始遇到与内存使用相关的问题;例如,更大的工作集、增加的缓存和 TLB 争用、抖动。

但最重要的是,您添加的任何性能测量代码都可能会改变系统的性能特征。

于 2013-04-28T01:46:30.007 回答
0

性能/资源使用问题:

  • 在每次调用期间读取和写入 HashMap 可能会很昂贵,并发 hashmap 更是如此。这不是必需的。
  • 您还调用 System.nanoTime(),这可能很昂贵,并且您在每次迭代时都这样做。这不是必需的。

我的建议

  • 声明方法执行次数的静态运行计数器
  • 在同步块中,递增计数器
  • 当计数器达到阈值(例如 1000)时,确定经过的时间(以毫秒为单位)
  • 在列表中记录经过的时间

    static List<long> methodElapsedMillisList = new ArrayList<long>();
    final int methodCountBlock = 1000; 
    static long methodCountStartMillis = System.currentTimeMillis();
    static int methodCount = 0;
    static Object methodCountMonitorObj = new Object();
    
    
    // within instrumented method:
    synchronised (methodCountMonitorObj) {
           methodCount++;
           if (methodCount > methodCountBlock ) {
                long newMethodCountStartMillis = System.currentTimeMillis();
                long elapsedMillis = newMethodCountStartMillis - methodCountStartMillis;
                methodCountStartMillis = newMethodCounterStartMillis;
                methodElapsedMillisList.add(elapsedMillis);
                methodCount = 0;
           }
    }
    
于 2013-05-01T08:18:09.257 回答