2012-10-17 27 views
6

Bu yüzden bir URL'ye çok sayıda (10.000'den fazla) GET isteği göndermesi gerektiğini belirten bir programa sahibim ve olabildiğince hızlı olmamız gerekiyor. Programı ilk oluşturduğumda bağlantıları bir forma soktum ama gerçekten yavaştı çünkü devam etmeden önce her bağlantının tamamlanmasını beklemesi gerekecekti. Daha hızlı yapmak istedim, bu yüzden iplikleri kullanmayı denedim ve biraz daha hızlı yaptı ama yine de memnun değilim.Konuları Anlar + Eşzamansız

Bu konuda gitmenin ve gerçekten hızlı olmanın doğru yolunun asenkron bağlantı ve tüm URL'lere bağlanması olduğunu tahmin ediyorum. Bu doğru bir yaklaşım mı?

Ayrıca, iş parçacığı ve nasıl çalıştığını anlamaya çalışıyorum ama anlayamıyorum. Üzerinde bulunduğum bilgisayarın bir Intel Core i7-3610QM dört çekirdekli işlemci var. Intel'in bu işlemcinin özellikleri için web sitesine göre, 8 iş parçacığına sahip. Bu, bir Java uygulamasında 8 iş parçacığı oluşturabildiğim anlamına geliyor ve hepsi aynı anda mı çalışacak? 8'den fazla ve hız artışı olmayacak mı?

Sayı, "Performans" sekmesi altındaki görev yöneticisinde "Konular" ın yanında neyi temsil ediyor? Şu anda, görev yöneticim 1000'den fazla "Konular" gösteriyor. Neden bu sayı ve tüm işlemcim destekliyorsa 8'i nasıl geçebilir? Ayrıca, programımı 500 iş parçacığıyla bir sınama olarak denediğimde, görev yöneticisindeki sayının 500 arttığını, ancak bunun yerine 8 iş parçacığı kullanacak şekilde aynı hıza sahip olduğunu fark ettim. Bu yüzden sayı, Java uygulamamda kullandığım iş parçacığı sayısına göre artarsa, hız neden aynıdır?

Ayrıca, Java'da küçük bir test yapmayı denedim, ancak çıktı bana mantıklı gelmiyor.

Bu gibi bir çıktı üretir
import java.text.SimpleDateFormat; 
import java.util.Date; 

public class Test { 

    private static int numThreads = 3; 
    private static int numLoops = 100000; 
    private static SimpleDateFormat dateFormat = new SimpleDateFormat("[hh:mm:ss] "); 

    public static void main(String[] args) throws Exception { 

     for (int i=1; i<=numThreads; i++) { 
      final int threadNum = i; 
      new Thread(new Runnable() { 
       public void run() { 
        System.out.println(dateFormat.format(new Date()) + "Start of thread: " + threadNum); 
        for (int i=0; i<numLoops; i++) 
         for (int j=0; j<numLoops; j++); 
        System.out.println(dateFormat.format(new Date()) + "End of thread: " + threadNum); 
      } 
      }).start(); 
      Thread.sleep(2000); 
     } 

    } 
} 

:

[09:48:51] Start of thread: 1 
[09:48:53] Start of thread: 2 
[09:48:55] Start of thread: 3 
[09:48:55] End of thread: 3 
[09:48:56] End of thread: 1 
[09:48:58] End of thread: 2 

Neden ve hemen üçüncü iplik başlangıç ​​biter ise birinci ve ikinci 5 saniye her sürebilir İşte benim Testi sınıftır? Eğer 3 iş parçacığı daha eklerseniz, aynı şey 2'nin üstündeki tüm iş parçacıkları için de geçerlidir.

Eğer bu çok uzun bir okuma olsaydı üzgünüm, çok fazla soru vardı. Şimdiden teşekkürler.

cevap

9

İşlemciniz 8 çekirdek değil, iş parçacığına sahiptir. Bu aslında herhangi bir anda sadece 8 şeyin çalışabileceği anlamına geliyor. Bu sadece 8 iş parçacığıyla sınırlı olduğun anlamına gelmez.

Bir iş parçacığı bir URL ile bağlantıyı eşzamanlı olarak açıyorsa, uzak sunucunun geri dönmesini beklerken sık sık uyur. Bu iş parçacığı uyurken diğer konuları iş yapıyor olabilir. 500 iş parçacığınız varsa ve tüm 500 kişi uyuyorsa, CPU'nuzun çekirdeklerini kullanmıyorsunuzdur.

