找回密码
 立即注册

QQ登录

只需一步,快速开始

KevinChen 讲师达人认证 悬赏达人认证 SpreadJS 开发认证
论坛元老   /  发表于:2021-2-28 02:39  /   查看:2540  /  回复:3
本帖最后由 KevinChen 于 2021-3-4 16:54 编辑

本专题上一篇博客介绍了怎样在nodejs环境中搭建SpreadJS的运行环境,本篇博客是此系列的第二篇,重点在于对比GcExcel for Java和Node.js中运行SpreadJS的性能比较。由于SpreadJS和GcExcel的功能非常丰富,本文仅选择最为常见的两个功能点做对比,分别是设置区域数据,并导出Excel文档。对 GcExcel 不熟悉的同学,可以先看一下上边链接的官网主页,如果用过Apache POI的小伙伴,看一下这张图就知道,相对于 SpreadJS with Node.js,GcExcel的确是个强大的对手: image.png933774413.png

传送门:
专题:服务端Node.js运行SpreadJS处理Excel的方案探讨(一)



本次测试的几个大前提:
首先,众所周知,Node.js是基于V8引擎来执行JavaScript的,因此它的js也是基于事件机制的非阻塞单线程运行,文件的I/O都是异步执行的。单线程的好处在于编码简单,开发难度低,心智消耗相对较小(对咱们码农比较友好);而且它的文件的I/O是异步执行的,不像Java这种需要创建、回收线程(Node.js的IO操作在底层也是线程,这里不做深入讨论),这方面开销较小。
但是,相对的,单线程在做复杂运算方面相比多线程语言,没有优势,无法利用多线程来有效调配多核CPU进行优化;对于Node.js来说这个问题并非无解,但咱们这个专题讨论的大前提是基于SpreadJS的,这方面我们并没有提供针对Node.js计算的优化包(这跟SpreadJS产品定位实在是相去甚远,且我们也有了更好的解决方案GcExcel),因此在Node.js中运行SpreadJS就只能是单线程JS。
因此,基于以上前提,为了“公平”起见,本篇中设计的测试案例,在两个环境(Java 和 Node.js)中,都采用单线程执行,并且选择了对Node.js更加有优势的批量I/O操作,看看在性能上能否有得一战。


GCExcel 测试代码和结果:
代码非常简单,一个Performance类中执行了1000次设置数据、导出Excel文档的操作,如下所示:
  1. public class Performance {

  2.         public static void main(String[] args) {
  3.                 System.out.println(System.getProperty("user.dir") + "/sources/jsonData");
  4.                 String jsonStr = readTxtFileIntoStringArrList(System.getProperty("user.dir") + "/sources/jsonData");
  5.                 JSONArray jsonArr = JSON.parseArray(jsonStr);
  6.                 //JSONObject jsonObj = (JSONObject) jsonArr.get(0);
  7.                 //System.out.println(jsonObj.get("Film"));
  8.                 run(1000, jsonArr);
  9.         }

  10.         public static void run(int times, JSONArray dataArr) {
  11.                 String path = System.getProperty("user.dir") + "/results/";
  12.                 System.out.println(path + "result.xlsx");
  13.                 long start = new Date().getTime();
  14.                 for (int i = 0; i < times; i++) {
  15.                         Workbook workbook = new Workbook();
  16.                         IWorksheet worksheet = workbook.getWorksheets().get(0);
  17.                         for (int j = 0; j < dataArr.size(); j++) {
  18.                                 JSONObject jsonObj = (JSONObject) dataArr.get(j);
  19.                                 worksheet.getRange(j, 0, 1, 8).get(0).setValue(jsonObj.get("Film"));
  20.                                 worksheet.getRange(j, 0, 1, 8).get(1).setValue(jsonObj.get("Genre"));
  21.                                 worksheet.getRange(j, 0, 1, 8).get(2).setValue(jsonObj.get("Lead Studio"));
  22.                                 worksheet.getRange(j, 0, 1, 8).get(3).setValue(jsonObj.get("Audience Score %"));
  23.                                 worksheet.getRange(j, 0, 1, 8).get(4).setValue(jsonObj.get("Profitability"));
  24.                                 worksheet.getRange(j, 0, 1, 8).get(5).setValue(jsonObj.get("Rating"));
  25.                                 worksheet.getRange(j, 0, 1, 8).get(6).setValue(jsonObj.get("Worldwide Gross"));
  26.                                 worksheet.getRange(j, 0, 1, 8).get(7).setValue(jsonObj.get("Year"));
  27.                         }
  28.                         workbook.save(path + "result" + i + ".xlsx");
  29.                 }
  30.                 System.out.println("运行"+times+"次花费时常(ms): " + (new Date().getTime() - start));

  31.         }

  32.         public static String readTxtFileIntoStringArrList(String filePath) {
  33.                 StringBuilder list = new StringBuilder();
  34.                 try {
  35.                         String encoding = "GBK";
  36.                         File file = new File(filePath);
  37.                         if (file.isFile() && file.exists()) {
  38.                                 InputStreamReader read = new InputStreamReader(new FileInputStream(file), encoding);// 考虑到编码格式
  39.                                 BufferedReader bufferedReader = new BufferedReader(read);
  40.                                 String lineTxt = null;

  41.                                 while ((lineTxt = bufferedReader.readLine()) != null) {
  42.                                         list.append(lineTxt);
  43.                                 }
  44.                                 bufferedReader.close();
  45.                                 read.close();
  46.                         } else {
  47.                                 System.out.println("找不到指定的文件");
  48.                         }
  49.                 } catch (Exception e) {
  50.                         System.out.println("读取文件内容出错");
  51.                         e.printStackTrace();
  52.                 }
  53.                 return list.toString();
  54.         }

  55. }
