Tags:

//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 กลับทำเวลาได้ดีกว่า

Get latest news from Blognone
By: UltimaWeapon
Windows PhoneRed HatWindowsIn Love
on 6 December 2014 - 00:07 #770006
UltimaWeapon's picture

ลองใส่ inline ข้างหน้าฟังชั่น calculate ดูคับ

By: mr_tawan
ContributoriPhoneAndroidWindows
on 6 December 2014 - 04:32 #770054
mr_tawan's picture

ผมไม่ทราบนะว่าทำไม อันนี้เป็นผลลัพท์จากเครื่องผมครับ

Java : Sun Java 1.7.0_51

200000000
Execution time: 141.665119 milliseconds

C : Clang 3.5.0 (MSYS2)

200000000
Execution time: 0.556000 milliseconds

GCC 4.9.2 ก็ทำได้พอ ๆ กันนะครับ

เครื่องที่ใช้เป็น

  • AMD Phenom II X4 925 2.50GHz
  • RAM 16GB
  • Windows 8.1 x64

Edit: Java ผมลองอีกที ทำได้ประมาณ 34 ms ครับ

Edit 2: โค๊ดจับเวลาของคุณในภาษา C มีปัญหาครับ คือ ผมเดาว่าคุณใช้ Linux คุณก็เลยหารด้วย 100000 แต่บน Windows เนี่ยมันแค่ 1000 tick ต่อวินาทีเองครับ (ดังนั้นต้องหารพัน) ผมเข้าใจว่าคุณจับเวลาได้ถูกแล้วล่ะ บน C ช้ากว่าจริง ๆ

The value returned is expressed in clock ticks, which are units of time of a constant but system-specific length (with a relation of CLOCKS_PER_SEC clock ticks per second).


  • 9tawan.net บล็อกส่วนตัวฮับ
By: mr_tawan
ContributoriPhoneAndroidWindows
on 6 December 2014 - 04:48 #770069
mr_tawan's picture

อ่า ลองใส่ -O3 (เปิด optimization level 3) ในคำสั่ง gcc ดูนะครับ

ที่ผมลองดูมันเร็วขึ้น 10 เท่าเลย


  • 9tawan.net บล็อกส่วนตัวฮับ
By: Bigkung
iPhoneWindows Phone
on 6 December 2014 - 17:38 #770386 Reply to:770069
Bigkung's picture

อ่ารบกวนหน่อยครับ พอจะทราบข้อเสียของการใช้ -O3 หน่อยได้หรือเปล่าครับ หรือว่าไม่มีครับ เห็นมีระดับให้เลือกใส่ได้ด้วย สงสัยว่ามีกี่ระดับถ้าเอาระดับสูงๆจะเกิดอะไรขึ้นครับ ขอบคุณครับ

ผมเจออะไรเด็ดๆ มาด้วยหล่ะ 3.10 Options That Control Optimization

แต่อยากถามผู้ที่ลองนำไปแล้วแล้วหน่ะครับว่าผลเป็นรูปธรรมยังไงบ้าง

By: mr_tawan
ContributoriPhoneAndroidWindows
on 7 December 2014 - 17:34 #770728 Reply to:770386
mr_tawan's picture

เท่าที่ผมทราบนะครับ เวลาเปิด Optimization ผลลัพท์ที่ได้อาจจะไม่ตรงกับโค๊ดที่เราเขียนน่ะครับ แต่ผลลัพท์ควรจะตรงกัน

เวลาเปิด asm ดูอาจจะงงว่า เฮ้ย เราไม่ได้เขียนแบบนี้นี่หว่า

ทั้งนี้ถ้าเป็น optimization ระดับสูงมาก ๆ อาจจะเจอว่าผลลัพท์ไม่ตรงตามที่คาดไว้ด้วยครับ


  • 9tawan.net บล็อกส่วนตัวฮับ
By: neizod
ContributorTraineeIn Love
on 9 December 2014 - 02:06 #771283 Reply to:770386
neizod's picture

ข้อเสียของการใช้ -O3 คือ space-time tradeoff ครับ ซึ่งมีหลายตัวอย่างมากๆ แต่ผมรู้จักไม่กี่อัน เช่น เทคนิคที่เรียกว่า loop unrolling กล่าวคือถ้าคอมไพล์ลูปง่ายๆ อย่างลูปที่จำนวนรอบเป็นค่าคงที่ ตัวคอมไพลเลอร์อาจจะพยายามกำจัด loop ทิ้งไป ดังนี้

for (i=0; i<100; i++) {
    x *= 2;
}

พอคอมไพล์แล้วอาจกลายเป็น

for (i=0; i<50; i++) {
    x *= 2;
    x *= 2;
}

จะเห็นว่าแค่นี้ก็ประหยัดเวลาการตรวจเงื่อนไขของลูป for ไปได้ครึ่งๆ เลยทีเดียว ส่วนการคอมไพล์จริงอาจจะแตกออกมาจนไม่ต้องตรวจเงื่อนไขเลยก็ได้ครับ