Kapak tarafında, 500 iş parçacığınız varsa ve tüm 500 iş parçacığı bir şeyler yapmak istiyorsa, hepsi aynı anda çalışamaz. Bu senaryoyu ele almak için özel bir araç var. İşlemciler (ya da daha büyük olasılıkla işletim sistemi ya da ikisinin bazı kombinasyonları) herhangi bir zamanda hangi iş parçacıklarının aktif olarak işlemci üzerinde çalışacağını belirleyen bir programlayıcıya sahiptir. Bu zamanlayıcıların nasıl çalıştığını kontrol eden birçok farklı kural ve bazen rastgele etkinlik vardır. Bu, yukarıdaki örnekte, iş parçacığının (3) neden her zaman ilkini bitirdiğini açıklayabilir. Belki de programcı iş parçacığı 3'ü tercih ediyor çünkü ana iş parçacığı tarafından planlanan en son iş parçacığı olduğu için, davranışı bazen tahmin etmek imkansız olabilir.

Şimdi performansla ilgili sorularınızı yanıtlayın.Eğer bir bağlantıyı açmak hiç bir zaman bir uykuyu etkilemiyorsa, eşzamanlı veya eşzamansız bir şekilde ele alıp almamanız önemli değildir, 8 iş parçacığının üzerinde herhangi bir performans artışı elde edemezsiniz. Gerçekte, bir bağlantıyı açmada çoğu zaman uyumak için harcanır. Eşzamansız ve senkronize arasındaki fark, uyumak için harcanan sürenin nasıl ele alınacağıdır. Teorik olarak, ikisi arasında neredeyse eşit performans elde edebilmeniz gerekir.

Çok iş parçacıklı bir modelde, çekirdekler olduğundan daha çok iş parçacığı oluşturabilirsiniz. İş parçacığı uyuduğu zaman, diğer iş parçacıklarının işe yaramasına izin verirler. Bu bazen işlemek için daha kolay olabilir, çünkü iş parçacıkları arasında herhangi bir zamanlama veya etkileşim yazmanız gerekmez.

Eşzamansız bir modelde, yalnızca çekirdek başına tek bir iş parçacığı oluşturabilirsiniz. Eğer bu iş parçacığının uyuma ihtiyacı varsa, o zaman uyumuyor, ama aslında bir sonraki bağlantıya geçiş yapmak için bir kodun olması gerekiyor. Eğer çekirdek gerekenden fazla konuları sahip olmanın hiçbir anlamı yoktur bu yüzden hiçbir noktada iplik uyku yapar

while (!connectionsList.isEmpty()) { 
    for(Connection connection : connectionsList) { 

    if connection.getState() == READY_FOR_A { 
     connection.stepA(); 
     //this method should return immediately and the connection 
     //should go into the waiting state for some time before going 
     //into the READY_FOR_B state 
    } 
    if connection.getState() == READY_FOR_B { 
     connection.stepB(); 
     //same immediate return behavior as above 
    } 
    if connection.getState() == READY_FOR_C { 
     connection.stepC(); 
     //same immediate return behavior as above 
    } 
    if connection.getState() == WAITING { 
     //Do nothing, skip over 
    } 
    if connection.getState() == FINISHED { 
     connectionsList.remove(connection); 
    } 
    } 
} 

Uyarı: Örneğin, bir bağlantı (A, B, C) açılması üç adım vardır varsayalım. Nihayetinde, eşzamanlı bir yaklaşımla mı yoksa asenkronize bir yaklaşımla mı gidileceği, kişisel tercih meselesidir. Sadece mutlak aşırı uçlarda, ikisi arasında performans farklılıkları olacak ve uygulamanızda darboğaz olduğu noktaya ulaşmak için uzun bir profil oluşturmanız gerekecektir.

Çok fazla iş parçacığı yarattığınız ve herhangi bir performans kazancı elde etmediğiniz anlaşılıyor. Bunun birkaç nedeni olabilir.

  • Bağlantı kurmanızın aslında uyumadığı olasıdır; bu durumda, 8 iş parçacığından sonra bir performans artışı görmeyi beklemezdim. Bunun büyük olasılıkla olduğunu sanmıyorum.
  • Tüm iş parçacıklarının ortak bir paylaşılan kaynak kullanıyor olması olasıdır. Bu durumda, diğer iş parçacığı çalışmıyor çünkü uyku iş parçacığı paylaşılan kaynağa sahip. Bütün konuların paylaştığı bir nesne var mı? Bu nesnede herhangi bir senkronize yöntem var mı?
  • Kendi senkronizasyonunuz olması mümkündür. Bu yukarıda belirtilen sorunu yaratabilir.
  • Her bir iş parçacığının, birden fazla iş parçacığı kullanarak kazandığınız yardımı yenen bir tür kurulum/ayırma işi yapmak zorunda olması olasıdır.

