2015-01-07 8 views
5

Brian Goetz tarafından yapılan bazı yorumlarda, seri hale getirilebilen lambdaların "basılmamış lambdalara kıyasla önemli ölçüde daha yüksek performans maliyetlerine sahip olduğunu" okudum.Java 8'de serialize edilebilir lambdaların performansı

Şu an merak ediyorum: Nerede bu yük ve bunun nedeni nedir? Sadece bir lambda ya da aynı zamanda çağrışımın gerçekleşmesini etkiler mi?

Aşağıdaki kodda, her iki durumda da (CallExistingInstance() ve callWithNewInstance()) "MyFunction" işlevinin ya da yalnızca ikinci durumun serileştirilebilirliğinden etkilenir mi?

interface MyFunction<IN, OUT> { 
    OUT call(IN arg); 
} 

void callExistingInstance() { 

    long toAdd = 1; 
    long value = 0; 

    final MyFunction<Long, Long> adder = (number) -> number + toAdd; 

    for (int i = 0; i < LARGE_NUMBER; i++) { 
     value = adder.call(value); 
    } 
} 

void callWithNewInstance() { 

    long value = 0; 

    for (int i = 0; i < LARGE_NUMBER; i++) { 
     long toAdd = 1; 

     MyFunction<Long, Long> adder = (number) -> number + toAdd; 

     value = adder.call(value); 
    } 
} 

cevap

3

Performans vuruşu, seri hale getirdiğinizde/serttiğinizde ve siz başlattığınızda gelir. Sadece ikinci örneğiniz isabet alır. Pahalı olmasının nedeni, serpiştirdiğinizde, lambda'nızın temel sınıfının, bir düz eski serileştirilmiş nesneden ziyade, bir tür özel yansıma (bir sınıf oluşturma/tanımlama yeteneğine sahip) ile ortaya çıkarılmasıdır. ?), bazı güvenlik denetimleri gerçekleştirmenin yanı sıra ...

+0

Güzel açıklama için teşekkür ederiz. –

+0

Bu açıklama, bu soruna isabet etmiyor. Desitleştirici nesneler, lambda ifadeleri kullanıp kullanmadığınızdan bağımsız olarak her zaman pahalı Yansıma kullanacaktır. Ancak sorunun kodu hiçbir şeyi kaldırmaz. – Holger

+0

Soru, diğer lamdbas türlerinin aksine serileştirilebilir lambdaların serileştirilmesi ve kullanılmasıydı. OP, bu nesnelerin yaşam döngüsünün hangi kısmının normal lambdalardan daha az performans gösterdiğini soruyordu. Onun kodu, çağrı ve örnekleme arasında doğru bir şekilde ayrılıyor ve onun sorusu doğrudan bunu soruyor. Yanıtın belirttiği gibi, tam olarak davalarından biri etkilenmiştir. Destatörleştirici lambdalar diğer nesneleri ("özel yansıma") serpiştirmekten çok farklı şeyler yapar ve doğal olarak daha pahalıdır. – BadZen

2

Normalde, lambda uygulamasının çalışma zamanı bölümü temel olarak tek bir uygulama yönteminden oluşacak bir sınıf oluşturur. Böyle bir sınıfı oluşturmak için gereken bilgi, çalışma zamanında LambdaMetafactory.metafactory'a bir bootstrap yöntemi çağrısı ile verilir.

Seri hale getirmeyi etkinleştirirken, işler daha karmaşık hale gelir. İlk olarak, derlenmiş kod, parametre dizisinde belirtilen bayraklara göre varargs parametrelerini ayrıştırmak zorunda kalmadan daha fazla esneklik sunan alternatif önyükleme yöntemini LambdaMetafactory.altMetafactory kullanır.

sonra üretilen lambda sınıfı oluşturmak ve lambda örneği yeniden oluşturmak için gereken tüm bilgileri içeren bir SerializedLambda örneği dönmek sahip bir writeReplace yöntemi (Serializable documentation ikinci yarısını bakınız) sahip olması gerekir. Bir lambda'nın sınıfının tek uygulama yöntemi, yalnızca basit bir temsilci çağrısından oluştuğundan, writeReplace yönteminin ve ilgili sabit bilgilerin üretilen sınıfın boyutunu çoğaltacağıdır.

Ayrıca bu Serializable lambda örneği oluşturarak sınıf sentetik yöntem $deserializeLambda$ (lamda en writeReplace sürecine bir meslektaşı olarak class documentation of SerializedLambda karşılaştırmak olacaktır dikkati çekiyor. Bu senin sınıfları disk kullanımını ve yükleme süresini artırmak (ama olmaz) lambda ifadeleri değerlendirilmesini etkileyebilir.


senin örneğin kodunda, iki yöntem önyüklemesinin ve sınıf nesil olarak zaman aynı miktarda etkilenecek olan lambda ifadesi başına sadece bir kez olur. müteakip kontrolde, the class generated on the first evaluation will be re-used and only a new instance created (if not even the instance is re-used) Burada kuzu olduğunda bile tek seferlik bir ek yük hakkında konuşuyoruz. da ifade bir döngüde bulunur, yalnızca ilk yinelemeyi etkiler. Eğer bir döngü içinde lambda ifadesi varsa, orada emin için tüm döngü sırasında bir örneği olacaktır döngü dışına yerken her yineleme için oluşturulan yeni bir örneği olabileceğini

Not. Ancak bu davranış, hedef arabirimin Serializable olup olmadığı sorusuna bağlı değildir. Sadece ifadenin değeri yakalayıp yakalamadığına bağlıdır (this answer ile karşılaştırın).Eğer ikinci yöntemde

final long toAdd = 1; 
MyFunction<Long, Long> adder = (number) -> number + toAdd; 

yazılı olsaydı o

Not değeri toAdd sabit bir derleme zamanı olacağını ve ifade Eğer (number) -> number + 1 yazılı olsaydı gibi derlenmiş (açık final değiştirici dikkat edin), Yani artık bir değer yakalamayacak. Daha sonra, her bir döngü yinelemesinde (Oracle'ın JVM'nin geçerli sürümünde) aynı lambda örneğini alırsınız. Dolayısıyla, yeni bir örneğin oluşturulup oluşturulmadığı sorusu bazen bağlamın küçük parçalarına bağlıdır. Ancak genellikle, performans etkisi oldukça küçüktür.