当前位置:Gxlcms > 数据库问题 > Java读取Level-1行情dbf文件极致优化(3)

Java读取Level-1行情dbf文件极致优化(3)

时间:2021-07-01 10:21:17 帮助过:3人阅读

long get_long_efficiently_and_multiply_1000(byte[] src, final int index) { long multiplicand = 3; long result =0; Field field = getFields()[index]; boolean in_decimal_part = false; boolean negative = false; int offset = field.getOffset(); int length = field.getLength(); int end = offset+length; for(int i =field.getOffset(); i< end; i++) { byte ch = src[i]; if(ch>=48 && ch<=57) //如果是数字 { result *= 10; result += ch-48; if(in_decimal_part) multiplicand--; if(multiplicand==0) break; continue; } if(ch==32) //如果是空格 continue; if(ch == 46) //如果是小数点 { in_decimal_part = true; continue; } if(ch == ‘-‘) //如果是负号 { negative = true; } throw new NumberFormatException(); } if(multiplicand == 3) result *= 1000; else if (multiplicand == 2) result *=100; else if (multiplicand == 1) result *=10; if(negative) { result= 0 - result; } return result; }

 

上面的算法负责读取字段转换为数字的同时,对它乘以1000。并且代码中尽量优化了执行步骤。

 

对于整形的读取,我们也进行了优化,添加一个get_long_efficiently:

public long get_long_efficiently(byte[] src, final int index)
    {
        long result =0;
        boolean negative = false;
        Field field = getFields()[index];
        for(int i =field.getOffset(); i< field.getOffset()+ field.getLength(); i++)
        {
            byte ch = src[i];
            if(ch>=48 && ch<=57) //如果是数字
            {
                result = result*10 + (src[i]-48);
                continue;
            }
            
            if(src[i]==32) //如果是空格
                continue;
            
            if(ch == ‘-‘) //如果是负号
            {
                negative = true;
            }
            
            throw new NumberFormatException();
        }
        
        if(negative)
        {
            result= 0 - result;
        }
        
        return result;
    }

 

以上的2个算法并不复杂,但却非常关键,一个dbf文件包含大约5000行,每行包括20~30个Float类型或者Int类型的字段,该优化涉及10万+个字段的读取。测试下来,这步改进将读取速度从50ms-70ms提升至15ms至20ms,细节在魔鬼当中,这是速度提升最快的一项优化。

(优化五的代码在改进的DBFReader中,上午中已经提供下载,这里再提供下载链接:技术分享DBFReader库 )

 

优化六:线程池并行处理

对5000多个行进行字段读取并转换成对象,采用多线程处理是最自然不过的优化方式。

 

一般我们采用的方法是把任务分成等份的块,每个线程处理一大块。比如,如果采用5个线程处理,那么把5000行分成1000个行一块,每个线程处理一块。这样看貌似公平,其实不然,因为我们的操作系统是分时操作系统,每个线程开始工作的时间,占用的CPU时间片,和任务的强度都不完全一致。等分的办法貌似平均,但是很有可能导致有些线程完成工作了,另外一些还有很多没做完。

 

这里介绍一种我喜欢的任务分配方式:每个线程每次从5000个行的任务中申请一小块,比如16个行,完成后,再申请16个行。这样快的线程就会多工作些,慢的就少工作些,直到所有的行处理完毕。那么,这些线程怎么协调呢,任务分配岂不是要用到锁?不用锁,我们采用CAS机制就能做到(实际用的是AtomicInteger,AtomicInteger就是基于CAS实现的),这里不解释太多了。看代码:

class ReaderTask implements Runnable {
        Collector collector;
        List<byte[]> recordList;
        CountDownLatch countDownLatch;
        AtomicInteger cursor;
        DBFReader reader;

        public ReaderTask(Collector collector, DBFReader dbfreader, List<byte[]> recordList, AtomicInteger cursor,
                CountDownLatch countDownLatch) {
            this.collector = collector;
            this.reader = dbfreader;
            this.recordList = recordList;
            this.cursor = cursor;
            this.countDownLatch = countDownLatch;
        }

        @Override
        public void run() {
            try {
                int length = recordList.size();
                do {
                    final int step = 16; //每次分配16行给该线程处理。
                    int endIndex = cursor.addAndGet(step);
                    int startIndex = endIndex - step ;

                    for (int i = startIndex; i < endIndex && i < length; i++) {
                        byte[] row = recordList.get(i);
                        MarketRealtimeData SHData = new MarketRealtimeData();
                        SHData.setMarketType(Constants.MARKET_SH_STOCK);
                        SHData.setIdNum(reader.get_string_efficiently(row, 0));
                        SHData.setPrefix(reader.get_string_efficiently(row, 1));
                        SHData.setPreClosePrice(reader.get_long_efficiently_and_multiply_1000(row, 2));
                        SHData.setOpenPrice(reader.get_long_efficiently_and_multiply_1000(row, 3));
                        SHData.setTurnover(reader.get_long_efficiently_and_multiply_1000(row, 4));
                        SHData.setHighPrice(reader.get_long_efficiently_and_multiply_1000(row, 5));
                        SHData.setLowPrice(reader.get_long_efficiently_and_multiply_1000(row, 6));
                        SHData.setMatchPrice(reader.get_long_efficiently_and_multiply_1000(row, 7));
                        //读取所有的Field,以下省略若干行
                        //... ...
                        //... ...

                        if (collector != null) {
                            collector.collect(SHData);
                        }
                    }
                } while (cursor.get() < length);
            } finally {
                if (countDownLatch != null)
                    countDownLatch.countDown();
            }
        }
    }

 

private void readHangqingFile(String path, String name) throws Exception {
            // Long t1 = System.nanoTime();
            DBFReader dbfreader_SH = null;
            try {
                dbfreader_SH = new DBFReader(new File(path+File.separator + name));
                List<byte[]> list_sh = dbfreader_SH.recordsWithOutDel_efficiently(cacheManager);
                
                AtomicInteger cursor = new AtomicInteger(0); //原子变量,用于线程间分配任务
                CountDownLatch countDownLatch = new CountDownLatch(WORK_THREAD_COUNT);

                for (int i = 0; i < WORK_THREAD_COUNT - 1; i++) { //把任务分配给线程池多个线程
                    ReaderTask task = new ReaderTask(collector, dbfreader_SH, list_sh, cursor, countDownLatch);
                    globalExecutor.execute(task);
                }
                new ReaderTask(collector, dbfreader_SH, list_sh, cursor, countDownLatch).run(); //当前线程自己也作为工作线程
                countDownLatch.await();
                //Long t2 = System.nanoTime();
                //System.out.println("speed time on read and object:" + (t2 - t1));

            } finally {
                if (dbfreader_SH != null)
                    dbfreader_SH.close();
            }
        }

 

测试表明,在使用4个线程并行处理的情况下,处理时间从15ms-20ms缩短至4ms-7ms。

 

在使用本文章介绍的所有优化方法,整个读取效率从耗时300ms以上,优化至5ms-10ms之间。我们讨论的是从文件更新始,到完成文件读取,完成5000多个对象,100,000个字段的转换的总耗时。

 

如果继续深入,我们可能还有不少细节可以改进。测试表明,时延的稳定性还不够好,很可能是由于GC造成的,我们还可以从减少对象的创建,以减少性能损耗,减少GC;并且控制GC执行的时间,让GC在空闲时执行等方面优化。

 

Binhua Liu原创文章,转载请注明原地址http://www.cnblogs.com/Binhua-Liu/p/5616761.html

Java读取Level-1行情dbf文件极致优化(3)

标签:

人气教程排行