22. december 2003 - 16:24Der er
15 kommentarer og 2 løsninger
Hvorfor er denne operation så dyr
Jeg har en server med et antal arbejder-tråde. Arbejderne aktiveres ved at lægge noget i deres "handoff-box" (en QueueArray-instans med 1 plads). Så længe der ikke er noget at lave, står arbejderen altså blokkeret i QueueArray.remove(), der er en synkroniseret metode, som anvender wait(), så længe køen er tom. QueueArray ses nedenfor.
I et server-scenarie står remove() der kalder waitWhileEmpty(), altså wait(), for over 60% af CPU-forbruget, altså over 60% af den tid, den aktuelle tråd faktisk er på CPU'en, hvordan i al verden kan det være !?
package com.shockwaved.octane.shared.util;
public class QueueArray { private Object[] queue; private int capacity; private int size; private int head; private int tail;
public QueueArray(int cap) { capacity = (cap > 0) ? cap : 1; queue = new Object[capacity]; head = 0; tail = 0; size = 0; }
public int capacity() { return capacity; }
public synchronized int size() { return size; }
public synchronized boolean isEmpty() { return (size == 0); }
public synchronized boolean isFull() { return (size == capacity); }
remove er synkroniseret, så når du venter *indeni* remove er din instans af QueArray konstant låst, så det skulle slet ikke være muligt at kalde add metoden.
men det er det åbenbart. mon ikke arne_v kan forklare hvorfor :-))
Det synes jeg så bestemt også. Baggrunden for tallet er en test jeg har kørt sammen med Borlands Profiler fra OptimizeIt-suiten. Testen gik ud på at 20 klienter hver, hvert 3. sekund har sendt en besked til serveren, som så er returneret til alle 20. CPU-målingen varede i ca. 5 minutter og blev gennemført et stykke tid efter at testen faktisk var startet således, at JVM har nået at kompilere det nødvendige til nativ kode.
Serveren kørte med 5 worker-tråde og ved modtagelse af beskeder fra klienterne er der foretaget en del beregninger, gået gennem kritiske regioner mv., så der er ikke tale om blot echo.
Belastningen har altså i snit været 6,67 modtagede beskeder i sekundet og 133,33 sendte.
Kunne du prøve at køre mit test program på din box og se hvad det giver:
public class Test { public static void main(String[] args) throws Exception { QueueArray qa = new QueueArray(1); T1[] t1 = new T1[100]; T2[] t2 = new T2[100]; for(int t = 0; t < 100; t++) { t1[t] = new T1(qa); t2[t] = new T2(qa); } long start = System.currentTimeMillis(); for(int t = 0; t < 100; t++) { t1[t].start(); t2[t].start(); } for(int t = 0; t < 100; t++) { t1[t].join(); t2[t].join(); } long end = System.currentTimeMillis(); System.out.println("done in " + (end - start)); } }
Description of CPU usage for thread Worker: 2 100.0% - 8255 ms - Worker$1.run() 100.0% - 8255 ms - Worker.access$000() 100.0% - 8255 ms - Worker.start() 62.83% - 5187 ms - QueueArray.remove() 19.3% - 1594 ms - Worker.handleWrite() 15.78% - 1303 ms - Worker.handleSelectionKey() 1.7% - 141 ms - BalancedWorkerPool.releaseWorker()
Ok, jeg har fundet ud af det. Profileren måler program eksekveringstid, ikke CPU-tid, hvorfor tiden brugt i waitWhileEmpty() angiver hvor lang tid tråden har sovet. Tak for hjælpen og besværet.
I øvrigt udførte jeg en lille test, hvor serveren absolut intet lavede så længe CPU-belastningen måltes. Resultatet blev, at alle workere havde brugt 100% tid i waitWhileEmpty()
Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.