在軟體測試中,經常會遇到随機數。我簡單分成了兩類:
- 簡單取随機數;
- 從一個集合中随機取值。
其實第二個場景包含在第一個場景内。對于接口測試來說,通常我們直接使用第二種場景比較多,就是從某一個集合中随機取一個值。如果更複雜一些,每個值擁有不同的權重,其中這個也可以轉化成第二個場景來說。
緣起
為什麼要把第二個場景和第一個場景分開呢,這個問題源于之前寫過的文章ConcurrentHashMap性能測試,當時發現自己封裝的
com.funtester.frame.SourceCode#random(java.util.List<F>)
方法性能存在瓶頸,特别消耗CPU資源。
雖然單機QPS也在50萬+,但是因為這個方法很多地方都會用到,是以還是想提升一些性能。是以我就搜尋了一些高性能随機數的功能,跟我之前搜到的資料一緻,使用
java.util.concurrent.ThreadLocalRandom
這個實作類是性能最高的,方法如下:
/**
* 擷取随機數,擷取1~num 的數字,包含 num
*
* @param num 随機數上限
* @return 随機數
*/
public static int getRandomInt(int num) {
return ThreadLocalRandom.current().nextInt(num) + 1;
}
針對第二種場景,還有一種實作思路:通過循環去集合中取即可。就是順序去取,而不是每次都從集合中随機。
舉個例子,我們有10萬測試使用者進行流量回放,示範代碼如下:
def funtest = {
random(drivers).getGetResponse(random(urls))
}
new FunQpsConcurrent(funtest).start()
這裡調用了兩次
com.funtester.frame.SourceCode#random(java.util.List<F>)
,當QPS到達10萬級别時候,理論上這個方法導緻的瓶頸還是有一些影響的。
多線程
是以我用了新思路進行改造,下面是兩種思路的對比壓測用例,這個測試用例裡面其實有三個實作:
- random
- AtomicInteger
- int
用例如下:
package com.funtest.groovytest
import com.funtester.base.constaint.FixedThread
import com.funtester.frame.SourceCode
import com.funtester.frame.execute.Concurrent
import java.util.concurrent.atomic.AtomicInteger
class FunTest extends SourceCode {
static int times = 1000
static int thread = 500
static def integers = 0..100 as List
static def integer = new AtomicInteger()
static def i = 0
static def size = integers.size()
public static void main(String[] args) {
RUNUP_TIME = 0
new Concurrent(new FunTester(), thread, "測試随機數性能").start()
}
private static class FunTester extends FixedThread {
FunTester() {
super(null, times, true)
}
@Override
protected void doing() throws Exception {
10000.times {random(integers)}
// 10000.times {integers.get(integer.getAndIncrement() % size)}
// 10000.times {integers.get(i++ % size)}
}
@Override
FunTester clone() {
return new FunTester()
}
}
}
由于測試中均達到了CPU硬體瓶頸,相同參數情況下結論比較明顯,就沒有進行多輪的對比測試。下面分享一下測試結果:
- random:1151
- AtomicInteger:3152
- int:2273
沒想到用了
java.util.concurrent.atomic.AtomicInteger
反而性能更高了,這個問題略微有點深奧,暫時沒有思路。
單線程
下面我們來測試一下單線程的性能,下面是我的用例:
package com.funtest.groovytest
import com.funtester.frame.SourceCode
import java.util.concurrent.atomic.AtomicInteger
class FunTestT extends SourceCode {
static int times = 1000000
static def integers = 0..100 as List
static def integer = new AtomicInteger()
static def i = 0
static def size = integers.size()
public static void main(String[] args) {
time {
// times.times {random(integers)}
// times.times {integers.get(integer.getAndIncrement() % size)}
times.times {integers.get(i++ % size)}
} , "随機數性能測試"
}
}
下面是測試結果,這裡我記錄了執行完所有循環次數的時間,機關是ms(毫秒)。
- random:763
- AtomicInteger:207
- int:270
這下結論明确了,就
java.util.concurrent.atomic.AtomicInteger
了。
末了
/**
* 随機選擇某個對象
*
* @param list
* @param index 自增索引
* @param <F>
* @return
*/
public static <F> F random(List<F> list, AtomicInteger index) {
if (list == null || list.isEmpty()) ParamException.fail("數組不能為空!");
return list.get(index.getAndIncrement() % list.size());
}
- 性能測試專題
- Java、Groovy、Go、Python
- 單測&白盒
- FunTester社群風采
- 測試理論雞湯
- 接口功能測試專題
- FunTester視訊專題
- 案例分享:方案、BUG、爬蟲
- UI自動化專題
- 測試工具專題