ในคู่มือของ gcc (เว็บที่แปะมานั่นแหละ) เค้าบอกเลยเลยว่า -O1 เปิด flag optimize ตัวไหนบ้าง -O2 เปิดตัวไหนบ้าง ถ้าอยากรู้โค้ดเบื้องหลังจริงๆ แนะนำให้คอมไพล์เทียบกันเป็น assembly มาดูเลยครับ โดยอาจจะเลือกเปิดทีละ flag ที่อยากรู้ก็ได้

ส่วนเรื่องเปิด -O3 แล้วโปรแกรมที่ได้ออกมาดันบั๊กจนหลายๆ คนแนะนำว่าเปิดแค่ -O2 พอ มันเป็นเรื่องเมื่อกว่าสิบปีมาแล้วครับ ตอนนี้สบายใจได้ ไปดูว่าเวลาที่ใช้ในการคอมไพล์/ขนาดผลลัพธ์ที่ได้จากการคอมไพล์มันใหญ่เกินรับได้หรือเปล่าดีกว่า

By: R60 on 6 December 2014 - 09:10 #770148

Linux 32 bit (Ubuntu)
Java ใช้ openjdk-7
C ใช้ gcc 4.8
Intel centrino duo 1.8 GHz

ผมทดลองปรับลูปให้เหลือแค่ 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 ดูว่าจะเป็นอย่างไรขอบคุณครับ

By: satchkid
ContributoriPhone
on 6 December 2014 - 10:22 #770196
satchkid's picture

โอ...เท่าที่ผมลองดู
ใส่ -O3 กับไม่ใส่เนี่ย..ความเร็วมันต่างกันฟ้ากับเหวเลยนะครับเนี่ย

อันนี้ไม่ใส่ -O3

200000000
Execution time: 804.320007 milliseconds

ถ้าใส่ -O3

200000000
Execution time: 0.041000 milliseconds

ส่วนบน Java

200000000
Execution time: 31.942999999999998 milliseconds

C ผมใช้ Clang LLVM 3.5
ส่วน Java ผมใช้ Java 1.8.0

Com ผม
CPU: i5 2.3 GHz
RAM: 8GB
OS X 10.10.1

By: nat3738
ContributorAndroidRed HatUbuntu
on 6 December 2014 - 10:28 #770199 Reply to:770196

C/C++ ถ้าไม่เปิด optimize อะไรเลย มันจะแปลงโค้ดตรงๆ ไม่เปลี่ยนอะไรเลยครับ (มีคูณอยู่ในเงื่อนไข for มันก็คูณให้ทุกรอบ) เพื่อให้ดีบักง่ายครับ เวลาคอมไพล์จริงๆ ต้อง -O3 หรือ -Ofast ทุกครั้งครับ

By: R60 on 6 December 2014 - 10:58 #770209

อั๊ยยะ ผมนี่เรียกเพื่อนมาดูเลย -O3 มันดีอย่างนี้นี่เอง
ปกติชีวิตก็วนเวียนอยู่กับ Java ไม่ค่อยมีประการณ์กับ C/C++เท่าไร ขอบคุณทุกคำตอบครับ
แต่เอ่อ gcc มันตกกระป๋องแล้วเหรอครับ ทุกท่านใช้ clang กันหมดเลย 555

By: mr_tawan
ContributoriPhoneAndroidWindows
on 7 December 2014 - 17:32 #770727 Reply to:770209
mr_tawan's picture

Clang อ่าน Error/Warning ง่ายกว่าครับ ไม่ต้องตกใจไป :)


  • 9tawan.net บล็อกส่วนตัวฮับ
By: luckyman
ContributoriPhoneAndroidRed Hat
on 6 December 2014 - 21:50 #770493

ช้ากว่าเพราะเป็นการรัน optimized java เทียบกับ unoptimized C ครับ
เพราะโปรแกรมมันรันนานพอให้ jit ของจาวาทำงาน

โดยทั่วไปถ้าจะทำ benchmark แบบนี้ ต้องเปิด optimization ประมาณ -O2 ครับ

ถ้าจะรันจาวาแบบไม่ jit สามารถลองได้โดย
java -Djava.compiler=NONE Bench

By: tekkasit
ContributorAndroidWindowsIn Love
on 7 December 2014 - 22:26 #770814 Reply to:770493
tekkasit's picture

เอิ่มพอสั่งปิด นี่มันเข้าโหมด 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

By: luckyman
ContributoriPhoneAndroidRed Hat
on 8 December 2014 - 11:24 #771008 Reply to:770814

ใช่ครับ ดูเหมือนจะไม่มีวิธีปิดเฉพาะ optimization

By: McKay
ContributorAndroidWindowsIn Love
on 9 December 2014 - 02:49 #771287 Reply to:771008
McKay's picture

กรณี Java JIT ทำงานคงเรียกว่า optimization ไม่ได้นะครับ เพราะมันเป็นค่า default และ JIT เองก็เป็น character ของ Java อยู่แล้ว

กรณีนี้ควรเรียกว่ารูปแบบของคำสั่งเหมาะกับ JIT มากกว่าครับ


In Soviet Warcraft, Argus comes to you.