复制代码
完整的工程zip请参考附件:GcExcelPerformanceSample.zip
运行方式:导入Eclipse后直接run as Application
运行结果:
image.png65560606.png

Node.js 与 SpreadJS的测试代码和结果:
同样,代码没什么好讲的,如果有问题,走传送门回到第一篇复习一下~
  1. const fs = require('fs');

  2. // Initialize the mock browser variables
  3. const mockBrowser = require('mock-browser').mocks.MockBrowser;
  4. global.window = mockBrowser.createWindow();
  5. global.document = window.document;
  6. global.navigator = window.navigator;
  7. global.HTMLCollection = window.HTMLCollection;
  8. global.getComputedStyle = window.getComputedStyle;

  9. const fileReader = require('filereader');
  10. global.FileReader = fileReader;

  11. const GC = require('@grapecity/spread-sheets');
  12. const GCExcel = require('@grapecity/spread-excelio');

  13. GC.Spread.Sheets.LicenseKey = GCExcel.LicenseKey = "Your License";

  14. const dataSource = require('./data');

  15. function runPerformance(times) {

  16.   const timer = `test in ${times} times`;
  17.   console.time(timer);

  18.   for(let t=0; t<times; t++) {
  19.     // const hostDiv = document.createElement('div');
  20.     // hostDiv.id = 'ss';
  21.     // document.body.appendChild(hostDiv);
  22.     const wb = new GC.Spread.Sheets.Workbook()//global.document.getElementById('ss'));
  23.     const sheet = wb.getSheet(0);
  24.     for(let i=0; i<dataSource.length; i++) {
  25.       sheet.setValue(i, 0, dataSource[i]["Film"]);
  26.       sheet.setValue(i, 1, dataSource[i]["Genre"]);
  27.       sheet.setValue(i, 2, dataSource[i]["Lead Studio"]);
  28.       sheet.setValue(i, 3, dataSource[i]["Audience Score %"]);
  29.       sheet.setValue(i, 4, dataSource[i]["Profitability"]);
  30.       sheet.setValue(i, 5, dataSource[i]["Rating"]);
  31.       sheet.setValue(i, 6, dataSource[i]["Worldwide Gross"]);
  32.       sheet.setValue(i, 7, dataSource[i]["Year"]);
  33.     }
  34.     exportExcelFile(wb, times, t);
  35.   }
  36.   
  37. }

  38. function exportExcelFile(wb, times, t) {
  39.     const excelIO = new GCExcel.IO();
  40.     excelIO.save(wb.toJSON(), (data) => {
  41.         fs.appendFile('results/Invoice' + new Date().valueOf() + '_' + t + '.xlsx', new Buffer(data), function (err) {
  42.           if (err) {
  43.             console.log(err);
  44.           }else {
  45.             if(t === times-1) {
  46.               console.log('Export success');
  47.               console.timeEnd(`test in ${times} times`);
  48.             }
  49.           }
  50.         });
  51.     }, (err) => {
  52.         console.log(err);
  53.     }, { useArrayBuffer: true });
  54. }

  55. runPerformance(1000)
