介紹
SpiderMonkey是Firefox使用的腳本引擎,V8是Google Chrome使用的腳本引擎。這篇文章介紹了怎樣在自己的C++程序中嵌入這兩種腳本引擎,以及簡單做了一些橫向的對比。
編譯
SpiderMonkey篇
SpiderMonkey支持1.0~1.8版本的JavaScript語法,包括ECMAScript,ECMA 263-3,以及Mozilla擴展,可選支持E4X
由于SpiderMonkey的makefile只支持GNU Make。為了便于編譯,建議使用MSYS,請先準備好MSYS(http://www.mingw.org)
下載SpiderMonkey源代碼(https://developer.mozilla.org/En/SpiderMonkey/1.8)
解壓...
1. 從“VC命令提示”進入控制臺,然后進入到MSYS中,這樣做的目的是為了讓MSYS能使用VC的編譯器。
2. 在MSYS里,用cd命令進入到SpiderMonkey源代碼目錄中
3. 輸入make -f makefile.ref BUILD_OPT=1
這樣就編譯完成了,還算簡單吧。
BUILD_OPT=1參數的作用是把SpiderMonkey編譯成Release版本。
另外,默認是使用VC的運行庫的(即使用-MD編譯參數),如果不喜歡,可以修改src\confg目錄下的*.mk文件(比如把-MD改成-MT)
對于MinGW用戶,請參考這里http://jargon.ca/spidermonkey/編譯
V8篇
不知道Google為什么把他們的腳本引擎稱為V8,其宣稱V8的速度遠遠超過SpiderMonkey,同樣支持支持ECMAScript、ECMA-262-3。
下載V8源代碼,需要安裝SVN,命令是svn checkout http://v8.googlecode.com/svn/trunk/ v8 或者可以在本站下載(見文章末尾)
下載后解壓...
可以在tools\visual_studio里找到工程文件,直接打開工程文件就可以編譯了。
如果編譯成功,可以無視下面這段。如果沒搞定,請靜下心來繼續...
命令行編譯方法
需要Python2.4以上版本(http://www.python.org )
安裝scons(http://www.scons.org ),這是一個python的編譯腳本軟件,可以把它看作是“高級”一點的make。
1. 進入“VC命令提示”
2. 進入V8目錄
3. 設置Python路徑 set path=c:\Python25;c:\Python25\Scripts;%path%
官方文檔說到這一步輸入scons就可以開始編譯了
不過例外總是有的,比如我的VC2005就不行,一會兒說找不到cl命令,一會兒又找不到頭文件-_-
我們得告訴它環境變量的值,這樣寫就可以了:
scons env="PATH:%path%,INCLUDE:%include%,LIB:%lib%"
默認是靜態鏈接,靜態VC庫,可以這樣修改編譯參數
scons library=shared msvcrt=shared env=...
輸入scons --help能看到更多編譯參數
想在MinGW里編譯,要到這里下個補丁才行:http://codereview.chromium.org/18309
注意:如果編譯成動態庫,使用時包含頭文件v8.h之前應該先來一句 #define USING_V8_SHARED 1
對比
偶不打算把這篇文章寫成權威的橫向評測報告(聽見有人說:切~~沒這個能力還嘴硬,-_-!!!)。只是簡單地比較一下腳本運行速度、腳本與宿主程序之間通信速度以及編程的簡易程度。我想這也是C++編程人員關心的主要問題。
先放上比較用的腳本:
腳本運行速度測試腳本
這是一個從網上找來的計算Pi的JS腳本
1. mess="";
2. //10^11 seems to be the maximum
3. //too high a figure for the base introduces errors
4. Base=Math.pow(10,11);
5. //num digits in each array item
6. cellSize=Math.floor(Math.log(Base)/Math.LN10);
7. //below is not used in this script
8. a=Number.MAX_VALUE;
9. MaxDiv=Math.floor(Math.sqrt(a));
10. function makeArray (n, aX, Integer) {
11. var i=0;
12. for (i=1; i<n; i++)
13. aX[i] = null;
14. aX[0] = Integer;
15. }
16. function isEmpty (aX) {
17. var empty=true
18. for (i=0; i<aX.length; i++)
19. if (aX[i])
20. {
21. empty= false;
22. break;
23. }
24. return empty;
25. }
26. //junior school math
27. function Add (n, aX,aY) {
28. carry=0
29. for (i=n-1; i>=0; i--) {
30. aX[i] += Number(aY[i])+Number(carry);
31. if (aX[i]<Base)
32. carry = 0;
33. else {
34. carry = 1;
35. aX[i] =Number(aX[i])- Number(Base);
36. }
37. }
38. }
39. //subtract
40. function Sub (n, aX,aY) {
41. for (i=n-1; i>=0; i--) {
42. aX[i] -= aY[i];
43. if (aX[i]<0) {
44. if (i>0) {
45. aX[i] += Base;
46. aX[i-1]--;
47. }
48. }
49. }
50. }
51. //multiply big number by "small" number
52. function Mul (n, aX, iMult) {
53. carry=0;
54. for (i=n-1; i>=0; i--) {
55. prod = (aX[i])*iMult;
56. prod += carry;
57. if (prod>=Base) {
58. carry = Math.floor(prod/Base);
59. prod -= (carry*Base);
60. }
61. else
62. carry = 0;
63. aX[i] = prod;
64. }
65. }
66. //divide big number by "small" number
67. function Div (n, aX, iDiv,aY) {
68. carry=0;
69. for (i=0; i<n; i++) {
70. //add any previous carry
71. currVal = Number(aX[i])+Number(carry*Base);
72. //divide
73. theDiv =Math.floor(currVal/iDiv);
74. //find next carry
75. carry = currVal-theDiv*iDiv;
76. //put the result of division in the current slot
77. aY[i] = theDiv;
78. }
79. }
80. //compute arctan
81. function arctan (iAng, n, aX) {
82. iAng_squared=iAng*iAng;
83. k=3; //k is the coefficient in the series 2n-1, 3,5..
84. sign=0;
85. makeArray (n, aX, 0); //aX is aArctan
86. makeArray (n, aAngle, 1);
87. Div (n, aAngle, iAng, aAngle); //aAngle = 1/iAng, eg 1/5
88. Add (n, aX, aAngle); // aX = aAngle or long angle
89. while (!isEmpty(aAngle)) {
90. Div (n, aAngle, iAng_squared, aAngle); //aAngle=aAngle/iAng_squared, iAng_squared is iAng*iAng
91. Div (n, aAngle, k, aDivK); /* aDivK = aAngle/k */
92. if (sign)
93. Add (n, aX, aDivK); /* aX = aX+aDivK */
94. else Sub (n, aX, aDivK); /* aX = aX-aDivK */
95. k+=2;
96. sign = 1-sign;
97. }
98. mess+="aArctan="+aArctan+"<br>";
99. }
100. // Calculate pi
101. function calcPI (numDec) {
102. var ans="";
103. t1=new Date();
104. numDec=Number(numDec)+5;
105. iAng=new Array(10);
106. coeff=new Array(10);
107. arrayLength=Math.ceil(1+numDec/cellSize);
108. aPI = new Array(arrayLength);
109. aArctan = new Array(arrayLength);
110. aAngle=new Array(arrayLength);
111. aDivK=new Array(arrayLength);
112. //Pi/4 = 4*arctan(1/5)-arctan(1/239)
113. //coeff is an array of the coefficients
114. //the last item is 0!
115. coeff[0] = 4;
116. coeff[1] = -1;
117. coeff[2] = 0;
118. //iAng holds the angles, 5 for 1/5, etc
119. iAng[0] = 5;
120. iAng[1] = 239;
121. iAng[2] = 0;
122. makeArray (arrayLength, aPI, 0);
123. //Machin: Pi/4 = 4*arctan(1/5)-arctan(1/239)
124. makeArray(arrayLength,aAngle,0);
125. makeArray(arrayLength,aDivK,0);
126. for (var i=0; coeff[i]!=0; i++) {
127. arctan(iAng[i], arrayLength, aArctan);
128. //multiply by coefficients of arctan
129. Mul (arrayLength, aArctan, Math.abs(coeff[i]));
130. if (coeff[i]>0)
131. Add (arrayLength, aPI, aArctan);
132. else
133. Sub (arrayLength, aPI, aArctan);
134. }
135. //we have calculated pi/4, so need to finally multiply
136. Mul (arrayLength, aPI, 4);
137. //we have now calculated PI, and need to format the answer
138. //to print it out
139. sPI="";
140. tempPI="";
141. //put the figures in the array into the string tempPI
142. for (i=0;i<aPI.length;i++)
143. {
144. aPI[i]=String(aPI[i]);
145. //ensure there are enough digits in each cell
146. //if not, pad with leading zeros
147. if (aPI[i].length<cellSize&&i!=0)
148. {
149. while (aPI[i].length<cellSize)
150. aPI[i]="0"+aPI[i];
151. }
152. tempPI+=aPI[i];
153. }
154. //now put the characters into the string sPI
155. for (i=0;i<=numDec;i++)
156. {
157. //put the 3 on a different line, and add a decimal point
158. if (i==0)
159. sPI+=tempPI.charAt(i)+". ";
160. else
161. {
162. if ((i)%50==0&&i!=0)
163. sPI+=tempPI.charAt(i)+" ";
164. else
165. sPI+=tempPI.charAt(i);
166. }//i not zero
167. }
168. //now put the print-out together
169. //print our pi
170. ans+=("PI ("+numDec+")="+sPI+" ");
171. //Window's calculator Pi (for confirmation);
172. ans+=("Win PI= 3.1415926535897932384626433832795 ");
173. t2=new Date();
174. timeTaken=(t2.getTime()-t1.getTime())/1000;
175. ans+="It took: "+timeTaken+" seconds";
176. return ans;
177.
178. }
179.
180. myprint(calcPI(1000));
腳本與宿主程序之間通信速度測試腳本
1. // MyClass是宿主提供給JS的類(一個queue的直接包裝)
2. var c = new MyClass();
3. t1=new Date();
4.
5. //10萬次push和pop,測試JS調用宿主代碼的速度
6. for(i=0; i<100000; i++)
7. {
8. c.push(i);
9. c.pop();
10. }
11. t2=new Date();
12. timeTaken=(t2.getTime()-t1.getTime())/1000;
13. //myprint也是宿主提供的全局函數,用于顯示結果
14. myprint("It took:",timeTaken,"seconds");
接下來我們開始編寫代碼,利用SpiderMonkey和V8引擎分別實現出能夠執行上述腳本的程序,看看哪個速度更快吧~~
下載