Jump to content


Windows Server 2012

- - - - -

ทำไมแตกเธรดแล้วมันทำงานช้ากว่าเดิม


  • Please log in to reply
8 replies to this topic

#1 momomomo

momomomo

    Newbie

  • Members
  • Pip
  • 6 posts

Posted 14 July 2011 - 11:31 PM

ผมได้ลองทำการเขียน java แบบแตกเธรดแล้วทำไมมันถึงได้ทำงานช้าลงจากการจับเวลา ซึ่งถ้ากำหนดให้แตกหนึ่งเธรดจะเร็วกว่าแตก10 เธรด 100เธรด
ในจำนวนเดต้า 5000 , 500000 , 5000000 ยังไงหนึ่งเธรดก็เร็วกว่าหรือยิ่งไม่แตกเธรดยิ่งเร็วกว่า ผมไม่แน่ใจว่าจับเวลาถูกหรือเปล่าหรือทำอะไรผิด

ขอคำชี้แนะด้วยคับ ท่านจอมยุทธทั้งหลาย

import java.io.*;
import java.util.Scanner;
class threads extends Thread
{
static int numArray=0;
static int data=0,tmp=0;
static double [ ] myArray;
static int nInput;
static int check = 0;
threads(int input){
nInput=input;
}
public void run(){
//System.out.println(nInput +" -- "+ data+" -- "+(tmp)); //commentไว้ถ้าเอา comment ออกคือแสดงว่าได้ใส่ข้อมูลลง
int i; //Array ครบแล้ว
for (i = (nInput-data);i<nInput;i++ )
{
myArray[i] = Math.random();
//System.out.println(i + " : " + myArray[i]); //commentไว้ถ้าเอา comment ออกคือแสดงว่าได้ใส่ข้อมูลลง
} //Array ครบแล้ว
if (i==tmp)
{
for (int j = tmp;j<numArray;j++)
{
myArray[j] = Math.random();
//System.out.println(j + " : " + myArray[j]); //commentไว้ถ้าเอา comment ออกคือแสดงว่าได้ใส่ข้อมูลลง
//Array ครบแล้ว
}
}

//System.out.println("Compress : " + (nInput/data)); //commentไว้ถ้าเอา comment ออกคือแสดงว่าได้ใส่ข้อมูลลง
//Array ครบแล้ว
}
public static void main(String[] args)
{
try{
do
{
Scanner sc = new Scanner(System.in);
System.out.print("Input Array Data : ");
numArray = sc.nextInt(); //รับค่าเพิ่อนำมาสร้างขนาดของ Arrary
myArray = new double [numArray];
System.out.print("Input Threads : ");
int numThread = sc.nextInt(); //รักค่าเพื่อนำมาสร้างจำนวนเธรด
int j=1;
data = numArray/numThread; //คำนวนหา size ของแต่ละเธรด
tmp = data*numThread; //คำนวนหาจำนวนที่หารไม่ลงตัวของ data
long time1 = System.currentTimeMillis(); //ใช้เริ่มจับเวลา
Thread [] myThread = new Thread[numThread];
for (int i = 0; i<numThread ;i++) //วน for แตกเธรด
{
try{
myThread[i] = new threads(j*data); //ส่งค่าให้กับ constucter โดยค่าที่ส่งจะเป็นค่าจำนวนครั้งในการวน
myThread[i].start();
myThread[i].join();
j++;
}
catch(Exception e){
System.out.println("Error : #014356");
}
}
long time2 = System.currentTimeMillis(); //ใช้ในการจับเวลาหลังทำทุกอย่างเสร็จแล้ว
System.out.println("Time : " + (time2-time1)); //แสดงเวลาทั้งหมด

}
while (true);
}catch(Exception e){System.out.println("Error #10110");}//ปริ้น Error เล่นๆ
}
}

นี่ครับโค้ดผม ไม่เข้าตรงไหนถามได้เพราะผมก็งงงงงงงงง

#2 momomomo

momomomo

    Newbie

  • Members
  • Pip
  • 6 posts

Posted 14 July 2011 - 11:55 PM

ขอถามอีกอย่างครับ มันมีสูตรคำนวนหรือไม่ว่าถ้าข้อมูล หนึ่งล้านหรือสองล้านตัว ควรใช้กี่เทรด จึงจะดีที่สุด
เพราะถ้าใครบอกว่าเทรดยิ่งเยอะยิ่งเร็วงั้นผมก็แตกหนึ่งล้านเทรดสิคับจะได้ทำงานพร้อมกัน

