`

如何利用多核提升分词速度

阅读更多

在进行中文分词的时候,我们如何利用多核提升分词速度呢?

计算机很早就进入多核心时代了,不充分利用多核CPU是对计算资源的一种极大的浪费。

在对一段文本进行分词的时候,word分词器的处理步骤如下:

1、把要分词的文本根据标点符号分割成句子;

2、以分割后的句子为基本单位进行分词;

3、把各个句子的分词结果按原来的句子顺序组合起来;

word分词器充分考虑到了利用多核提升分词速度这个问题,在第1步完成后,如果分割出了多个句子,那么这多个句子就可以同时(并行)进行分词,这样就能充分利用多核CPU来提升分词速度。

word分词器提出了两种解决方案,我们分别来介绍:

1、多线程

在Java中,多线程可以帮助我们充分利用CPU,我们可以根据自己的机器情况及其应用特点在配置文件word.conf中指定合适的线程池大小:

#配置分词使用的固定线程池大小,根据机器的情况设置合适的值
thread.pool.size=4

 

代码实现核心片段如下:

private static final ExecutorService EXECUTOR_SERVICE = Executors.newFixedThreadPool(WordConfTools.getInt("thread.pool.size", 4));
@Override
public List<Word> seg(String text) {
    List<String> sentences = Punctuation.seg(text, KEEP_PUNCTUATION);
    if(sentences.size() == 1){
        return segSentence(sentences.get(0));
    }
    //如果是多个句子,可以利用多线程提升分词速度
    List<Future<List<Word>>> futures = new ArrayList<>(sentences.size());
    for(String sentence : sentences){
        futures.add(submit(sentence));
    }
    sentences.clear();
    List<Word> result = new ArrayList<>();
    for(Future<List<Word>> future : futures){
        List<Word> words;
        try {
            words = future.get();
            if(words != null){
                result.addAll(words);
            }
        } catch (InterruptedException | ExecutionException ex) {
            LOGGER.error("获取分词结果失败", ex);
        }
    }
    futures.clear();
    return result;
}
/**
 * 将切分句子的任务提交给线程池来运行
 * @param sentence 句子
 * @return 切分结果
 */
private Future<List<Word>> submit(final String sentence){
    return EXECUTOR_SERVICE.submit(new Callable<List<Word>>(){
        @Override
        public List<Word> call() {
            return segSentence(sentence);
        }
    });
}

 

2、Parallel Stream

Java8 support functional-style operations on streams of elements,if we use "parallelStream()" instead of "stream()" , the serial process can automatic change to parallel process.

Java8的内置的并行处理功能,通过上面的简短的介绍我们应该有所了解,使用这种方案的好处是,随着JDK的改进,程序的性能会间接受益,而且我们的代码可以更简单,虽然我们不再能够控制并行度,比如上面我们能指定线程数,但是我们可以放心JRE会做出合理的调度与优化,而且人为指定线程数很多时候都不是最合理的。

我们可以在配置文件word.conf中指定是否启用并行分词:

#是否利用多核提升分词速度
parallel.seg=true

 

上面多线程的代码可以简化为:

private static final boolean PARALLEL_SEG = WordConfTools.getBoolean("parallel.seg", true);
@Override
public List<Word> seg(String text) {
    List<String> sentences = Punctuation.seg(text, KEEP_PUNCTUATION);
    if(sentences.size() == 1){
        return segSentence(sentences.get(0));
    }
    if(!PARALLEL_SEG){
        //串行顺序处理,不能利用多核优势
        return sentences.stream().flatMap(sentence->segSentence(sentence).stream()).collect(Collectors.toList());
    }
    //如果是多个句子,可以利用多核提升分词速度
    Map<Integer, String> sentenceMap = new HashMap<>();
    int len = sentences.size();
    for(int i=0; i<len; i++){
        //记住句子的先后顺序,因为后面的parallelStream方法不保证顺序
        sentenceMap.put(i, sentences.get(i));
    }
    //用数组收集句子分词结果
    List<Word>[] results = new List[sentences.size()];
    //使用Java8中内置的并行处理机制
    sentenceMap.entrySet().parallelStream().forEach(entry -> {
        int index = entry.getKey();
        String sentence = entry.getValue();
        results[index] = segSentence(sentence);
    });
    sentences.clear();
    sentences = null;
    sentenceMap.clear();
    sentenceMap = null;
    List<Word> resultList = new ArrayList<>();
    for(List<Word> result : results){
        resultList.addAll(result);
    }
    return resultList;
}

 

如果对更多的细节感兴趣,请点击这里查看代码的源文件

 

 

 

 

 

 

 

 

3
1
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics