Web uygulamamın performansları hakkında endişelenerek, performansla ilgili olarak "if / else" veya switch ifadelerinden hangisinin daha iyi olduğunu merak ediyorum.
if
vb. Yerine tablo aramaları kullanılarak optimizasyon yapılabilir .
Web uygulamamın performansları hakkında endişelenerek, performansla ilgili olarak "if / else" veya switch ifadelerinden hangisinin daha iyi olduğunu merak ediyorum.
if
vb. Yerine tablo aramaları kullanılarak optimizasyon yapılabilir .
Yanıtlar:
Bu, kötü olan mikro optimizasyon ve erken optimizasyondur. Daha ziyade, söz konusu kodun okunabilirliği ve sürdürülebilirliği konusunda endişelenin. Birbirine if/else
yapıştırılmış ikiden fazla blok varsa veya boyutu tahmin edilemezse, o zaman bir switch
ifade düşünebilirsiniz .
Alternatif olarak, Polimorfizm'i de alabilirsiniz . Önce bir arayüz oluşturun:
public interface Action {
void execute(String input);
}
Ve bazılarında tüm uygulamaları yakalayın Map
. Bunu statik veya dinamik olarak yapabilirsiniz:
Map<String, Action> actions = new HashMap<String, Action>();
Son olarak if/else
veya yerine switch
böyle bir şey koyun (boş işaretçiler gibi önemsiz kontrolleri bir kenara bırakın):
actions.get(name).execute(input);
Bu belki daha microslower olmak if/else
veya switch
, fakat kod en azından çok daha iyi muhafaza edilebilir.
Web uygulamalarından bahsederken, HttpServletRequest#getPathInfo()
eylem anahtarı olarak kullanabilirsiniz (sonunda, bir eylem bulunana kadar yol bilgisinin son bölümünü bir döngüde ayırmak için biraz daha kod yazın). Burada benzer cevaplar bulabilirsiniz:
Genel olarak Java EE web uygulaması performansı hakkında endişeleniyorsanız, bu makaleyi de yararlı bulabilirsiniz . Ham Java kodunu yalnızca (mikro) optimize etmekten çok daha fazla performans kazancı sağlayan başka alanlar da vardır .
Erken optimizasyonun kaçınılması gereken bir şey olduğu fikrine tamamen katılıyorum.
Ancak Java sanal makinesinin switch () 'ler için kullanılabilecek özel bayt kodlarına sahip olduğu doğrudur.
WM Spesifikasyonuna bakın (arama anahtarı ve tablo anahtarı )
Dolayısıyla, kod performans CPU grafiğinin bir parçasıysa, bazı performans kazanımları olabilir.
Bir if / else veya bir anahtarın performans sıkıntılarınızın kaynağı olması son derece düşük bir ihtimaldir. Performans sorunları yaşıyorsanız, yavaş noktaların nerede olduğunu belirlemek için önce bir performans profili analizi yapmalısınız. Erken optimizasyon, tüm kötülüklerin köküdür!
Yine de, Java derleyici optimizasyonları ile anahtar ve if / else'in göreceli performansı hakkında konuşmak mümkündür. Öncelikle, Java'da switch ifadelerinin çok sınırlı bir etki alanında (tamsayılar) çalıştığını unutmayın. Genel olarak, bir switch ifadesini aşağıdaki gibi görüntüleyebilirsiniz:
switch (<condition>) {
case c_0: ...
case c_1: ...
...
case c_n: ...
default: ...
}
nerede c_0
,, c_1
... ve c_N
switch ifadesinin hedefleri olan ve <condition>
bir tamsayı ifadesine çözülmesi gereken integral sayılardır .
Bu küme "yoğun" ise - yani (max (c i ) + 1 - min (c i )) / n> α, burada 0 <k <α <1, burada k
bazı ampirik değerlerden daha büyük, a Son derece verimli olan atlama tablosu oluşturulabilir.
Bu küme çok yoğun değilse, ancak n> = β ise, bir ikili arama ağacı hedefi O (2 * log (n)) içinde bulabilir ve bu da yine de etkilidir.
Diğer tüm durumlar için, bir switch ifadesi tam olarak eşdeğer if / else ifadeleri dizisi kadar etkilidir. Α ve β'nin kesin değerleri bir dizi faktöre bağlıdır ve derleyicinin kod optimizasyon modülü tarafından belirlenir.
Son olarak, elbette, etki alanı <condition>
tamsayı değilse, switch deyimi tamamen işe yaramaz.
Anahtarı kullanın!
If-else-bloklarını korumaktan nefret ediyorum! Bir test yapın:
public class SpeedTestSwitch
{
private static void do1(int loop)
{
int temp = 0;
for (; loop > 0; --loop)
{
int r = (int) (Math.random() * 10);
switch (r)
{
case 0:
temp = 9;
break;
case 1:
temp = 8;
break;
case 2:
temp = 7;
break;
case 3:
temp = 6;
break;
case 4:
temp = 5;
break;
case 5:
temp = 4;
break;
case 6:
temp = 3;
break;
case 7:
temp = 2;
break;
case 8:
temp = 1;
break;
case 9:
temp = 0;
break;
}
}
System.out.println("ignore: " + temp);
}
private static void do2(int loop)
{
int temp = 0;
for (; loop > 0; --loop)
{
int r = (int) (Math.random() * 10);
if (r == 0)
temp = 9;
else
if (r == 1)
temp = 8;
else
if (r == 2)
temp = 7;
else
if (r == 3)
temp = 6;
else
if (r == 4)
temp = 5;
else
if (r == 5)
temp = 4;
else
if (r == 6)
temp = 3;
else
if (r == 7)
temp = 2;
else
if (r == 8)
temp = 1;
else
if (r == 9)
temp = 0;
}
System.out.println("ignore: " + temp);
}
public static void main(String[] args)
{
long time;
int loop = 1 * 100 * 1000 * 1000;
System.out.println("warming up...");
do1(loop / 100);
do2(loop / 100);
System.out.println("start");
// run 1
System.out.println("switch:");
time = System.currentTimeMillis();
do1(loop);
System.out.println(" -> time needed: " + (System.currentTimeMillis() - time));
// run 2
System.out.println("if/else:");
time = System.currentTimeMillis();
do2(loop);
System.out.println(" -> time needed: " + (System.currentTimeMillis() - time));
}
}
switch
?
Java bayt kodunda 2 tür Switch ifadesi olduğunu okuduğumu hatırlıyorum. ('Java Performance Tuning'de olduğunu düşünüyorum Biri, çalıştırılacak kodun ofsetini bilmek için switch deyiminin tamsayı değerlerini kullanan çok hızlı bir uygulamadır. Bu, tüm tam sayıların ardışık ve iyi tanımlanmış bir aralıkta olmasını gerektirir. Bir Enum'un tüm değerlerini kullanmanın da o kategoriye gireceğini tahmin ediyorum.
Yine de diğer birçok postere katılıyorum ... Bu çok sıcak bir kod olmadığı sürece, bunun için endişelenmek için erken olabilir.
switch
birkaç farklı yol uygular , bazıları diğerlerinden daha verimli. Genel olarak, verimlilik basit bir " if
merdiven" ten daha kötü olmayacaktır , ancak yeterince varyasyon vardır (özellikle JITC ile) bundan çok daha kesin olmanın zor olduğu.
Cliff Click'in 2009 Java One konuşmasında Modern Donanımda Bir Kilitlenme Kursu'na göre :
Günümüzde performans, bellek erişim kalıplarının hakimiyetindedir. Önbellek hakimiyeti kaçırır - bellek yeni disktir. [Slayt 65]
Tam slaytlarını buradan alabilirsiniz .
Cliff, CPU'nun kayıt yeniden adlandırma, dal tahmini ve spekülatif yürütme yapmasına rağmen, iki önbellek eksikliğinden dolayı bloke etmek zorunda kalmadan önce yalnızca 4 saat döngüsünde 7 işlemi başlatabildiğini gösteren bir örnek (Slayt 30'da bitirme) verir. Döndürülecek 300 saat döngüsü.
Bu yüzden programınızı hızlandırmak için bu tür küçük sorunlara değil, "SOAP → XML → DOM → SQL → ..." gibi gereksiz veri biçimi dönüşümleri yapıp yapmadığınız gibi daha büyük sorunlara bakmanız gerektiğini söylüyor. "bu" tüm verileri önbellekten geçirir ".
Benim testimde daha iyi performans Windows7'de ENUM> MAP> SWITCH> IF / ELSE IF'dir.
import java.util.HashMap;
import java.util.Map;
public class StringsInSwitch {
public static void main(String[] args) {
String doSomething = null;
//METHOD_1 : SWITCH
long start = System.currentTimeMillis();
for (int i = 0; i < 99999999; i++) {
String input = "Hello World" + (i & 0xF);
switch (input) {
case "Hello World0":
doSomething = "Hello World0";
break;
case "Hello World1":
doSomething = "Hello World0";
break;
case "Hello World2":
doSomething = "Hello World0";
break;
case "Hello World3":
doSomething = "Hello World0";
break;
case "Hello World4":
doSomething = "Hello World0";
break;
case "Hello World5":
doSomething = "Hello World0";
break;
case "Hello World6":
doSomething = "Hello World0";
break;
case "Hello World7":
doSomething = "Hello World0";
break;
case "Hello World8":
doSomething = "Hello World0";
break;
case "Hello World9":
doSomething = "Hello World0";
break;
case "Hello World10":
doSomething = "Hello World0";
break;
case "Hello World11":
doSomething = "Hello World0";
break;
case "Hello World12":
doSomething = "Hello World0";
break;
case "Hello World13":
doSomething = "Hello World0";
break;
case "Hello World14":
doSomething = "Hello World0";
break;
case "Hello World15":
doSomething = "Hello World0";
break;
}
}
System.out.println("Time taken for String in Switch :"+ (System.currentTimeMillis() - start));
//METHOD_2 : IF/ELSE IF
start = System.currentTimeMillis();
for (int i = 0; i < 99999999; i++) {
String input = "Hello World" + (i & 0xF);
if(input.equals("Hello World0")){
doSomething = "Hello World0";
} else if(input.equals("Hello World1")){
doSomething = "Hello World0";
} else if(input.equals("Hello World2")){
doSomething = "Hello World0";
} else if(input.equals("Hello World3")){
doSomething = "Hello World0";
} else if(input.equals("Hello World4")){
doSomething = "Hello World0";
} else if(input.equals("Hello World5")){
doSomething = "Hello World0";
} else if(input.equals("Hello World6")){
doSomething = "Hello World0";
} else if(input.equals("Hello World7")){
doSomething = "Hello World0";
} else if(input.equals("Hello World8")){
doSomething = "Hello World0";
} else if(input.equals("Hello World9")){
doSomething = "Hello World0";
} else if(input.equals("Hello World10")){
doSomething = "Hello World0";
} else if(input.equals("Hello World11")){
doSomething = "Hello World0";
} else if(input.equals("Hello World12")){
doSomething = "Hello World0";
} else if(input.equals("Hello World13")){
doSomething = "Hello World0";
} else if(input.equals("Hello World14")){
doSomething = "Hello World0";
} else if(input.equals("Hello World15")){
doSomething = "Hello World0";
}
}
System.out.println("Time taken for String in if/else if :"+ (System.currentTimeMillis() - start));
//METHOD_3 : MAP
//Create and build Map
Map<String, ExecutableClass> map = new HashMap<String, ExecutableClass>();
for (int i = 0; i <= 15; i++) {
String input = "Hello World" + (i & 0xF);
map.put(input, new ExecutableClass(){
public void execute(String doSomething){
doSomething = "Hello World0";
}
});
}
//Start test map
start = System.currentTimeMillis();
for (int i = 0; i < 99999999; i++) {
String input = "Hello World" + (i & 0xF);
map.get(input).execute(doSomething);
}
System.out.println("Time taken for String in Map :"+ (System.currentTimeMillis() - start));
//METHOD_4 : ENUM (This doesn't use muliple string with space.)
start = System.currentTimeMillis();
for (int i = 0; i < 99999999; i++) {
String input = "HW" + (i & 0xF);
HelloWorld.valueOf(input).execute(doSomething);
}
System.out.println("Time taken for String in ENUM :"+ (System.currentTimeMillis() - start));
}
}
interface ExecutableClass
{
public void execute(String doSomething);
}
// Enum version
enum HelloWorld {
HW0("Hello World0"), HW1("Hello World1"), HW2("Hello World2"), HW3(
"Hello World3"), HW4("Hello World4"), HW5("Hello World5"), HW6(
"Hello World6"), HW7("Hello World7"), HW8("Hello World8"), HW9(
"Hello World9"), HW10("Hello World10"), HW11("Hello World11"), HW12(
"Hello World12"), HW13("Hello World13"), HW14("Hello World4"), HW15(
"Hello World15");
private String name = null;
private HelloWorld(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void execute(String doSomething){
doSomething = "Hello World0";
}
public static HelloWorld fromString(String input) {
for (HelloWorld hw : HelloWorld.values()) {
if (input.equals(hw.getName())) {
return hw;
}
}
return null;
}
}
//Enum version for betterment on coding format compare to interface ExecutableClass
enum HelloWorld1 {
HW0("Hello World0") {
public void execute(String doSomething){
doSomething = "Hello World0";
}
},
HW1("Hello World1"){
public void execute(String doSomething){
doSomething = "Hello World0";
}
};
private String name = null;
private HelloWorld1(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void execute(String doSomething){
// super call, nothing here
}
}
/*
* http://stackoverflow.com/questions/338206/why-cant-i-switch-on-a-string
* https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-3.html#jvms-3.10
* http://forums.xkcd.com/viewtopic.php?f=11&t=33524
*/
Time taken for String in Switch :3235 Time taken for String in if/else if :3143 Time taken for String in Map :4194 Time taken for String in ENUM :2866
Blokların çoğu switch
ve çoğu için if-then-else
, performansla ilgili kayda değer veya önemli endişeler olduğunu hayal edemiyorum.
Ama switch
olay şu: eğer bir blok kullanıyorsanız , onun kullanımı derleme zamanında bilinen bir dizi sabitten alınan bir değeri açtığınızı gösterir. Bu durumda, sabit-özel yöntemlerle switch
kullanabiliyorsanız , gerçekten hiç ifadeler kullanmamalısınız enum
.
Bir deyimle karşılaştırıldığında switch
, enum, bakımı daha kolay olan daha iyi tür güvenliği ve kod sağlar. Numaralandırmalar, sabitler kümesine bir sabit eklenirse kodunuz yeni değer için sabite özgü bir yöntem sağlamadan derlenmeyecek şekilde tasarlanabilir. Öte yandan, case
bir switch
bloğa yeni bir blok eklemeyi unutmak bazen yalnızca bloğunuzu bir istisna atacak şekilde ayarlayacak kadar şanslıysanız, çalışma zamanında yakalanabilir.
Arasındaki performans switch
bir ve enum
sabit özgü yöntemiyle önemli ölçüde farklı olmamalıdır, ama ikincisi daha okunabilir, daha güvenli ve bakımı kolaydır.