复制代码
完整的Demo工程请参考附件:spreadjs-nodejs-performance.zip
运行方式:
npm install
node ./app.js
运行结果:
image.png90727538.png
本机配置:i7-9750H & 32G

总结:
1、从性能上分析:
SpreadJS in Node.js在“擅长”的批量I/O方面也输给了 GcExcel for Java,一方面由于GcExcel性能确实非常优异,它在Java平台上运用了很多优秀成熟的解决方案,做到了同类产品中最一流的性能表现,同时在对Excel和SpreadJS两方面的功能支持上也十分全面,是我们在服务器端处理Excel文档的首选方案;
2、从优化编码难度上分析:
另一方面,Node.js中采用了V8引擎,可以认为是目前针对JavaScript性能最好的引擎,但语言平台的硬伤,也确实很难弥补。这里所说的语言层面的瓶颈,并非是指JS不可能达到Java的性能表现,事实上如果你是V8引擎的核心开发者,完全有可能用JavaScript写出超过普通Java程序员的代码的性能,这里有一篇Vyacheslav Egorov大神用JS代码吊打Rust编译的WASM模块的文章。而是指要达到某个较高的性能指标,优化的难度和成本相比较而言,还是Java更低一些,从现实出发,毕竟不是每个程序员都能达到V8引擎核心开发者Vyacheslav Egorov的水平,而且我们还得分出大量的精力关注业务实现。
3、从技术选型上分析:
当然,本系列没有提到的一个对技术选型有决定性的地方就在于Node.js和Java/.Net平台还是有较大区别,如果项目本身采用的是Java Web或.Net Web架构,那选择GcExcel也是顺理成章的事情。另外认真阅读过第一篇的同学应该也注意到,Node.js中运行SpreadJS需要依赖诸如mock-browser / jsdom等第三方的组件,这对生产环境来说都是不可控的风险因素。

3 个回复

倒序浏览
KevinChen讲师达人认证 悬赏达人认证 SpreadJS 开发认证
论坛元老   /  发表于:2021-2-28 02:42:13
沙发
完整的Demo请参考附件。

GcExcelPerformanceSample.zip

11.68 KB, 下载次数: 37

spreadjs-nodejs-performance.zip

4.04 KB, 下载次数: 38

回复 使用道具 举报
wuba_lzy
金牌服务用户   /  发表于:2022-3-4 14:26:22
板凳
node示例跑不通呢
我们有个用node 跑spjs的需求 这块还挺关心的

image.png87706297.png
回复 使用道具 举报
Derrick.Jiao讲师达人认证 悬赏达人认证 SpreadJS 开发认证
论坛元老   /  发表于:2022-3-4 14:56:30
地板
wuba_lzy 发表于 2022-3-4 14:26
node示例跑不通呢
我们有个用node 跑spjs的需求 这块还挺关心的

你好,因为SpreadJS是个纯前端表格控件,很多绘制以及计算是需要依赖canvas等实现,所以在node.jsz中存在一些限制,因此我们是不建议在node.js中使用SpreadJS的。如果在node中出现了问题,我们这边可能也无法进行支持,本篇文章只是使用上的一个探讨。如果想要在后端使用,我们有GcExcel这样一款后端的组件,可以到我们官网了解一下
https://www.grapecity.com.cn/dev ... ocuments/excel-java
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 立即注册
返回顶部