และในความเป็นจิงถ้าเครื่องผมcore2แล้วแตกเทรดมันจะทำงานกี่coreคับ
ถ้ามันทำcoreเดียวแล้วเราจะเขียนยังไงให้มันทำงานสองcore

#3 Remixman

Remixman

    Newbie

  • Members
  • Pip
  • 32 posts

Posted 15 July 2011 - 12:00 AM

ผมยังไม่ได้ดูตัวโค้ดนะครับ แต่ขอตอบคร่าวๆตามที่ทราบมาดังนี้

1. การสร้าง Thread แต่ละครั้งมี overhead ครับ พูดง่ายๆคือเราต้องเสียเวลาเพิ่มขึ้นในการแตก Thread ออกมา ดังนั้นถ้าการคำนวนนั้นไม่ได้นานมาก การแตก Thread ออกมาอาจจะช้ากว่า

2. ส่วนใหญ่แล้วจะไม่สร้าง Thread เป็นจำนวนมากถึงขนาด 10 หรือ 100 ในการคำนวนบน CPU ครับ เพราะอย่างมากมันก็มีแค่ 4 core ที่สามารถประมวลผลได้ กลับกันยิ่งสร้าง Thread มากก็ยิ่งเสียเวลาไปกับการสร้างมากขึ้นไปอีก

ดังนั้นไม่จำเป็นว่าใช้ Thread แล้วต้องเร็วขึ้นเสมอครับ ลองเปลี่ยนเป็น 2-4 Thread ดูน่าจะดีกว่า อาจจะต้องดูด้วยว่าตัวโปรแกรมมันคำนวนนานพอที่การสร้าง Thread จะช่วยให้เร็วขึ้นมั้ย

#4 Remixman

Remixman

    Newbie

  • Members
  • Pip
  • 32 posts

Posted 15 July 2011 - 12:02 AM

 momomomo, on 14 July 2011 - 11:55 PM, said:

ขอถามอีกอย่างครับ มันมีสูตรคำนวนหรือไม่ว่าถ้าข้อมูล หนึ่งล้านหรือสองล้านตัว ควรใช้กี่เทรด จึงจะดีที่สุด
เพราะถ้าใครบอกว่าเทรดยิ่งเยอะยิ่งเร็วงั้นผมก็แตกหนึ่งล้านเทรดสิคับจะได้ทำงานพร้อมกัน

และในความเป็นจิงถ้าเครื่องผมcore2แล้วแตกเทรดมันจะทำงานกี่coreคับ
ถ้ามันทำcoreเดียวแล้วเราจะเขียนยังไงให้มันทำงานสองcore

กรณีที่มีข้อมูลมาก จำนวน Thread ควรอ้างอิงกลับ Logical Core ครับ

แต่ถ้าข้อมูลน้อยมาก อาจจะไม่ต้องใช้ Thread เลยด้วยซ้ำ

#5 mahajone

mahajone

    Member

  • Members
  • PipPip
  • 125 posts

Posted 16 July 2011 - 02:49 AM

มีหลายประการที่ทำให้การใช้เธรดจำนวนมากขึ้นแล้วโปรแกรมทำงานช้าลง ตอนแรกผมเห็นงานของแต่ละเธรดเป็นลักษณะ CPU bound คือใช้แต่ CPU resource
ไม่ได้ block รอ IO หรือมีการใช้ with/notify เลย และงานก็จบสั้นๆในตัวไม่แตกออกเป็นงานย่อย
ลักษณะงานอย่างนี้นี้เร็วสุดน่าจะเป็นใช้เธรดเท่าจำนวน core เยอะกว่านั้นคงไม่ดีขึ้นอะไรครับ.  

แต่พอผมอ่านโค้ดดูคร่าวๆแล้วรู้สึกงงๆ เหมือนโค้ดมันไม่ได้วัดสิ่งที่ต้องการหรือเปล่าครับ โค้ดด้านล่างนี้มันรอให้เธรดตัวที่พึ่ง start ไปรันให้จบก่อนค่อยไปวน start ตัวใหม่นะครับ
 myThread[i] = new threads(j*data);			
 myThread[i].start();
 myThread[i].join();

ถ้าอยากพิสูจน์ว่าเธรดมันช่วยให้งาน(บางประเภท)เสร็จเร็วขึ้นไหมน่าจะต้องให้มันรัน concurrent กันนะ




www.devguli.com

Edited by mahajone, 16 July 2011 - 02:51 AM.


#6 juacompe

juacompe

    Site Admin

  • Admin
  • PipPipPipPip
  • 2904 posts

Posted 16 July 2011 - 04:14 PM

