Disruptor移植到Android

简介

最近在做一个嵌入式AI项目,需要不断地从摄像头里取帧运算,送到模型里运算并得到结果传到后台去比对,主要的压力基本都在嵌入式开发板上,图像处理、模型检测还有其他的都是很消耗性能的,由于相机的流是一直发的和处理线程不能在一个线程里,一开始通过队列来实现一个生产者和消费者模型一开始选定的是CucurrentLinkedList效果不理想(100ms以上的延时在嵌入式设备上很致命),后来找到了Disruptor(线程撕裂者)由应该的一家叫LMAX的证券公司开发,号称1秒1核心600万单的处理速度,不知道他们用的什么cpu可能这个框架本身就是给服务器用的,现在我也要用到它。

准备环境

开发板:RK3399
系统:Android 7.1.2
Gradle:3.3.0
Android Studio:3.3
JDK:1.8.201

开始干活

环境都配好之后,我们只需要简单的几步就能够使用Disruptor了,废话不多说,上代码:

1、最外层build.gradle配置

buildscript {
    repositories {
        google()
        jcenter()
        
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.3.0'
        
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        google()
        jcenter()
        
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

2、app配置

到app目录下找到build.gradle配置文件增加以下配置:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 28
    defaultConfig {
        applicationId "com.watone.disruptor"
        minSdkVersion 21
        targetSdkVersion 22
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
//    compileOptions {
//        sourceCompatibility JavaVersion.VERSION_1_8
//        targetCompatibility JavaVersion.VERSION_1_8
//    }
}

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    implementation 'com.android.support:appcompat-v7:28.0.0'
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
    implementation 'org.wso2.orbit.com.lmax:disruptor:3.3.7.wso2v1'
}

测试案例

这个就比较简单了,以long型数据为例,其他的可自行定义,直接看代码:

LongEvent.java

package com.demo.disruptor;

public class LongEvent {
    private long value;

    public void set(long value)
    {
        this.value = value;
    }

    public long getValue() {
        return value;
    }
}

LongEventFactory.java

package com.demo.disruptor;

import com.lmax.disruptor.EventFactory;

public class LongEventFactory implements EventFactory<LongEvent> {

    public LongEvent newInstance()
    {
        return new LongEvent();
    }
}

LongEventHandler.java

package com.demo.disruptor;

import android.util.Log;

import com.lmax.disruptor.EventHandler;

public class LongEventHandler implements EventHandler<LongEvent> {

    @Override
    public void onEvent(LongEvent event, long sequence, boolean endOfBatch) throws Exception {
        Log.i("TAG", "Event: " + event.getValue());
    }
}

MainActivity.java

package com.demo.disruptor;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;

import com.lmax.disruptor.BlockingWaitStrategy;
import com.lmax.disruptor.EventFactory;
import com.lmax.disruptor.EventHandler;
import com.lmax.disruptor.InsufficientCapacityException;
import com.lmax.disruptor.RingBuffer;
import com.lmax.disruptor.SleepingWaitStrategy;
import com.lmax.disruptor.WaitStrategy;
import com.lmax.disruptor.YieldingWaitStrategy;
import com.lmax.disruptor.dsl.Disruptor;
import com.lmax.disruptor.dsl.ProducerType;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class MainActivity extends AppCompatActivity {
    private ExecutorService executor = Executors.newCachedThreadPool();
    //BlockingWaitStrategy 是最低效的策略,但其对CPU的消耗最小并且在各种不同部署环境中能提供更加一致的性能表现;
    private WaitStrategy BLOCKING_WAIT = new BlockingWaitStrategy();
    //SleepingWaitStrategy 的性能表现跟 BlockingWaitStrategy 差不多,对 CPU 的消耗也类似,但其对生产者线程的影响最小,适合用于异步日志类似的场景;
    private WaitStrategy SLEEPING_WAIT = new SleepingWaitStrategy();
    //YieldingWaitStrategy 的性能是最好的,适合用于低延迟的系统。在要求极高性能且事件处理线数小于 CPU 逻辑核心数的场景中,推荐使用此策略;例如,CPU开启超线程的特性。
    private WaitStrategy YIELDING_WAIT = new YieldingWaitStrategy();
    private final String TAG = MainActivity.class.getSimpleName();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        new Thread(new Runnable() {
            @Override
            public void run() {
                EventFactory<LongEvent> eventFactory = new LongEventFactory();
                ExecutorService executor = Executors.newSingleThreadExecutor();
                int ringBufferSize = 1; // RingBuffer 大小,必须是 2 的 N 次方;

                Disruptor<LongEvent> disruptor = new Disruptor<LongEvent>(eventFactory, ringBufferSize, executor, ProducerType.SINGLE, new YieldingWaitStrategy());
                EventHandler<LongEvent> eventHandler = new LongEventHandler();
                disruptor.handleEventsWith(eventHandler);
                disruptor.start();
                long start = System.currentTimeMillis();
                for (int i = 0; i < 10000000; i++) {
                    // 发布事件;
                    RingBuffer<LongEvent> ringBuffer = disruptor.getRingBuffer();
                    long sequence = 0;
                    try {
                        sequence = ringBuffer.tryNext();//请求下一个事件序号;
                        LongEvent event = ringBuffer.get(sequence);//获取该序号对应的事件对象;
//                long data = i;//获取要通过事件传递的业务数据;
                        event.set(i);
                    } catch (InsufficientCapacityException e) {
                        e.printStackTrace();
                    } finally {
                        ringBuffer.publish(sequence);//发布事件;
                    }
                }
                Log.i(TAG, "Totle = " + (System.currentTimeMillis() - start));
            }
        }).start();
    }
}

