JcTools中队列性能测试记录

对JcTools中MPMC和SPMC两种队列的性能进行测试、计较并记录。


前言

记录一下使用JcTools的MPMC和SPMC两种无锁队列的性能比较。


一、搭建项目

maven依赖

<dependency>
    <groupId>org.jctools</groupId>
    <artifactId>jctools-core</artifactId>
    <version>4.0.2</version>
</dependency>
<dependency>
   <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.24</version>
</dependency>

二、编写代码

1. 引入库

代码如下(示例):
Main

public class Main {

    public static final String JCTOOLS_TYPE = "jctools.type";
    public static final String JCTOOLS_MESSAGE_SIZE = "jctools.message.size";
    public static final String JCTOOLS_QUEUE_SIZE = "jctools.queue.size";
    public static final String JCTOOLS_PRODUCER_THREAD_MIN = "jctools.producer.thread.min";
    public static final String JCTOOLS_PRODUCER_THREAD_MAX = "jctools.producer.thread.max";
    public static final String JCTOOLS_PRODUCER_QUEUE_SIZE = "jctools.producer.queue.size";
    public static final String JCTOOLS_CONSUMER_QUEUE_SIZE = "jctools.consumer.queue.size";
    public static final String JCTOOLS_CONSUMER_THREAD_MIN = "jctools.consumer.thread.min";
    public static final String JCTOOLS_CONSUMER_THREAD_MAX = "jctools.consumer.thread.max";
    public static final String JCTOOLS_CONSUMER_SLEEP = "jctools.consumer.sleep";

    public static void main(String[] args) {
        String type = System.getProperty(JCTOOLS_TYPE);
        Integer messageSize = Integer.valueOf(System.getProperty(JCTOOLS_MESSAGE_SIZE));
        Integer queueSize = Integer.valueOf(System.getProperty(JCTOOLS_QUEUE_SIZE));
        Integer produceMin = Integer.valueOf(System.getProperty(JCTOOLS_PRODUCER_THREAD_MIN));
        Integer produceMax = Integer.valueOf(System.getProperty(JCTOOLS_PRODUCER_THREAD_MAX));
        Integer produceQueueSize = Integer.valueOf(System.getProperty(JCTOOLS_PRODUCER_QUEUE_SIZE));
        Integer consumeQueueSize = Integer.valueOf(System.getProperty(JCTOOLS_CONSUMER_QUEUE_SIZE));
        Integer consumeMin = Integer.valueOf(System.getProperty(JCTOOLS_CONSUMER_THREAD_MIN));
        Integer consumeMax = Integer.valueOf(System.getProperty(JCTOOLS_CONSUMER_THREAD_MAX));
        Integer consumeSleep = Integer.valueOf(System.getProperty(JCTOOLS_CONSUMER_SLEEP));
        Result result = null;
        if ("mpmc".equals(type)) {
            System.out.println("===========================【" + type + "】测试开始===========================");
            MpmcTest mpmcTest = MpmcTest.INSTANCE;
            mpmcTest.setMessageSize(messageSize);
            mpmcTest.setQueueSize(queueSize);
            mpmcTest.setConsumerExecutor(new ThreadPoolExecutor(consumeMin, consumeMax, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<>(consumeQueueSize), new ThreadPoolExecutor.CallerRunsPolicy()));
            mpmcTest.setProducerExecutor(new ThreadPoolExecutor(produceMin, produceMax, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<>(produceQueueSize), new ThreadPoolExecutor.CallerRunsPolicy()));
            mpmcTest.setSleepTime(consumeSleep);
            result = mpmcTest.test();
        }
        if ("spmc".equals(type)) {
            System.out.println("===========================【" + type + "】测试开始===========================");
            SpmcTest spmcTest = SpmcTest.INSTANCE;
            spmcTest.setMessageSize(messageSize);
            spmcTest.setQueueSize(queueSize);
            spmcTest.setConsumerExecutor(new ThreadPoolExecutor(consumeMin, consumeMax, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<>(consumeQueueSize), new ThreadPoolExecutor.CallerRunsPolicy()));
            spmcTest.setProducerExecutor(Executors.newSingleThreadExecutor());
            spmcTest.setSleepTime(consumeSleep);
            result = spmcTest.test();
        }
        if (result != null) {
            System.out.println("===========================【" + type + "】测试结束===========================");
            System.out.println("发送成功数:\t" + result.getProduceSuccess());
            System.out.println("发送失败数:\t" + result.getProduceFail());
            System.out.println("消费成功数:\t" + result.getConsumeSuccess());
            System.out.println("消费失败数:\t" + result.getConsumeFail());
            System.out.println("总耗时:\t" + result.getDurTime()/1000000);
        }
    }
}

Result

@Data
public class Result {
    private Integer consumeSuccess = 0;
    private Integer consumeFail = 0;
    private Integer produceSuccess = 0;
    private Integer produceFail = 0;
    private Long durTime = 0L;
}

MpmcTest