multi-threading ไม่ได้เอาไว้แก้ IO bound เหรอครับ? ผมเข้าใจว่า multi-processing ต่างหากที่ใช้แก้ CPU bound ผมเข้าใจว่า Java แตกได้แต่ thread แตก process ไม่ได้

คำอธิบายเพิ่มเติม
- IO bound คือโปรแกรมมันช้าเพราะติด IO เช่นรอข้อมูลจาก Harddisk หรือ Network แต่ว่า CPU ยังไม่เต็ม 100%
- CPU bound คือโปรแกรมมันช้าเพราะ CPU มันไม่ไหวแล้ว ทั้งๆที่ IO ยังว่างอยู่

การแตก tread เป็นการเพิ่มการใช้งาน CPU อันนึงให้เต็มที่ยิ่งขึ้น ฉะนั้นถ้าโปรแกรมมันช้าเพราะ CPU วิ่ง 100% อยู่แล้ว การแตก tread ไม่น่าจะช่วยครับ เพราะการแตก thread เพิ่ม overhead มาอีก 2 อย่างคือ
1. thread scheduling (เลือก thread มาทำงาน)
2. context switching (สลับงานไปๆมาๆ)

อธิบาย thread scheduling, ลองนึกภาพว่าเรามีงานต้องทำ 6 งาน ส่งพรุ่งนี้ทุกงาน! แล้วเราก็ต้องมาเลือกว่าจะทำอันไหนก่อนหลัง บางอันง่าย บางอันยาก บางอันเสี่ยงที่จะซับซ้อนแล้วกลายเป็นเรื่องยุ่งยากจนทำไม่ทัน บางอันใช้เวลานานหน่อย แต่ไม่ค่อยมีความเสี่ยง แค่เลือกก็ปวดหัวแล้วจริงไหมครับ?

อธิบาย context switching, ลองนึกภาพว่าเราทำๆอะไรอยู่ แล้วก็มีหัวหน้ามาสั่งให้หยุดแล้วไปทำอีกงานก่อน แล้วยังทำไม่ทันเสร็จ หัวหน้าใหญ่ (ใหญ่กว่าตะกี้ 2 ขั้น) มาสั่งว่าหยุด แล้วมาทำงานผมก่อน แล้วหัวหน้าใหญ่ใหญ่ (ใหญ่ 5 เด้ง) มาสั่งว่า หยุดเด๋วนี้ มาทำอันนี้ก่อน ด่วนมาก แล้วเจ้าของบริษัทก็เข้ามาแทรกอีก พอทำอันนึงเสร็จ ก็ค่อยกลับไปทำงานที่หยุดค้างไว้ ก็ต้องมานั่งนึกว่าทำถึงไหนแล้ว แบบนี้แหละครับ context switching cost

จะ improve performance ของโปรแกรม อันดับแรกเลยคือหาให้เจอว่าอะไรทำให้มันช้า? แรมหมด หรือว่า CPU 100% หรือว่ากำลังต่อ Internet, พอเจอแล้วค่อยมาวิเคราะห์หาทางแก้ครับ

หวังว่าจะเป็นประโยชน์ไม่มากก็น้อยนะครับ

ถ้าผิดพลาดประการใด ต้องขออภัยด้วยนะครับ รบกวนผู้รู้แก้หรือเสริมให้ทีครับ ^/\^

#7 momomomo

momomomo

    Newbie

  • Members
  • Pip
  • 6 posts

Posted 16 July 2011 - 04:29 PM

 mahajone, on 16 July 2011 - 02:49 AM, said:

มีหลายประการที่ทำให้การใช้เธรดจำนวนมากขึ้นแล้วโปรแกรมทำงานช้าลง ตอนแรกผมเห็นงานของแต่ละเธรดเป็นลักษณะ CPU bound คือใช้แต่ CPU resource
ไม่ได้ block รอ IO หรือมีการใช้ with/notify เลย และงานก็จบสั้นๆในตัวไม่แตกออกเป็นงานย่อย
ลักษณะงานอย่างนี้นี้เร็วสุดน่าจะเป็นใช้เธรดเท่าจำนวน core เยอะกว่านั้นคงไม่ดีขึ้นอะไรครับ.  

แต่พอผมอ่านโค้ดดูคร่าวๆแล้วรู้สึกงงๆ เหมือนโค้ดมันไม่ได้วัดสิ่งที่ต้องการหรือเปล่าครับ โค้ดด้านล่างนี้มันรอให้เธรดตัวที่พึ่ง start ไปรันให้จบก่อนค่อยไปวน start ตัวใหม่นะครับ
 myThread[i] = new threads(j*data);			
 myThread[i].start();
 myThread[i].join();