写在最后

sequence = ringBuffer.next();//请求下一个事件序号;
sequence = ringBuffer.tryNext();//尝试请求下一个事件序号;

这句要特殊讲一下子,缓存获取序列号有两种方式,上面那一个是请求一个序列号,如果没有闲置的序列号就等在那里,知道出现闲置的序列再进行下一步,下面那一种是尝试获取序列号的意思,如果没有闲置的序列就会抛一个异常。这里我选的是单线程模式运行会创建一个SingleProducerSequencer我们去看看next和tryNext的源代码

next

public long next() {
        return this.next(1);
    }

    public long next(int n) {
        if (n < 1) {
            throw new IllegalArgumentException("n must be > 0");
        } else {
            long nextValue = this.nextValue;
            long nextSequence = nextValue + (long)n;
            long wrapPoint = nextSequence - (long)this.bufferSize;
            long cachedGatingSequence = this.cachedValue;
            if (wrapPoint > cachedGatingSequence || cachedGatingSequence > nextValue) {
                this.cursor.setVolatile(nextValue);

                long minSequence;
                while(wrapPoint > (minSequence = Util.getMinimumSequence(this.gatingSequences, nextValue))) {
                    this.waitStrategy.signalAllWhenBlocking();
                    LockSupport.parkNanos(1L);
                }

                this.cachedValue = minSequence;
            }

            this.nextValue = nextSequence;
            return nextSequence;
        }
    }

重点在LockSupport.parkNanos(1L);,这个是通过Unsafe实现的睡眠public static void park() { U.park(false, 0L); },这个通过jdk1.8的源代码是点不进去的,因为Unsafe这个类在jdk1.8里是不存在的(官方解释是出于安全考虑),但是官方并没有将它从虚拟机里移除,所以相当于你不能用Unsafe类开发,但是你的程序可以在虚拟机跑起来。如果你想看源代码或者用这个类来开发可以下载openjdk1.8,它在private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
可以看出next方法有一个不断获取,不断休眠的一个操作,直到获取到序列号之后就会跳出循环(或者被打断,不过它只休眠1纳秒,我没遇到过被打断的情形),LockSupport.parkNanos(1L);这个休眠方法和sleep()是有一定差别的,一般不会用到只有在JUC的高级同步原语的实现中看到。具体Unsafe的实现原理这里不说,大家可以自己查资料研究下。

tryNext

public long tryNext() throws InsufficientCapacityException {
        return this.tryNext(1);
    }

    public long tryNext(int n) throws InsufficientCapacityException {
        if (n < 1) {
            throw new IllegalArgumentException("n must be > 0");
        } else if (!this.hasAvailableCapacity(n, true)) {
            throw InsufficientCapacityException.INSTANCE;
        } else {
            long nextSequence = this.nextValue += (long)n;
            return nextSequence;
        }
    }
    private boolean hasAvailableCapacity(int requiredCapacity, boolean doStore) {
        long nextValue = this.nextValue;
        long wrapPoint = nextValue + (long)requiredCapacity - (long)this.bufferSize;
        long cachedGatingSequence = this.cachedValue;
        if (wrapPoint > cachedGatingSequence || cachedGatingSequence > nextValue) {
            if (doStore) {
                this.cursor.setVolatile(nextValue);
            }

            long minSequence = Util.getMinimumSequence(this.gatingSequences, nextValue);
            this.cachedValue = minSequence;
            if (wrapPoint > minSequence) {
                return false;
            }
        }
        return true;
    }

这个比next的稍微简单一些,没有循环获取的操作,只会获取一次,如果没有拿到就抛异常,开发者可以通过捕获异常来进行进一步操作。

Warning

很重要的忘记说了,补上,Disruptor的版本一定要大于等于3.3.7,之前官方已经确认了3.3.6版本出现的一个可能导致死锁的问题,切记否则出了问题可能不好查。
安卓和java的Disruptor是可以设置ringBuffer数量为1的,也就是2的0次幂,官方要求ringBuffer的大小必须是2的次幂,而C++版不能设置小于2,需要更改源代码方法在我的另一篇博客里。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值