//Java
public class Main {
protected static int global;
public static void main(String[] args) {
long t1 = System.nanoTime();
int value = 0;
for (int i = 0; i < 100 * 1000 *1000; i++) {
value = calculate(value);
}
System.out.println(value);
long t2 = System.nanoTime();
System.out.println("Execution time: " + ((t2 - t1) * 1e-6) + " milliseconds");
}
protected static int calculate(int arg) {
//L1: assert (arg >= 0) : "should be positive";
//L2: if (arg < 0) throw new IllegalArgumentException("arg = " + arg + " < 0");
global = arg * 6;
global += 3;
global /= 2;
return arg + 2;
}
}
javac Main.java
java Main
Execution time: 16.550917 milliseconds
//C
#include<stdio.h>
#include<time.h>
int global;
clock_t t1, t2;
int main(){
t1 = clock();
int value = 0;
for (int i = 0; i < 100 * 1000 *1000; i++) {
value = calculate(value);
}
printf("%d \n",value);
t2 = clock();
float diff = (((float)t2 - (float)t1) / 1000000.0F ) * 1000;
printf("Execution time: %f milliseconds \n",diff);
return 0;
}
int calculate(int arg){
global = arg * 6;
global += 3;
global /= 2;
return arg + 2;
}
gcc main.c -std=c99 -o main
./main
Execution time: 703.835022 milliseconds
ทำไมทำ C ถึงทำเวลาได้แย่กว่า Java เมื่อจำนวนลูปเพิ่มขึ้น แต่เมื่อจำนวนลูปน้อยๆ C กลับทำเวลาได้ดีกว่า
ลองใส่ inline ข้างหน้าฟังชั่น
UltimaWeapon Sat, 06/12/2014 - 00:07
ลองใส่ inline ข้างหน้าฟังชั่น calculate ดูคับ
ผมไม่ทราบนะว่าทำไม
mr_tawan Sat, 06/12/2014 - 04:32
ผมไม่ทราบนะว่าทำไม อันนี้เป็นผลลัพท์จากเครื่องผมครับ
Java : Sun Java 1.7.0_51
C : Clang 3.5.0 (MSYS2)
GCC 4.9.2 ก็ทำได้พอ ๆ กันนะครับ
เครื่องที่ใช้เป็น
Edit: Java ผมลองอีกที ทำได้ประมาณ 34 ms ครับ
Edit 2: โค๊ดจับเวลาของคุณในภาษา C มีปัญหาครับ คือ ผมเดาว่าคุณใช้ Linux คุณก็เลยหารด้วย 100000 แต่บน Windows เนี่ยมันแค่ 1000 tick ต่อวินาทีเองครับ (ดังนั้นต้องหารพัน) ผมเข้าใจว่าคุณจับเวลาได้ถูกแล้วล่ะ บน C ช้ากว่าจริง ๆ
อ่า ลองใส่ -O3 (เปิด
mr_tawan Sat, 06/12/2014 - 04:48
อ่า ลองใส่ -O3 (เปิด optimization level 3) ในคำสั่ง gcc ดูนะครับ
ที่ผมลองดูมันเร็วขึ้น 10 เท่าเลย
อ่ารบกวนหน่อยครับ
Bigkung Sat, 06/12/2014 - 17:38
In reply to อ่า ลองใส่ -O3 (เปิด by mr_tawan
อ่ารบกวนหน่อยครับ พอจะทราบข้อเสียของการใช้ -O3 หน่อยได้หรือเปล่าครับ หรือว่าไม่มีครับ เห็นมีระดับให้เลือกใส่ได้ด้วย สงสัยว่ามีกี่ระดับถ้าเอาระดับสูงๆจะเกิดอะไรขึ้นครับ ขอบคุณครับ
ผมเจออะไรเด็ดๆ มาด้วยหล่ะ 3.10 Options That Control Optimization
แต่อยากถามผู้ที่ลองนำไปแล้วแล้วหน่ะครับว่าผลเป็นรูปธรรมยังไงบ้าง
เท่าที่ผมทราบนะครับ เวลาเปิด
mr_tawan Sun, 07/12/2014 - 17:34
In reply to อ่ารบกวนหน่อยครับ by Bigkung
เท่าที่ผมทราบนะครับ เวลาเปิด Optimization ผลลัพท์ที่ได้อาจจะไม่ตรงกับโค๊ดที่เราเขียนน่ะครับ แต่ผลลัพท์ควรจะตรงกัน
เวลาเปิด asm ดูอาจจะงงว่า เฮ้ย เราไม่ได้เขียนแบบนี้นี่หว่า
ทั้งนี้ถ้าเป็น optimization ระดับสูงมาก ๆ อาจจะเจอว่าผลลัพท์ไม่ตรงตามที่คาดไว้ด้วยครับ
ข้อเสียของการใช้ -O3 คือ
neizod Tue, 09/12/2014 - 02:06
In reply to อ่ารบกวนหน่อยครับ by Bigkung
ข้อเสียของการใช้
-O3คือ space-time tradeoff ครับ ซึ่งมีหลายตัวอย่างมากๆ แต่ผมรู้จักไม่กี่อัน เช่น เทคนิคที่เรียกว่า loop unrolling กล่าวคือถ้าคอมไพล์ลูปง่ายๆ อย่างลูปที่จำนวนรอบเป็นค่าคงที่ ตัวคอมไพลเลอร์อาจจะพยายามกำจัด loop ทิ้งไป ดังนี้พอคอมไพล์แล้วอาจกลายเป็น
จะเห็นว่าแค่นี้ก็ประหยัดเวลาการตรวจเงื่อนไขของลูป
forไปได้ครึ่งๆ เลยทีเดียว ส่วนการคอมไพล์จริงอาจจะแตกออกมาจนไม่ต้องตรวจเงื่อนไขเลยก็ได้ครับในคู่มือของ gcc (เว็บที่แปะมานั่นแหละ) เค้าบอกเลยเลยว่า
-O1เปิด flag optimize ตัวไหนบ้าง-O2เปิดตัวไหนบ้าง ถ้าอยากรู้โค้ดเบื้องหลังจริงๆ แนะนำให้คอมไพล์เทียบกันเป็น assembly มาดูเลยครับ โดยอาจจะเลือกเปิดทีละ flag ที่อยากรู้ก็ได้ส่วนเรื่องเปิด
-O3แล้วโปรแกรมที่ได้ออกมาดันบั๊กจนหลายๆ คนแนะนำว่าเปิดแค่-O2พอ มันเป็นเรื่องเมื่อกว่าสิบปีมาแล้วครับ ตอนนี้สบายใจได้ ไปดูว่าเวลาที่ใช้ในการคอมไพล์/ขนาดผลลัพธ์ที่ได้จากการคอมไพล์มันใหญ่เกินรับได้หรือเปล่าดีกว่าLinux 32 bit (Ubuntu) Java
R60 Sat, 06/12/2014 - 09:10
ผมทดลองปรับลูปให้เหลือแค่ for (int i = 0; i < 100 * 1000 ; i++) ใน C จะทำงานได้เร็วกว่า แต่เมื่อเพิ่มจำนวนลูป for (int i = 0; i < 100 * 1000 * 1000 ; i++) C กลับทำได้แย่กว่าเสียอย่างนั้น ตอนแรกคิดว่าที่ช้าลงน่าจะเกิดจากการเรียกใช้งาน ฟังชั่น calculate แต่พอลองคอมเมนต์ฟังก์ชั่นในลูป ก็ยังช้าเหมือนเดิมแสดงว่าที่ช้าลงน่าเกิดจำนวนลูปที่เพิ่มขึ้น (มันช้าลงนะถูก แต่ช้ากว่า Java นี่ซิ) ผมพยามที่แปลงโค๊ด Java บางส่วนของผมมาเป็น native เลยหาโด๊ดทดสอบ แต่โด๊ดนี้เมื่อแปลงเป็น native ดันช้ากว่าเสียอย่างนั้น เดียวจะลองใส่ -O3 ดูว่าจะเป็นอย่างไรขอบคุณครับ
โอ...เท่าที่ผมลองดู ใส่ -O3
satchkid Sat, 06/12/2014 - 10:22
โอ...เท่าที่ผมลองดู
ใส่ -O3 กับไม่ใส่เนี่ย..ความเร็วมันต่างกันฟ้ากับเหวเลยนะครับเนี่ย
อันนี้ไม่ใส่ -O3
ถ้าใส่ -O3
ส่วนบน Java
C ผมใช้ Clang LLVM 3.5
ส่วน Java ผมใช้ Java 1.8.0
Com ผม
CPU: i5 2.3 GHz
RAM: 8GB
OS X 10.10.1
C/C++ ถ้าไม่เปิด optimize
nat3738 Sat, 06/12/2014 - 10:28
In reply to โอ...เท่าที่ผมลองดู ใส่ -O3 by satchkid
C/C++ ถ้าไม่เปิด optimize อะไรเลย มันจะแปลงโค้ดตรงๆ ไม่เปลี่ยนอะไรเลยครับ (มีคูณอยู่ในเงื่อนไข for มันก็คูณให้ทุกรอบ) เพื่อให้ดีบักง่ายครับ เวลาคอมไพล์จริงๆ ต้อง -O3 หรือ -Ofast ทุกครั้งครับ
อั๊ยยะ
R60 Sat, 06/12/2014 - 10:58
อั๊ยยะ ผมนี่เรียกเพื่อนมาดูเลย -O3 มันดีอย่างนี้นี่เอง
ปกติชีวิตก็วนเวียนอยู่กับ Java ไม่ค่อยมีประการณ์กับ C/C++เท่าไร ขอบคุณทุกคำตอบครับ
แต่เอ่อ gcc มันตกกระป๋องแล้วเหรอครับ ทุกท่านใช้ clang กันหมดเลย 555
Clang อ่าน Error/Warning
mr_tawan Sun, 07/12/2014 - 17:32
In reply to อั๊ยยะ by R60
Clang อ่าน Error/Warning ง่ายกว่าครับ ไม่ต้องตกใจไป :)
ช้ากว่าเพราะเป็นการรัน
luckyman Sat, 06/12/2014 - 21:50
ช้ากว่าเพราะเป็นการรัน optimized java เทียบกับ unoptimized C ครับ
เพราะโปรแกรมมันรันนานพอให้ jit ของจาวาทำงาน
โดยทั่วไปถ้าจะทำ benchmark แบบนี้ ต้องเปิด optimization ประมาณ -O2 ครับ
ถ้าจะรันจาวาแบบไม่ jit สามารถลองได้โดย
java -Djava.compiler=NONE Bench
เอิ่มพอสั่งปิด นี่มันเข้าโหมด
tekkasit Sun, 07/12/2014 - 22:26
In reply to ช้ากว่าเพราะเป็นการรัน by luckyman
เอิ่มพอสั่งปิด นี่มันเข้าโหมด interpreter เลยนะครับ ช้าลงสามร้อยห้าสิบเท่าเห็นจะได้ (จากต่ำสุดที่ 26 ms ปาไปไม่ต่ำกว่า 9100 ms)
Oracle Server JRE 1.8.0_20-b26 บน Windows 7 64-bit Intel Core i5 (i5-3320M) @2.6GHz แรม 16 GB
ใช่ครับ
luckyman Mon, 08/12/2014 - 11:24
In reply to เอิ่มพอสั่งปิด นี่มันเข้าโหมด by tekkasit
ใช่ครับ ดูเหมือนจะไม่มีวิธีปิดเฉพาะ optimization
กรณี Java JIT ทำงานคงเรียกว่า
McKay Tue, 09/12/2014 - 02:49
In reply to ใช่ครับ by luckyman
กรณี Java JIT ทำงานคงเรียกว่า optimization ไม่ได้นะครับ เพราะมันเป็นค่า default และ JIT เองก็เป็น character ของ Java อยู่แล้ว
กรณีนี้ควรเรียกว่ารูปแบบของคำสั่งเหมาะกับ JIT มากกว่าครับ