คือ myThread[i].join(); ผมได้พิสูจน์แล้วว่ามันไม่ได้รอให้เธรดแรกเสร็จแล้วจึงทำเธรดต่อไปแต่ว่ามันจะรอทุกเธรดที่ได้มีการสั่ง join เสร็จทั้งหมดก่อนจึงทำงานต่อไป (ถ้าใช่หรือไม่ใช่อย่างไรชี้แนะด้วยคับ)



"ถ้าอยากพิสูจน์ว่าเธรดมันช่วยให้งาน(บางประเภท)เสร็จเร็วขึ้นไหมน่าจะต้องให้มันรัน concurrent กันนะ";



อยากรู้ว่าการทำ concurrent คืออะไรหรอคับ อธิบายหน่อยคับผม

#8 mahajone

mahajone

    Member

  • Members
  • PipPip
  • 125 posts

Posted 18 July 2011 - 02:13 AM

 juacompe, on 16 July 2011 - 04:14 PM, said:

multi-threading ไม่ได้เอาไว้แก้ IO bound เหรอครับ? ผมเข้าใจว่า multi-processing ต่างหากที่ใช้แก้ CPU bound ผมเข้าใจว่า Java แตกได้แต่ thread แตก process ไม่ได้

อืมมม ผมไม่แน่ใจว่าผมใช้คำว่า CPU bound ถูกต้องตามหลักหรือเปล่า แต่ที่อ่านๆมาเหมือนมันใช้บอกลักษณะของงานก็ได้นะครับ ประมาณว่างานนี้มันกิน CPU อย่างเดียวเลยเรียกว่า CPU bound.
ในที่นี้ผมไม่ได้ใช้ในความหมายว่า CPU ขึ้น 100% ทุก core แล้วประมาณนั้นครับ เลยพูดว่างานลักษณะนี้มี 2 core ก็แตก 2 thread น่าจะได้ผลดีที่สุด.

ที่เคยใช้จริงมาก็คือแตกเธรดมาเพิ่มโอกาสให้ CPU ได้ทำงานอื่นขณะที่มีเธรด wait รอ IO อยู่ ตามที่คุณ juacompe บอกละครับ
แต่ก็เคยอ่านเห็นวิธีการแบบว่าแตกงานที่เป็น CPU bound ออกเป็นงานย่อยๆแล้วกระจายไปประมวลผลใน thread pool เพื่อใช้งาน multi-cores ให้เต็มที่ อย่างพวก Fork/join framework.


momomomo said:

อยากรู้ว่าการทำ concurrent คืออะไรหรอคับ อธิบายหน่อยคับผม

ถามนิยามนี่ผมก็ไม่ค่อยแม่นนะ แต่ในทางปฏิบัติ หลายเธรดรันบน multi-processors มันรัน concurrent กันคือรันพร้อมๆกันครับ
คล้ายๆว่า บางประเภทงาน สองคนแบ่งทำงานพร้อมๆกันคนละครึ่งอาจจะเสร็จเร็วขึ้น(มีหลายเงื่อนไขที่แบ่งกันทำมันอาจจะช้าลงก็ได้)
ผมเข้าใจว่าโค้ดคุณตอนนี้ มันเหมือนคนนึงทำงานเสร็จก่อนแล้วอีกคนค่อยเริ่มทำนะครับ

#9 juacompe

juacompe

    Site Admin

  • Admin
  • PipPipPipPip
  • 2904 posts

Posted 18 July 2011 - 02:41 AM

ขอบคุณคุณ mahajone ที่มาเอา fork/join มาแบ่งปันครับ เท่าที่อ่านผ่านๆ เข้ามาใน Java 7 น่าจะออกมาเพื่อทำ multi processing ที่หายไปใน Java ครับ

คำว่าหายไปไม่ได้แปลว่า Java ไม่ได้ใช้มากกว่า 1 core ในการทำงานนะครับ ภายใต้ JVM คงมีการใช้ แค่ developer ไม่สามารถควบคุมมันได้เฉยๆ (เพราะก่อน Java 7 เราแตกได้แต่ thread)

ส่วนเรื่อง CPU bound น่าจะเอาไปใช้ประยุกต์เรียกประเภทของงานได้ไม่น่าแปลกใจครับ bound แปลว่าขอบ CPU bound ก็แปลคร่าวๆว่ามันทำงานได้เร็วสุดขอบ CPU (หมายถึงงานประเภทนั้นไม่มี overhead ของ IO ที่น่าจะทำให้ใช้ CPU ได้ไม่เต็มที่)

ส่วนความหมายของ concurrent ก็อย่างที่คุณ mahajone อธิบายไว้ครับ คือ run หลายๆงานพร้อมๆกัน




0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users