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 มากกว่าครับ


Russia is just nazi who accuse the others for being nazi.
someone once said : ผมก็ด่าของผมอยู่นะ :)