手机腾讯网mt2.0目前已经应用在线上案例,在使用的过程中,为了提高增量更新的效率,我们使用编辑距离算法来替代原来的chunk算法,在这个过程中碰到了一个性能问题,我们这里写一下优化方法。
问题:
编辑距离计算需要用一个矩阵来存放2新旧2个版本的字符,这在js文件较少的情况下ok,但是实际情况下很多js(尤其是多个js合并为一个js)的字符都是几万字符的,如果用java定义一个30000*30000的矩阵,基本上就内存溢出了。。。
解决办法:
mixDiff:用chunk算法来计算出最长公共字符串,然后用公共字符串将要比较的两个字符串切成
preString+commString+nextString三个块,其中的commString可以用位置数字表示
然后判断preString和nextString的长度是否短于某个设定值,如果短于则走编辑距离算法,否则继续递递归调用mixDiff.
这样最终得到的增量文件的效果是跟单纯调用编辑距离算法的效果一样的,同时也解决了性能问题
流程图如下:
程序伪码:
啥也不说了上代码:
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);
}
}
}
MT是手机腾讯网前端团队开发维护的一个专注于移动端的、带有增增量更新特色的js模块管理框架
我们的官网是http://mt.tencent.com,https://mtjs.github.io
我们的github:https://github.com/mtjs/mt,如果觉得MT是个靠谱的项目,请给我们star,您的支持是我们最大的动力