@Data
public class MpmcTest {
    private MpmcTest() {
    }

    public static final MpmcTest INSTANCE = new MpmcTest();

    private ThreadPoolExecutor producerExecutor;

    private ThreadPoolExecutor consumerExecutor;

    private Integer messageSize;

    private Integer queueSize;

    private Integer sleepTime;

    private String prefix = "mpmc_thread_";
    private static AtomicInteger consumeSuccess = new AtomicInteger(0);
    private static AtomicInteger consumeFail = new AtomicInteger(0);
    private static AtomicInteger produceSuccess = new AtomicInteger(0);
    private static AtomicInteger produceFail = new AtomicInteger(0);

    public Result test() {
        MpmcArrayQueue<Long> queue = new MpmcArrayQueue<>(this.queueSize);
//        CountDownLatch countDownLatch = new CountDownLatch(messageSize);
        long startTime = System.nanoTime();
        for (int i = 0; i < messageSize; i++) {
            Long index = (long) i;
            producerExecutor.execute(() -> {
                while (!queue.offer(index)) {
                    System.out.println("===================任务发布失败,线程:" + prefix + index);
                    produceFail.getAndIncrement();
                }
                System.out.println("===================任务发布成功,线程:" + prefix + index);
                produceSuccess.getAndIncrement();
                consumerExecutor.execute(() -> {
                    Long retIndex = queue.poll();
                    if (retIndex == null) {
                        System.out.println(">>>>>>>>>>>>>>>>>>消费失败,线程:" + prefix + retIndex);
                        consumeFail.getAndIncrement();
                    } else {
                        System.out.println("消费成功,线程:" + prefix + retIndex);
                        consumeSuccess.getAndIncrement();
                        try {
                            Thread.sleep(sleepTime);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    if (messageSize <= (consumeSuccess.get() + consumeFail.get())) {
                        consumerExecutor.shutdown();
                    }
//                    countDownLatch.countDown();
                });
            });
        }
        Result result = new Result();
        try {
//            countDownLatch.await(120, TimeUnit.SECONDS);
//            System.out.println("countDown"+countDownLatch.getCount());
//            consumerExecutor.shutdownNow();
            if (consumerExecutor.awaitTermination(Long.MAX_VALUE, TimeUnit.MINUTES)) {
                result.setConsumeSuccess(consumeSuccess.get());
                result.setConsumeFail(consumeFail.get());
                result.setProduceSuccess(produceSuccess.get());
                result.setProduceFail(produceFail.get());
                result.setDurTime(System.nanoTime() - startTime);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return result;
    }
}

SpmcTest

@Data
public class SpmcTest {
    private SpmcTest() {
    }

    public static final SpmcTest INSTANCE = new SpmcTest();

    private ExecutorService producerExecutor;

    private ExecutorService consumerExecutor;

    private Integer messageSize;

    private Integer queueSize;

    private Integer sleepTime;

    private String prefix = "spmc_thread_";

    public Result test() {
        MpmcArrayQueue<Long> queue = new MpmcArrayQueue<>(this.queueSize);
        AtomicInteger consumeSuccess = new AtomicInteger();
        AtomicInteger consumeFail = new AtomicInteger();
        AtomicInteger produceSuccess = new AtomicInteger();
        AtomicInteger produceFail = new AtomicInteger();
        long startTime = System.nanoTime();
        for (int i = 0; i < messageSize; i++) {
            Long index = (long) i;
            producerExecutor.execute(() -> {
                while (!queue.offer(index)) {
                    produceFail.getAndIncrement();
                }
                produceSuccess.getAndIncrement();
                consumerExecutor.execute(() -> {
                    Long retIndex = queue.poll();
                    if (retIndex == null) {
                        System.out.println("===================消费失败,线程:" + prefix + retIndex);
                        consumeFail.getAndIncrement();
                    } else {
                        System.out.println("消费成功,线程:" + prefix + retIndex);
                        consumeSuccess.getAndIncrement();
                        try {
                            Thread.sleep(sleepTime);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    if (messageSize <= (consumeSuccess.get() + consumeFail.get())) {
                        consumerExecutor.shutdown();
                    }
                });
            });
        }
        Result result = new Result();
        try {
            if (consumerExecutor.awaitTermination(Long.MAX_VALUE, TimeUnit.MINUTES)) {
                result.setConsumeSuccess(consumeSuccess.get());
                result.setConsumeFail(consumeFail.get());
                result.setProduceSuccess(produceSuccess.get());
                result.setProduceFail(produceFail.get());
                result.setDurTime(System.nanoTime() - startTime);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return result;
    }
}

三、构建脚本并测试

1. 构建启动脚本

在Idea里添加如下配置

-Djctools.type=mpmc -Djctools.message.size=100000 -Djctools.queue.size=20000 -Djctools.producer.thread.min=10 -Djctools.producer.thread.max=40 -Djctools.producer.queue.size=10000 -Djctools.consumer.thread.min=20 -Djctools.consumer.thread.max=80 -Djctools.consumer.queue.size=20000 -Djctools.consumer.sleep=5

2. 更换条件,获取结果

方案一
数据量 100000 queueSize 10000 生产线程:10 40 10000 消费线程:20 80 20000 消费时间 5ms
SPMC 6793 qps 14,705 cpu 1% 内存 10m
MPMC 5327 qps 18,867 cpu 1% 内存 10m

方案二
数据量 1000000 queueSize 10000 生产线程:10 40 10000 消费线程:20 80 20000 消费时间 5ms
SPMC 69511 qps 14,388 cpu 3% 内存 170m
MPMC 47436 qps 21,097 cpu 1% 内存 100m

方案三
数据量 1000000 queueSize 10000 生产线程:10 40 10000 消费线程:200 800 20000 消费时间 5ms
SPMC 12492 qps 80,000 cpu 2% 内存200m
MPMC 16457 qps 62,500 cpu 2% 内存150m

方案四
数据量 10000000 queueSize 10000 生产线程:10 40 10000 消费线程:200 800 20000 消费时间 5ms
SPMC 108148 qps 92,592 cpu 初始8%(4s) 后2% 内存 峰值2833m 均值2000m
MPMC 142194 qps 70,373 cpu 2% 内存150m

方案五
数据量 10000000 queueSize 10000 生产线程:100 400 10000 消费线程:200 800 20000 消费时间 5ms
SPMC 107203 qps 92,592 cpu 初始8%(4s) 后2% 内存 峰值2833m 均值2000m
MPMC 161223 qps 161223 cpu 2% 内存150m

方案六
数据量 10000000 queueSize 10000 生产线程:100 400 100000 消费线程:200 800 20000 消费时间 5ms
SPMC 107203 qps 92,592 cpu 初始8%(4s) 后2% 内存 峰值2833m 均值2000m
MPMC 145828 qps 161223 cpu 2% 内存150m

方案七
数据量 10000000 queueSize 10000 生产线程:100 400 100000 消费线程:200 800 200000 消费时间 5ms
SPMC 107203 qps 92,592 cpu 初始8%(4s) 后2% 内存 峰值2833m 均值2000m
MPMC N/A qps N/A cpu 2% 内存150m 大量线程发布失败,93s执行470000

方案七
数据量 10000000 queueSize 100000 生产线程:100 400 100000 消费线程:200 800 200000 消费时间 5ms
SPMC 286462 qps 92,592 cpu 初始8%(4s) 后2% 内存 峰值2425m 均值2000m 出现大量发送失败,但最终都成功;发送成功数10000000;发送失败数2084979727
MPMC N/A qps N/A cpu 2% 内存150m 大量线程发布失败,5‘33s执行1223724

方案八
数据量 10000000 queueSize 40000 生产线程400 1000 20000 消费线程:1000 4000 40000 消费时间 5ms
SPMC 151742 qps 92,592 cpu 初始8%(4s) 后2% 内存 峰值2825m 均值2000m
MPMC 208681 qps cpu 2% 内存150m

方案九
数据量 10000000 queueSize 10000 生产线程400 1000 20000 消费线程:1000 4000 40000 消费时间 5ms
SPMC 144742 qps 92,592 cpu 初始8%(4s) 后2% 内存 峰值2825m 均值2000m 出现大量发送失败,但最终都成功;发送成功数10000000;发送失败数-1276524988
MPMC 208681 qps cpu 2% 内存150m

总结

未完待续

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C#队列可以通过System.Collections命名空间的Queue类来实现。队列是一种先进先出(FIFO)的数据结构,可以在队列的末尾添加元素,从队列的开头移除元素。以下是一个简单的示例代码: ``` using System; using System.Collections; class Program { static void Main(string[] args) { Queue myQueue = new Queue(); myQueue.Enqueue("apple"); myQueue.Enqueue("banana"); myQueue.Enqueue("orange"); Console.WriteLine($"队列的元素个数:{myQueue.Count}"); // 遍历队列 foreach (string fruit in myQueue) { Console.WriteLine(fruit); } // 移除队列的第一个元素 string firstFruit = (string)myQueue.Dequeue(); Console.WriteLine($"移除的元素是:{firstFruit}"); Console.WriteLine($"队列的元素个数:{myQueue.Count}"); // 查看队列的第一个元素 string nextFruit = (string)myQueue.Peek(); Console.WriteLine($"队列的下一个元素是:{nextFruit}"); } } ``` 运行结果: ``` 队列的元素个数:3 apple banana orange 移除的元素是:apple 队列的元素个数:2 队列的下一个元素是:banana ``` 在上面的示例代码,首先创建了一个空的队列`myQueue`,然后使用`Enqueue`方法向队列添加元素。使用`Count`属性可以获取队列的元素个数。使用`foreach`循环可以遍历整个队列。使用`Dequeue`方法可以移除队列的第一个元素,并将其返回。使用`Peek`方法可以查看队列的下一个元素,但不会将其移除。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值