olsam parçacığı bazı ufacık numarası (20) ile çalıştırırken senin uygulama profil JVisualVM gibi bir araç kullanmak. JVisualVM, iş parçacığı çalışırken, engellerken veya uyurken gösterilecek güzel bir renkli iplik grafiğine sahiptir. Bu, iş parçacığı sayısının sahip olduğunuz çekirdek sayısından daha az olduğunu görebildiğiniz gibi iş parçacığı/çekirdek ilişkisini anlamanıza yardımcı olacaktır. Ayrıca çok sayıda engellenmiş ileti görürseniz, bu durum sizi darboğazınıza yönlendirmenize yardımcı olabilir (çok sayıda iş parçacığı görüyorsanız, bu noktada bir iş parçacığı oluşturmak için JVisualVM'yi kullanın ve iş parçacıklarının nelerin engellendiğini görün).

+0

Yanıt için teşekkürler. Düzenleme ... – user1203585

+0

Ahh, aslında bu yorumu düzenleyemiyorum ... 5 dakika sınırı ... "Tüm parçacıkların paylaştığı herhangi bir nesne var mı? Bu nesnede herhangi bir senkronize yöntem var mı?" Tüm iş parçacıklarım aynı şeyi yapıyor: Bir URL nesnesini başlatır ve bağlantıyı bir proxy ile açar. URLConnection bağlan ve okuma zaman aşımlarını ayarlar. Daha sonra URLConnection'dan okumak için bir BufferedReader ve InputStreamReader kullanır. Son olarak, bir metin dosyasına bir kelime yazar. Her iş parçacığının yaptığı budur ve bu iş parçacıklarının 500'ü bunu hızlandırıyor gibi görünmüyor:/ – user1203585

+1

Biraz etrafta kazdım. Java'nın boyut olarak sınırlı bir temel bağlantı havuzuna sahip olduğundan şüpheleniyorum. Http.maxConnections adlı bir ağ özelliği vardır. [Http://docs.oracle.com/javase/1.4.2/docs/guide/net/properties.html]. Varsayılan 5'dir. Bu, 5'ten fazla bağlantı açık olduktan sonra, aynı 5 altta yatan soketi (paylaşılan kaynaklar) ve sonra açacağınız tüm bağlantıları engelleyecektir. Yine, bunu onaylamak için JVisualVM'yi kullanabilirsiniz. – Pace

1

Bazı kavramlar: Sistemin birçok iş parçacığı olabilir, ancak bunlardan sadece (sizin durumunuzda en fazla 8) bazı zaman herhangi bir noktada CPU üzerindeki "planlanan" olacak

. Yani, paralel çalışan 8 iş parçacığından daha fazla performans elde edemezsiniz. Aslında, iş parçacığı oluşturma, imha etme ve yönetme ile ilgili çalışmalar nedeniyle, iş parçacığı sayısını artırdıkça performans muhtemelen düşecektir.

Konular farklı durumlarda olabilir: http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/Thread.State.html Bu durumların dışında, RUNNABLE iş parçacığı bir işlemci zaman dilimi almak için ayağa kalkar. İşletim Sistemi, CPU zamanının iş parçacığına atanmasına karar verir. 1000 iş parçacığına sahip düzenli bir sistemde, belirli bir iş parçacığı CPU zamanı ve CPU'da ne kadar süre kaldığında tamamen tahmin edilemez. soruna Eğer çözme Hakkında

:

görünüyorsunuz doğru çözüm anladım etmiş - paralel asenkron ağ isteklerini yapma. Ancak, pratik olarak 10000+ iş parçacığı başlangıcı ve birçok ağ bağlantısının aynı anda başlatılması, sistem kaynaklarında bir zorlanma olabilir ve yalnızca işe yaramayabilir. Bu post, Java kullanarak eşzamansız G/Ç için birçok öneriye sahiptir. (İpucu: Kabul edilen cevaba bakmayın)

0

Bu çözüm, 10k isteklerini olabildiğince hızlı hale getirmeye çalışmakla ilgili genel sorundan daha belirleyicidir. Java HTTP kitaplıklarını terk etmenizi ve bunun yerine Apache'nin HttpClient kullanmasını öneririm. Kullanışlı olabilecek performansı en üst düzeye çıkarmak için birkaç suggestions var. Apache HttpClient kütüphanesinin genel olarak daha hızlı, daha hafif ve daha az yük olduğunu duydum.