手机腾讯网mt2.0目前已经应用在线上案例,在使用的过程当中,为了提升增量更新的效率,咱们使用编辑距离算法来替代原来的chunk算法,在这个过程当中碰到了一个性能问题,咱们这里写一下优化方法。html
问题: 编辑距离计算须要用一个矩阵来存放2新旧2个版本的字符,这在js文件较少的状况下ok,可是实际状况下不少js(尤为是多个js合并为一个js)的字符都是几万字符的,若是用java定义一个30000*30000的矩阵,基本上就内存溢出了。。。前端
解决办法: mixDiff:用chunk算法来计算出最长公共字符串,而后用公共字符串将要比较的两个字符串切成 preString+commString+nextString三个块,其中的commString能够用位置数字表示 而后判断preString和nextString的长度是否短于某个设定值,若是短于则走编辑距离算法,不然继续递递归调用mixDiff. 这样最终获得的增量文件的效果是跟单纯调用编辑距离算法的效果同样的,同时也解决了性能问题 流程图以下: java
程序伪码: nginx
啥也不说了上代码:git
<!-- lang: java --> public class MixDiff { class LcsItem { public String srcPre; public String tarPre; public JSONArray lcsPos; public String srcNext; public String tarNext; @Override public String toString() { return "LcsItem [srcPre=" + srcPre + ", tarPre=" + tarPre + ", lcsPos=" + lcsPos.toJSONString() + ", srcNext=" + srcNext + ", tarNext=" + tarNext + "]"; } } public MixDiff() { // TODO Auto-generated constructor stub } /** * 编辑距离算法 * @param source * @param target * @return */ public JSONArray getDiffEncode(int start,String oldContentStr,String newContentStr){ JSONArray jsonArray=lcsDiff(oldContentStr,newContentStr); //System.out.println("diffencode"+jsonArray); for(int i=0;i<jsonArray.size();i++){ Object jObj=jsonArray.get(i); if(jObj instanceof JSONArray ){ JSONArray jsonObj=(JSONArray)jObj; jsonObj.set(0, (jsonObj.getIntValue(0)+start)); } } if(jsonArray.size()==0){ JSONArray tempArray=new JSONArray(); tempArray.add(start+1); tempArray.add(oldContentStr.length()); jsonArray.add(tempArray); } return jsonArray; } public JSONArray lcsDiff(String source,String target){ LcsDiff lcsDiff=new LcsDiff(); //System.out.println("lcsdiff: "+source+" ||||| "+target ); return lcsDiff.diff(source, target).getJSONArray("data"); } /** * * @param source * @param target * @param chunkSize * @return */ public JSONArray chunkDiff(String source,String target,int chunkSize){ ChunkDiff chunkUtil = new ChunkDiff(); JSONObject json=chunkUtil.makeIncDataFile(source, target, chunkSize); return json.getJSONArray("data"); } public LcsItem getLcsStrByChunk(int initStart,String source,String target,int minLen){ JSONArray dataArray=chunkDiff(source,target,12); LcsItem lcsStrItem=new LcsItem(); JSONArray lcsPosInit=new JSONArray(); lcsPosInit.add(-1); lcsPosInit.add(-1); lcsStrItem.lcsPos=lcsPosInit; int maxLen=0; for(int i=0;i<dataArray.size();i++){ Object jObj=dataArray.get(i); if(jObj instanceof JSONArray ){ JSONArray jsonObj=(JSONArray)jObj; int len=jsonObj.getIntValue(1)*12; int start=jsonObj.getIntValue(0)*12; int end=start+len; if(len>=minLen&&len>maxLen){ JSONArray lcsPos=new JSONArray(); lcsPos.add(start+1+initStart); lcsPos.add(len); String lcsStr=source.substring(start, end); lcsStrItem.srcPre=source.substring(0,start); lcsStrItem.srcNext=source.substring(end,source.length()); lcsStrItem.lcsPos=lcsPos; //System.out.println(lcsStr); int tarStart=target.indexOf(lcsStr); int tarEnd=tarStart+lcsStr.length(); lcsStrItem.tarPre=target.substring(0,tarStart); lcsStrItem.tarNext=target.substring(tarEnd,target.length()); maxLen=len; } } } return lcsStrItem; } public JSONArray mixDiff(int start,String source,String target,int lcsMaxLen){ int minLen=12; int sourceLen=source.length(); int targetLen=target.length(); JSONArray reArray=new JSONArray(); //若是是 if((sourceLen*targetLen<lcsMaxLen*lcsMaxLen)&&(sourceLen*targetLen)>0){ return getDiffEncode(start,source,target); } LcsItem lcsStrItem=getLcsStrByChunk(start,source, target, minLen); //System.out.println("lcs::::"+lcsStrItem); if(lcsStrItem.lcsPos.getIntValue(0)==-1){ return getDiffEncode(start,source,target); } else{ JSONArray preArray=mixDiff(start,lcsStrItem.srcPre,lcsStrItem.tarPre,lcsMaxLen); addMerge(reArray, preArray); JSONArray midArray=new JSONArray(); midArray.add(lcsStrItem.lcsPos); addMerge(reArray,midArray); int nextStart=lcsStrItem.lcsPos.getIntValue(0)+lcsStrItem.lcsPos.getIntValue(1)-1 ; JSONArray nextArray=mixDiff(nextStart,lcsStrItem.srcNext,lcsStrItem.tarNext,lcsMaxLen); addMerge(reArray, nextArray); } return reArray; } public String merge(String oldContent,JSONObject incData){ String reContent=""; JSONArray dataArray=incData.getJSONArray("data"); for(int i=0;i<dataArray.size();i++){ Object jObj=dataArray.get(i); if(jObj instanceof JSONArray){ JSONArray jsonObj=(JSONArray)jObj; int start=jsonObj.getIntValue(0)-1; int len=jsonObj.getIntValue(1); //System.out.println("merge lcs:"+oldContent.substring(start,start+len)); reContent+=oldContent.substring(start,start+len); } else{ reContent+=jObj.toString(); } } return reContent; } public void addMerge(JSONArray strDataArray,JSONArray addArry){ if(strDataArray.size()==0){ strDataArray.addAll(addArry); return; } Object jObj=strDataArray.get(strDataArray.size()-1); Object addObj=addArry.get(0); if((jObj instanceof JSONArray )&&(addObj instanceof JSONArray )){ JSONArray jsonObj=(JSONArray)jObj; JSONArray addArrayObj=(JSONArray)addObj; if(jsonObj.getIntValue(0)+jsonObj.getIntValue(1)==addArrayObj.getIntValue(0)){ jsonObj.set(1, (jsonObj.getIntValue(1)+addArrayObj.getIntValue(1))); strDataArray.addAll(addArry.subList(1, addArry.size())); } else{ strDataArray.addAll(addArry); } } else{ strDataArray.addAll(addArry); } } public String readFile(String file, String encode) { File a = new File(file); StringBuffer strBuffer = new StringBuffer(""); ; if (a.exists()) { try { FileInputStream fi = new FileInputStream(a); InputStreamReader isr = new InputStreamReader(fi, "utf-8"); BufferedReader bfin = new BufferedReader(isr); String rLine = ""; while ((rLine = bfin.readLine()) != null) { strBuffer.append(rLine); } bfin.close(); } catch (Exception ex) { } } return strBuffer.toString(); } private String MD5(String s) { char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; try { byte[] btInput = s.getBytes(); MessageDigest mdInst = MessageDigest.getInstance("MD5"); mdInst.update(btInput); byte[] md = mdInst.digest(); int j = md.length; char str[] = new char[j * 2]; int k = 0; for (int i = 0; i < j; i++) { byte byte0 = md[i]; str[k++] = hexDigits[byte0 >>> 4 & 0xf]; str[k++] = hexDigits[byte0 & 0xf]; } return new String(str); } catch (Exception e) { e.printStackTrace(); return null; } } public JSONObject makeIncDataFromContent(String source,String target){ JSONObject resultFile = new JSONObject(); resultFile.put("modify", true); resultFile.put("chunkSize",12); resultFile.put("diffAlg", "lcs"); JSONArray strDataArray = new JSONArray(); if (MD5(source).equals(MD5(target))) { resultFile.put("modify", false); resultFile.put("data", strDataArray); return resultFile; } JSONArray jArray=mixDiff(0,source, target,12); resultFile.put("data", jArray); return resultFile; } public JSONObject makeIncDataFromFile(String oldFile, String newFile ) { String oldContent = readFile(oldFile, "utf-8"); String newContent = readFile(newFile, "utf-8"); return makeIncDataFromContent(oldContent,newContent); } /** * @param args */ public static void main(String[] args) { String src="define('init',['util','p1'],function(){console.log('dafds init depend on uil p1 ok!'),document.write('init depend on util p2 ok!</br>')}),define('util',[],function(){console.log('ut ok!'),document.write('util ok!</br>')});sadfafds"; String target="sdf define('init',['util','p1'],function(){console.log(' int depnd on util sdfs p1 ok 49!'),document.write('init depend on 34 util p2 ok!</br>')}),define('util',[],function(){console.log('util ok!'),document.write('il ok!</br>')});csadf"; MixDiff dUtil = new MixDiff(); JSONObject json = dUtil .makeIncDataFromFile( "/Users/waynelu/nginxhtmls/jetty/webapps/mtwebapp///release/2014071500017///base-2014071500017.js", "/Users/waynelu/nginxhtmls/jetty/webapps/mtwebapp///release/2014071600018///base-2014071600018.js"); JSONObject json1 = dUtil.makeIncDataFromContent(src,target); System.out.println(json.toJSONString()); String mergeContent=dUtil.merge(src,json1); System.out.println(target); System.out.println(mergeContent); if(target.equals(mergeContent)){ System.out.println(true); } else{ System.out.println(false); } }
}github
MT是手机腾讯网前端团队开发维护的一个专一于移动端的、带有增增量更新特点的js模块管理框架 咱们的官网是http://mt.tencent.com,https://mtjs.github.io 咱们的github:https://github.com/mtjs/mt,若是以为MT是个靠谱的项目,请给咱们star,您的支持是咱们最大的动力web