手機騰訊網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