這是一段用C++寫的計算十萬以內(nèi)的回文素數(shù)算法。
#include <iostream>
using namespace std;
int main()
{
int input_num=100000;
int pp_count=0;
for(int each=2; each<=input_num; each++)
{
int factorization_lst=0;
for(int factor=1; factor<=each; factor++)
if(each%factor==0&&!(factor>each/factor))
factorization_lst++;
if(factorization_lst==1)
{
int antitone=0,each_cpy=each;
while(each_cpy)
{
antitone=antitone*10+each_cpy%10;
each_cpy/=10;
}
if(antitone==each)
{
pp_count++;
cout<<pp_count<<':'<<each<<endl;
}
}
}
return 0;
}
稍微做一下修改的Java版,加了計時相關的部分。
public class main {
public static void main(String[] args) {
long start = System.currentTimeMillis();
int input_num = 100000;
int pp_count = 0;
for (int each = 2; each <= input_num; each++) {
int factorization_lst = 0;
for (int factor = 1; factor <= each; factor++)
if (each % factor == 0 && !(factor > each / factor))
factorization_lst++;
if (factorization_lst == 1) {
int antitone = 0, each_cpy = each;
while (each_cpy != 0) {
antitone = antitone * 10 + each_cpy % 10;
each_cpy /= 10;
}
if (antitone == each) {
pp_count++;
System.out.println(pp_count + ":" + each);
}
}
}
System.out.println(System.currentTimeMillis() - start);
}
}
執(zhí)行結果:
同樣的算法,C++用了230s,Java只用了124s。這是為什么呢,不是說C++的速度更快嗎?
注:運行環(huán)境是樹莓派3B的官方raspbian(在我的筆記本上運行過,但僅相差一秒不明顯,java17s),C++和Java分別用的默認倉庫的codeblocks和eclipse(都不是最新版本,eclipse的版本是2012年的3.8.1,codeblocks是2016年的16.01),gcc已經(jīng)默認開啟了-O2優(yōu)化選項,但還是如此相差懸殊。已經(jīng)看過類似于這樣的解釋文章。但還是不太明白。我的代碼只有一個main,沒有內(nèi)聯(lián)函數(shù)。Java編譯器難道不也是只分指令集的嗎,怎么能夠編譯出更加優(yōu)化的字節(jié)碼呢?而且這段代碼,Java還能怎么優(yōu)化呢?
追加:
按照@Untitled(sf沒有艾特的功能嗎)的提示,做下一個實驗證明JIT
對Java執(zhí)行速度的影響。這次使用命令行直接編譯,繞過IDE的影響。個人感覺兩分鐘僅輸出百來行的話IO操作對速度的影響可忽略不計。(由于這次圖片屢次上傳失敗因此只貼出shell相關操作,加上C++編譯結果)
pi@raspberrypi:~/workspace/testjava/src $ javac main.java
pi@raspberrypi:~/workspace/testjava/src $ java main
1:2
# 省略計算輸出
113:98689
110494
# 110秒,比在eclipse中執(zhí)行的速度還快,接下來禁用JIT
pi@raspberrypi:~/workspace/testjava/src $ java -Xint main
1:2
# 省略計算輸出
113:98689
797514
# 797秒,明顯慢于使用JIT的
pi@raspberrypi:~/workspace/testjava/src $
# C++編譯
pi@raspberrypi:~/cpplearn $ g++ -o main main.cpp
pi@raspberrypi:~/cpplearn $ time ./main
1:2
# 省略計算輸出
113:98689
real 4m5.606s
user 4m5.581s
sys 0m0.000s
#245秒,接下來啟用-O2選項
pi@raspberrypi:~/cpplearn $ g++ -O2 -o main main.cpp
pi@raspberrypi:~/cpplearn $ time ./main
1:2
# 省略計算輸出
113:98689
real 3m50.631s
user 3m50.384s
sys 0m0.010s
# 230秒,快了一點,和在codeblocks編譯的速度差不多
pi@raspberrypi:~/cpplearn $
JIT
確實是大幅度提升了Java的執(zhí)行速度。(從797到110)
看了一下JIT的相關資料(1,2),感覺就算是這樣,也不過就是不經(jīng)過JVM直接執(zhí)行了Java代碼,這和C++的編譯原理不是一樣的嗎?最多只是持平,怎么還會快這么多呢?
其實我不懂怎么反匯編,所以也不知道這怎么回事。我的循環(huán)也不是空的??赡艿脑挘蚁胫繨ava的JIT是怎么加快執(zhí)行這段代碼的速度的。
追加:
經(jīng)過幾次實驗,發(fā)現(xiàn)在x86/x64
架構中無論是在Windows還是Linux,實體機還是虛擬機,C++的速度在總體上都比Java更勝一籌。arm
的設備我除了樹莓派,剩下的只有Android手機了。我準備在一臺諾基亞7(驍龍630,4GB,原生Android 8.0,無root,已經(jīng)盡可能關掉所有后臺應用,在我看來是相當穩(wěn)定的測試環(huán)境。)上面進行測試。用來測試的軟件有兩個在手機上運行的IDE(部署Linux Deploy還是太麻煩了):AIDE (用來編譯Java代碼)和CIDE (用來編譯C++代碼,編譯器為aarch64的gcc7.2)。
由于在CIDE無法顯示程序執(zhí)行時間,因此這次在C++代碼也加入了計時。
#include <iostream>
#include <ctime>
using namespace std;
int main()
{
clock_t start = clock();
int input_num = 100000;
int pp_count = 0;
for (int each = 2; each <= input_num; each++)
{
int factorization_lst = 0;
for (int factor = 1; factor <= each; factor++)
if (each % factor == 0 && !(factor > each / factor))
factorization_lst++;
if (factorization_lst == 1)
{
int antitone = 0, each_cpy = each;
while (each_cpy)
{
antitone = antitone * 10 + each_cpy % 10;
each_cpy /= 10;
}
if (antitone == each)
{
pp_count++;
cout << pp_count << ':' << each << endl;
}
}
}
cout << 1000*(clock() - start) / CLOCKS_PER_SEC;
return 0;
}
優(yōu)化選項改成使用-O3(默認為-Os)
執(zhí)行結果:(這已經(jīng)是我挑選出來所用時間最短的了)
C++用了43s
Java用了37s
.....
(已經(jīng)經(jīng)過多次測試)
追加:
聽從Untitled的建議使用clang
編譯(Raspbian默認沒有安裝,還得自己apt install clang
一下)
速度有了質(zhì)的飛躍。(但還沒越過Java)
不使用優(yōu)化選項:3m22s(202s)
使用-O2選項:3m05s(185s)(使用-O3與-O2的執(zhí)行速度是差不多的)
順帶一提,我再次執(zhí)行java
版時去掉計時的那兩行代碼,
//long start = System.currentTimeMillis();
//System.out.println(System.currentTimeMillis() - start);
然后使用time
命令計時,結果時間延長了零點幾秒...
追加:
今晚身體不適,但還是抽出一點時間寫了Android上的測試應用。(源,下載)
在編寫過程中,我已經(jīng)盡量保證了公平。
因為今晚急著早點休息,暫時未進行充分的測試(但大體上C++比Java快很多)。大家可以自行下載測試一下,晚些時候我再發(fā)布一下詳細測試結果。
主要代碼:
MainActivity.java
package ryuunoakaihitomi.javacppperfcomparison;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.WindowManager;
import android.widget.Toast;
import java.util.Timer;
import java.util.TimerTask;
public class MainActivity extends Activity {
public static final String TAG = "JCPC";
static {
System.loadLibrary("native-lib");
}
@SuppressWarnings("ConstantConditions")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
setContentView(R.layout.activity_main);
getActionBar().setTitle("logcat -s JCPC");
Log.i(TAG, "Finding palindromic primes within 100,000.(Waiting for 3s)");
new Timer().schedule(new TimerTask() {
@Override
public void run() {
new Thread(new Runnable() {
@Override
public void run() {
final long jTime = pcTimer(true);
final long cTime = pcTimer(false);
runOnUiThread(new Runnable() {
@SuppressLint("DefaultLocale")
@Override
public void run() {
Toast.makeText(getApplicationContext(), String.format("java:%d\ncpp:%d", jTime, cTime), Toast.LENGTH_LONG).show();
finish();
}
});
}
}).start();
}
}, 3000);
}
public native void cpp();
long pcTimer(boolean isJava) {
long lStart = System.currentTimeMillis();
if (isJava)
Java.kernel();
else
cpp();
long lTime = System.currentTimeMillis() - lStart;
Log.i(TAG, "total time:" + lTime);
return lTime;
}
}
Java.java
package ryuunoakaihitomi.javacppperfcomparison;
public class Java {
static {
System.loadLibrary("native-lib");
}
static void kernel() {
int iInputNumber = 100000;
int iPalprimeCount = 0;
for (int iEach = 2; iEach <= iInputNumber; iEach++) {
int iFactorizationList = 0;
for (int iFactor = 1; iFactor <= iEach; iFactor++)
if (iEach % iFactor == 0 && !(iFactor > iEach / iFactor))
iFactorizationList++;
if (iFactorizationList == 1) {
int iAntitone = 0, iEachCopy = iEach;
while (iEachCopy != 0) {
iAntitone = iAntitone * 10 + iEachCopy % 10;
iEachCopy /= 10;
}
if (iAntitone == iEach) {
iPalprimeCount++;
ResultPrint(iPalprimeCount, iEach);
}
}
}
}
public static native void ResultPrint(int c, int e);
}
native-lib.cpp
#include <jni.h>
#include <android/log.h>
#include <string>
using namespace std;
void kernel();
void kernel_log(string, int, int);
extern "C" JNIEXPORT void
JNICALL
Java_ryuunoakaihitomi_javacppperfcomparison_MainActivity_cpp(
JNIEnv *,
jobject /* this */) {
kernel();
}
void kernel() {
int input_num = 100000;
int pp_count = 0;
for (int each = 2; each <= input_num; each++) {
int factorization_lst = 0;
for (int factor = 1; factor <= each; factor++)
/*Expression can be simplified to 'factor <= each / factor' less... (Ctrl+F1)
This inspection finds the part of the code that can be simplified, e.g. constant conditions, identical if branches, pointless boolean expressions, etc.*/
if (each % factor == 0 && factor <= each / factor)
factorization_lst++;
if (factorization_lst == 1) {
int antitone = 0, each_cpy = each;
while (each_cpy) {
antitone = antitone * 10 + each_cpy % 10;
each_cpy /= 10;
}
if (antitone == each) {
pp_count++;
kernel_log("c", pp_count, each);
}
}
}
}
void kernel_log(string t, int c, int e) {
__android_log_print(ANDROID_LOG_DEBUG, "JCPC", "%s %d:%d", t.c_str(), c, e);
}
extern "C"
JNIEXPORT void JNICALL
Java_ryuunoakaihitomi_javacppperfcomparison_Java_ResultPrint(JNIEnv *, jobject, jint c,
jint e) {
kernel_log("j", c, e);
}
追加:
準備環(huán)境:
實驗三次取各自的最小值,實驗結果:
說明:表格前四列的值均來自于android.os.Build
中對應名稱的常量
MODEL | MANUFACTURER | DISPLAY | SDK_INT | Java耗時 | C++耗時 | |
---|---|---|---|---|---|---|
GT-I9300 | samsung | lineage_i9300-userdebug 7.1.2 NJH47F 0f9e26b899 | 25 | 192169 |
171928 |
|
Redmi 4A | Xiaomi | NJH47F | 25 | 66009 |
31907 |
|
m2 | Meizu | Flyme 6.3.0.0A | 22 | 37722 |
34654 |
|
A2 | softwinner | 升級版四核2G運存 | 19 | 239865 |
202402 |
|
Redmi Note 3 | Xiaomi | OPM1.171019.018 | 27 | 22299 |
18105 |
|
TA-1041 | HMD Global | 00CN_1_34E | 26 | 37310 |
20234 |
|
HTC 802t | htc | LRX22G release-keys | 21 | 48211 | 125279 |
可以看出,絕大多數(shù)的arm Android設備運行C++的速度快過Java。但是最后這一行的結果超出了預料。
這個設備的CPU是驍龍600。(好奇怪......)
另:我前兩天買了一個香橙派zero plus
,用的全志H5
。C++45s
,java70s
。
我的所有arm設備已經(jīng)測試完成,我能不能得到以下結論。
在一小部分的arm指令集架構設備中,Java的運行速度會快于C++。
想知道原因。
1) 看下這個https://benchmarksgame-team.p...,這個基本上Cpp無一落敗
2) 除了-O2外,還有-O3,還有其他的編譯參數(shù),請參見GCC手冊
3) JVM默認是用空間換時間的,所以這么對比不是很適合
你的代碼,我在未優(yōu)化一行代碼的情況下,使用-O3來測試(實際上release都是-O3),運算3次都比Java快,這中間還包括加載程序啟動的時間,怎么得出結論會比java慢呢。
當然,C++對程序員要求很高,不了解內(nèi)存模型、編譯原理什么的,是很難寫出高質(zhì)量的C++的,在這一點上,java就好很多
最近買了一個樹莓派3B+,特意跑了下這個程序,從性能上看,C++比java在Arm上略快,優(yōu)勢不明顯,另外寫了一個rust版本的代碼,算法上未優(yōu)化,性能跟C++接近,在release情況下比Java略快。
fn main() {
let input_num=100001;
let mut pp_count =0;
for each in 2..input_num {
let mut factorization_lst=0;
for factor in 1..each+1 {
if each%factor==0 &&!(factor>each/factor) {
factorization_lst += 1;
}
}
if factorization_lst==1
{
let mut antitone =0;
let mut each_cpy =each;
while each_cpy != 0
{
antitone=antitone*10+each_cpy%10;
each_cpy/=10;
}
if antitone==each
{
pp_count += 1;
println!("{}:{}", pp_count, each);
}
}
}
}
從CPU上來,基本上在運行期這3個程序都是跑滿單核的(樹莓派3B+有4個core),但內(nèi)存上來看,C++和rust有明顯優(yōu)勢,大概為java版本的1/10.
這個測試從測試結果來看,這幾個語言的運行性能差異沒那么大,分析了下有幾個原因
1) 對于int數(shù)據(jù)類型,在java C++ rust里都是原生類型,原生的在計算時差別不大,對于java來說,如果是Integer可能有性能損耗。
2) 只跑了一個核,沒有多核之間的數(shù)據(jù)傳遞等
3) 沒有用到遞歸、值傳遞、引用傳遞、值拷貝等特性,差異不大。
結論: java是一個性價比比較好的語言,運行性能上或許不是最優(yōu),但開發(fā)效率很好,不像其他的語言要考慮跨平臺移植問題、編譯參數(shù)問題等。
PS 未來看好rust
不會寫 C++ 的人寫出來的 C++ 確實可以比 Java 慢,甚至比沒有任何優(yōu)化直接解釋執(zhí)行的 Java 慢。
但是會寫 C++ 的人永遠可以寫出完虐解釋執(zhí)行的 Java 的代碼,但是 JIT 的 Java 還是可以比 C++ 快,因為它能做一些依賴運行時才能知道的信息才能做的優(yōu)化。這也是現(xiàn)在 C++ 編譯器進化的方向,一個是加入運行時信息的優(yōu)化,一個是 link time 優(yōu)化。
歸根結底,比較兩個語言的速度是耍流氓,只能比寫代碼的人的質(zhì)量和編譯器的質(zhì)量。
題主的問題應該恰是一個運行時掌握的信息能進行更多優(yōu)化的例子。
幫題主重寫了下 C++ 和 Java 的代碼:
測試環(huán)境:x86_64 架構,C++/LLVM 6.0,Java/HotSpot 18.3
C++ 版用時 0.008s (time 命令)
Java 版用時 0.045340767000000004s (System.nanoTime)
可見 C++ 版算上前后的時間還是比 Java 快一個數(shù)量級。
(即便 Java 版在計時前運行同樣地代碼上萬次來預熱,多次運行結果也沒有變化,因此測量的結果是科學的。)
附代碼:
#include <iostream>
using namespace std;
constexpr int N = 100000;
bool C[N + 5];
int main() {
ios_base::sync_with_stdio(0);
cin.tie(0);
int c = 0;
for (int i = 2; i < N; ++i) {
if (!C[i]) {
for (int j = i + i; j < N; j += i) {
C[j] = true;
}
int z = 0;
for (int k = i; k != 0; ) {
z *= 10;
z += k % 10;
k /= 10;
}
if (z == i) {
cout << ++c << ':' << i << '\n';
}
}
}
}
class Two {
static final int N = 100000;
static final boolean[] C = new boolean[N + 5];
public static void main(String[] args) {
long start = System.nanoTime();
int c = 0;
for (int i = 2; i < N; ++i) {
if (!C[i]) {
for (int j = i + i; j < N; j += i) {
C[j] = true;
}
int z = 0;
for (int k = i; k != 0; ) {
z *= 10;
z += k % 10;
k /= 10;
}
if (z == i) {
System.out.println(++c + ":" + i);
}
}
}
System.out.println("time: " + (System.nanoTime() - start) * 1e-9 + "s");
}
}
我在學校的老服務器上跑了一遍你給的代碼,cpp的用系統(tǒng)自帶的time
計時,java的用程序自己的結果,結果是cpp的25.125s的用戶時間,25.130s的墻上時鐘時間;java的25.240s的用戶時間,25.250s的墻上時鐘時間,自稱25114毫秒(25.114s)運行結束,感覺好像是差不多的。為了排除I/O操作的影響,我把除了java版的最后一句輸出外的cpp和java的所有輸出都注釋掉再試了一次,結果還是差不多。所以我打算把十萬改成一百萬,還沒跑完,有空再把結果放上來哈哈哈哈哈哈哈哈哈哈哈哈哈
以上是非常臨時且不嚴謹?shù)膶嶒灲Y果,沒空做大量的對照實驗。我想某些情況下,代碼邏輯的完全相同的java程序比cpp程序或c程序快的原因應該就是編譯器優(yōu)化和個別操作實現(xiàn)的差異的原因,除此之外暫時想不到別的原因。不管是什么語言寫的程序,實際“運行”的一定是機器指令,cpp和c編譯后的正文部分是一堆機器指令序列,java編譯后是jvm的機器指令序列(是的吧?),但執(zhí)行的時候還要jvm去解釋字節(jié)碼,解釋執(zhí)行的時候仍要執(zhí)行宿主機的機器指令,不過好像還有JIT
的說法?(不是很熟悉java)。所以我相信理論上來講,不使用JIT
的話,java肯定是沒有cpp和c快的(在“相同的程序”的意義下,但這個“相同”又很難定義,你懂就好),因為java還要“解釋”,然后才能“執(zhí)行”。因此我覺得一定是編譯器的鍋,但也只是覺得,我手上也沒有什么實驗結果。
你可以試試能不能把JIT
關了,再比較結果?
有興趣的話可以多試試不同的程序,簡單的,復雜的,多種程序對比(記得java好像會自動跳過一些無意義的循環(huán)體,比如空轉的循環(huán)體,這點要注意)。其中簡單的程序可以對編譯出來的文件進行反匯編,分析理論執(zhí)行效率,找到導致差異的原因。
試了一下在樹莓派3B上跑,結果比題主的慢多了,但java確實比cpp快,用的和樓主一樣的代碼,cpp跑了4分多鐘,java跑了將近2分鐘,quite interesting :D。cpp和java均使用了默認編譯參數(shù),g++版本是4.9.2,javac版本是1.8.0_65,java版本是1.8.0_65-b17。
然后我又給cpp版的加了-O2
參數(shù),時間提升到3分40多秒——還是沒java版的快emmmmm
這就比較神奇了,于是我又試了一下別的程序——我自己寫了個斐波那契數(shù)列求值的——這回舒服了,cpp的1.6s,java的3.4s,更多詳情日后再說,先掃墓去了以下是測試代碼
// test.cc
#include <iostream>
using namespace std;
int fibo(int n)
{
switch(n)
{
case 1: return 1;
case 2: return 1;
default: return fibo(n - 1) + fibo(n - 2);
}
}
int main()
{
cout << fibo(40) << endl;
return 0;
}
// main.java
public class main {
private static int fibo(int n) {
switch(n) {
case 1: return 1;
case 2: return 1;
default: return fibo(n - 1) + fibo(n - 2);
}
}
public static void main(String[] args) {
long start = System.currentTimeMillis();
System.out.println(fibo(40));
System.out.println(System.currentTimeMillis() - start);
}
}
// Makefile
main: test.cc main.java
g++ test.cc -o test -O2
javac main.java
# test.sh
time(./test) 1>cpp.res 2>cpp.time
time(java main) 1>java.res 2>java.time
make
./test.sh
那個prompt是用powerline-shell
反正是要燒CPU,就沒用動態(tài)規(guī)劃了(其實是忘了怎么動態(tài)規(guī)劃了)。
可以初步推斷,簡單的遞歸cpp完勝java(除了“簡單的”,還需要補充哪些限制,歡迎評論)。
然后我又測試了瘋狂swap兩個int
// test.cc
#include <iostream>
#include <cstdlib>
using namespace std;
int main(int argc, char *argv[])
{
int a = 1, b = 2;
int times = atoi(argv[1]);
for(int i = 0; i < times; i++)
{
a = a + b;
b = a - b;
a = a - b;
}
return 0;
}
// main.java
public class main {
public static void main(String[] args) {
long start = System.currentTimeMillis();
int a = 1;
int b = 2;
int times = Integer.parseInt(args[0]);
for(int i = 0; i < times; i++) {
a = a + b;
b = a - b;
a = a - b;
}
System.out.println(System.currentTimeMillis() - start);
}
}
// Makefile
main: test.cc main.java
g++ test.cc -o test
javac main.java
注意:為什么不優(yōu)化了呢,因為我發(fā)現(xiàn)優(yōu)化的話cpp會偷懶,它知道實際上瘋狂swap兩個沒用的東西又沒輸出等于啥都沒干,所以會直接退出程序(O2優(yōu)化),或空循環(huán)(O1優(yōu)化)
# test.sh
time(./test $1) 2>cpp.time
time(java main $1) 1>java.res 2>java.time
cat cpp.time
cat java.res
make
./test.sh 1000
./test.sh 10000
./test.sh 100000
./test.sh 1000000
./test.sh 10000000
./test.sh 100000000
./test.sh 1000000000
看不太懂ATT風格的匯編代碼,關閉了優(yōu)化之后,cpp編譯出來的二進制文件中多出了str
(store register?)指令,瘋狂訪問內(nèi)存勢必會大大增長代碼執(zhí)行時間……至于java,用javap -c main
反匯編了一下,確實是有看到iload
和istore
的命令(我不會jvm的匯編,這兩句應該就是從內(nèi)存里加載到寄存器和從寄存器存儲到內(nèi)存的指令吧),看起來為什么java訪問內(nèi)存的速度比cpp快這么多呢,我也不知道哈哈哈哈哈,可能需要更深入地了解java的內(nèi)存模型之類的,另外這兩個指令是不是總是會導致jvm去訪問內(nèi)存呢?還是可能只是會訪問寄存器?顯然如果是后者,速度必然比前者快得多,然而這個問題我也不知道答案。
不會寫64位的arm匯編程序,不然我可以試試用匯編瘋狂交換兩個寄存器的值會不會比java版的還快。
你要是說在都不優(yōu)化的情況下同等代碼java跑得比cpp或c快我是不信的,一個純半解釋執(zhí)行的程序干得過編譯執(zhí)行的程序我也是不信的。我認為,導致等效的java程序跑得比記者cpp程序還快的原因應該就是編譯器(速度全靠一手編譯器)。至于上面的結果,我無可奉告也不知道如何解釋,雖然至少遞歸方面cpp勝出了emmm,才疏學淺,沒有辦法究其原因
技不如人,甘拜下風
ggwp
又在手機上小測試了一下,一加5T,驍龍835CPU(ARM架構),終端模擬器使用Termux,編譯器使用clang
和ecj
,java虛擬機使用安卓自帶的dalvikvm
,開啟了jit
,結果java跑了86秒,cpp跑了26秒。
編譯cpp代碼的命令(這里的g++
實際上還是clang
)
g++ test.cc -o test -O2
編譯java代碼使用的命令參考這里
運行命令參數(shù)加了-Xusejit:true
,-Xusejit:1
也試過了,結果也是86秒。
北大青鳥APTECH成立于1999年。依托北京大學優(yōu)質(zhì)雄厚的教育資源和背景,秉承“教育改變生活”的發(fā)展理念,致力于培養(yǎng)中國IT技能型緊缺人才,是大數(shù)據(jù)專業(yè)的國家
北大青鳥中博軟件學院創(chuàng)立于2003年,作為華東區(qū)著名互聯(lián)網(wǎng)學院和江蘇省首批服務外包人才培訓基地,中博成功培育了近30000名軟件工程師走向高薪崗位,合作企業(yè)超4
中公教育集團創(chuàng)建于1999年,經(jīng)過二十年潛心發(fā)展,已由一家北大畢業(yè)生自主創(chuàng)業(yè)的信息技術與教育服務機構,發(fā)展為教育服務業(yè)的綜合性企業(yè)集團,成為集合面授教學培訓、網(wǎng)
達內(nèi)教育集團成立于2002年,是一家由留學海歸創(chuàng)辦的高端職業(yè)教育培訓機構,是中國一站式人才培養(yǎng)平臺、一站式人才輸送平臺。2014年4月3日在美國成功上市,融資1
浪潮集團項目經(jīng)理。精通Java與.NET 技術, 熟練的跨平臺面向對象開發(fā)經(jīng)驗,技術功底深厚。 授課風格 授課風格清新自然、條理清晰、主次分明、重點難點突出、引人入勝。
曾工作于聯(lián)想擔任系統(tǒng)開發(fā)工程師,曾在博彥科技股份有限公司擔任項目經(jīng)理從事移動互聯(lián)網(wǎng)管理及研發(fā)工作,曾創(chuàng)辦藍懿科技有限責任公司從事總經(jīng)理職務負責iOS教學及管理工作。
精通HTML5和CSS3;Javascript及主流js庫,具有快速界面開發(fā)的能力,對瀏覽器兼容性、前端性能優(yōu)化等有深入理解。精通網(wǎng)頁制作和網(wǎng)頁游戲開發(fā)。
具有10 年的Java 企業(yè)應用開發(fā)經(jīng)驗。曾經(jīng)歷任德國Software AG 技術顧問,美國Dachieve 系統(tǒng)架構師,美國AngelEngineers Inc. 系統(tǒng)架構師。