We had several discussion previously about this during the Scala meeting.
I believe that we could solve the list issues by adding the @volatile annotation to the head and tail vars.
Since those fields is only read afterwards, it should not drastically affect performance, if at all - then again, would be useful to try to compile the distribution with and without the @volatile in Lists.
We had several discussion previously about this during the Scala meeting.
I believe that we could solve the list issues by adding the @volatile annotation to the head and tail vars.
Since those fields is only read afterwards, it should not drastically affect performance, if at all -
then again, would be useful to try to compile the distribution with and without the @volatile in Lists.
Cheers,
Alex
On 9/12/13 6:21 PM, Jason Zaugg wrote:
A few recent threads [1] [2] have led me to learn a bit more about the JMM and consider how this applies to the standard library.[3] http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html <http://www.cs.umd.edu/%7Epugh/java/memoryModel/jsr-133-faq.html>
Both of our poster-children for immutable data structures harbour some dirty little vars. The head and tail of our :: are vars, which allows ListBuffer to directly build a list in start-to-finish order, and Vector has a flag `dirty` that seems to be mutated during the first moments of its life before the reference is released.
But, this means that if someone unsafely publishes `new ::("", Nil)` or `Vector(1)` to another thread, that thread could observe a inconsistent state (a null `::#hd`, or `Vector#dirty` still set to true.)
The JMM makes stronger guarantees for final fields [3][4], this potential problem is specific to non-final fields, even ones that are assigned in the constructor.
Given that the canonical example for the need for the JMM's special treatment of final fields is avoiding data races in j.l.String (its somewhat lonely poster child for immutability); what is our position on not being protected by the same guarantees for List and Vector?
-jason
[1] http://scala-programming-language.1934581.n4.nabble.com/Why-use-List-td2217742.html
[2] https://groups.google.com/d/msg/scala-user/ORxWFIzRb2c/ZzsQ9fsmq40J
[4] 17.5 final Field Semantics
--
You received this message because you are subscribed to the Google Groups "scala-internals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-internals+unsubscribe@googlegroups.com.
--
You received this message because you are subscribed to the Google Groups "scala-internals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-internals+unsubscribe@googlegroups.com.
The head and tail of our :: are vars, which allows ListBuffer to directly build a list in start-to-finish order
--
You received this message because you are subscribed to the Google Groups "scala-internals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-interna...@googlegroups.com.
--
P.S. Moreover, the same trick can be used to implement lazy vals (and more generally, double checked locking) more efficiently - without any volatile reads on all but first accesses.override def toList: List[A] = {
if (!exported && !start.isEmpty) JMMBarriers.storeStore()exported = !start.isEmptystart}
--
You received this message because you are subscribed to the Google Groups "scala-internals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-interna...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
A few recent threads [1] [2] have led me to learn a bit more about the JMM and consider how this applies to the standard library.Both of our poster-children for immutable data structures harbour some dirty little vars. The head and tail of our :: are vars, which allows ListBuffer to directly build a list in start-to-finish order, and Vector has a flag `dirty` that seems to be mutated during the first moments of its life before the reference is released.But, this means that if someone unsafely publishes `new ::("", Nil)` or `Vector(1)` to another thread, that thread could observe a inconsistent state (a null `::#hd`, or `Vector#dirty` still set to true.)The JMM makes stronger guarantees for final fields [3][4], this potential problem is specific to non-final fields, even ones that are assigned in the constructor.Given that the canonical example for the need for the JMM's special treatment of final fields is avoiding data races in j.l.String (its somewhat lonely poster child for immutability); what is our position on not being protected by the same guarantees for List and Vector?
--
You received this message because you are subscribed to the Google Groups "scala-internals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-interna...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
This problem has been known for a while. It does not seem to come up in practice that much. I guess our position is that safe publishing has to be ensured by some other means. If your threads are Akka actors, for instance, safe publishing is assured by a message handshake.I am not sure whether giving better guarantees here is worth the run-time cost. A lot of careful experiments would be needed to get a good handle on what the typical and worst-case run-time cost would be, so that we can reach a more informed decision.
nobody has ever demonstrated
I couldn't break ::, but I could break something awfully similar:
Unsafe publication is unsafe...
However, there needs to be a decision on what is to be expected of immutable collections.
--
Other than knowing what variations to try to exploit the data race, though, the particular reason for the difference shouldn't inform our design.
On Thu, Sep 12, 2013 at 11:47 PM, Paul Phillips <pa...@improving.org> wrote:Yes, I don't mean to imply otherwise - but uncouth though it may be, even correctness is a tradeoff which has to be assessed against its costs. I have no horse in the race, but if I did, it would be my position that any change which is sure to negatively impact performance should be accompanied by at least an existence proof demonstrating its purpose. If something can happen in theory, then it should be possible to make it happen in practice. If it can't be made to happen in practice, then maybe the theory is incomplete.
On Thu, Sep 12, 2013 at 2:31 PM, Jason Zaugg <jza...@gmail.com> wrote:
Other than knowing what variations to try to exploit the data race, though, the particular reason for the difference shouldn't inform our design.I'd defer to the experience of others to figure out whether we fix this or document our way out of it. But if I tend to think that if these guarantees are important for String, we need to justify ourselves well to exclude List and Vector.
Pavel's `JMMBarriers` trick dries up the NPEs in my test, but I believe it is doing so by relying on implementation details, rather than the model. As i understand, writing to an unrelated volatile field doesn't guarantee anything other consistent access to that very field, not to unrelated non-volatile fields.
-jason
--
You received this message because you are subscribed to the Google Groups "scala-internals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-interna...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
--
13 sep 2013 kl. 08:23 skrev Jason Zaugg:On Thu, Sep 12, 2013 at 11:47 PM, Paul Phillips <pa...@improving.org> wrote:Yes, I don't mean to imply otherwise - but uncouth though it may be, even correctness is a tradeoff which has to be assessed against its costs. I have no horse in the race, but if I did, it would be my position that any change which is sure to negatively impact performance should be accompanied by at least an existence proof demonstrating its purpose. If something can happen in theory, then it should be possible to make it happen in practice. If it can't be made to happen in practice, then maybe the theory is incomplete.
On Thu, Sep 12, 2013 at 2:31 PM, Jason Zaugg <jza...@gmail.com> wrote:
Other than knowing what variations to try to exploit the data race, though, the particular reason for the difference shouldn't inform our design.I'd defer to the experience of others to figure out whether we fix this or document our way out of it. But if I tend to think that if these guarantees are important for String, we need to justify ourselves well to exclude List and Vector.I agree with this sentiment. See this thread for an example of where the “String is safe” assumption is broken by Thread.setName() (tl;dr is uses a Char[] instead of String internally because of native code interop).Pavel's `JMMBarriers` trick dries up the NPEs in my test, but I believe it is doing so by relying on implementation details, rather than the model.
As i understand, writing to an unrelated volatile field doesn't guarantee anything other consistent access to that very field, not to unrelated non-volatile fields.
--
You received this message because you are subscribed to the Google Groups "scala-internals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-interna...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
Hi Paolo,your approach introduces a synchronization edge between the volatile read and all preceding volatile writes. The problem is that in this codevar xs: List[String] = NilThread1:xs ::= "hello"Thread2:while (xs == Nil) {}println(xs)there is no synchronization action between the two threads which ensures the proper order of the accesses to your volatile field. The compiler or CPU might schedule the write to xs ahead of its time since the value is known before writing to hd and tl, meaning that the other thread might see that write before the volatile write has occurred. The volatile read then does nothing to ensure synchronization with the write which has yet to be done.According to the JMM this program is still not properly synchronized.
As far as I understand, stuff works when we declare the members final?
What about declaring them final then and using reflection to modify them where necessary?
In some cases, such as deserialization, the system will need to change the final fields of an object after construction. final fields can be changed via reflection and other implementation-dependent means. The only pattern in which this has reasonable semantics is one in which an object is constructed and then the final fields of the object are updated. The object should not be made visible to other threads, nor should the final fields be read, until all updates to the final fields of the object are complete. Freezes of a final field occur both at the end of the constructor in which the final field is set, and immediately after each modification of a final field via reflection or other special mechanism.
However, I don't understand yet the corner cases in the rest of the section, I didn't really look into it.
--
You received this message because you are subscribed to the Google Groups "scala-internals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-interna...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
What about making head and tail final and using something a temporary ArrayBuffer inside ListBuffer, and then create the list in the call to ListBuilder.result()?All operations that work with List in the traditional functional way (building lists by prepending new nodes) would not be affected in any way. If anything, they would become faster when head and tail is final. So the performance reduction would be confined to the ListBuilder. And I think it would not be that large. In the worst case some operations of List would have to be changed to not use ListBuffer, but that would be a good idea anyway.
I think that this would be the cleanest way to deal with this.
On Thursday, September 19, 2013 11:41:51 AM UTC+2, rklaehn wrote:What about making head and tail final and using something a temporary ArrayBuffer inside ListBuffer, and then create the list in the call to ListBuilder.result()?All operations that work with List in the traditional functional way (building lists by prepending new nodes) would not be affected in any way. If anything, they would become faster when head and tail is final. So the performance reduction would be confined to the ListBuilder. And I think it would not be that large. In the worst case some operations of List would have to be changed to not use ListBuffer, but that would be a good idea anyway.Note that List.map (and I guess flatMap) does *not* use the traditional approach. That is, every loop in a List-to-List for comprehension relies on efficient append (though they could be converted to using reverse, efficient prepend, and reverse again).I think that this would be the cleanest way to deal with this.
The idea makes sense, but ListBuffer supports insertion, so one shouldn't use ArrayBuffer. However, one can have separate implementations of ListBuffer (a mutable list) and List (a really immutable list), and create the list in ListBuilder.result(). This solution has the same thread-safety property of what you describe.
Making such code *thread-safe* would be much easier than the options we discussed, as one would expect; so it would be more maintainable than these alternatives.OTOH, with this code ListBuilder.result would take O(n) (alternatively, adding an element to a builder would take amortized O(1)), and would use additional temporary space.However, the authors of ListBuffer assumed the performance difference matters - this is even described in Programming in Scala, 22.3!So we just need somebody to implement and benchmark one of the alternatives, and show that relevant workloads are not affected badly. Scalac uses enough lists that it *might* be part of the workloads (though other people have much more clue than me on this).
On Thursday, September 19, 2013 11:41:51 AM UTC+2, rklaehn wrote:What about making head and tail final and using something a temporary ArrayBuffer inside ListBuffer, and then create the list in the call to ListBuilder.result()?All operations that work with List in the traditional functional way (building lists by prepending new nodes) would not be affected in any way. If anything, they would become faster when head and tail is final. So the performance reduction would be confined to the ListBuilder. And I think it would not be that large. In the worst case some operations of List would have to be changed to not use ListBuffer, but that would be a good idea anyway.Note that List.map (and I guess flatMap) does *not* use the traditional approach. That is, every loop in a List-to-List for comprehension relies on efficient append (though they could be converted to using reverse, efficient prepend, and reverse again).
I think that this would be the cleanest way to deal with this.
The idea makes sense, but ListBuffer supports insertion, so one shouldn't use ArrayBuffer. However, one can have separate implementations of ListBuffer (a mutable list) and List (a really immutable list), and create the list in ListBuilder.result(). This solution has the same thread-safety property of what you describe.
On Thu, Sep 19, 2013 at 3:29 PM, Paolo G. Giarrusso <p.gia...@gmail.com> wrote:
On Thursday, September 19, 2013 11:41:51 AM UTC+2, rklaehn wrote:What about making head and tail final and using something a temporary ArrayBuffer inside ListBuffer, and then create the list in the call to ListBuilder.result()?All operations that work with List in the traditional functional way (building lists by prepending new nodes) would not be affected in any way. If anything, they would become faster when head and tail is final. So the performance reduction would be confined to the ListBuilder. And I think it would not be that large. In the worst case some operations of List would have to be changed to not use ListBuffer, but that would be a good idea anyway.Note that List.map (and I guess flatMap) does *not* use the traditional approach. That is, every loop in a List-to-List for comprehension relies on efficient append (though they could be converted to using reverse, efficient prepend, and reverse again).I think that this would be the cleanest way to deal with this.
The idea makes sense, but ListBuffer supports insertion, so one shouldn't use ArrayBuffer. However, one can have separate implementations of ListBuffer (a mutable list) and List (a really immutable list), and create the list in ListBuilder.result(). This solution has the same thread-safety property of what you describe.
Making such code *thread-safe* would be much easier than the options we discussed, as one would expect; so it would be more maintainable than these alternatives.OTOH, with this code ListBuilder.result would take O(n) (alternatively, adding an element to a builder would take amortized O(1)), and would use additional temporary space.However, the authors of ListBuffer assumed the performance difference matters - this is even described in Programming in Scala, 22.3!So we just need somebody to implement and benchmark one of the alternatives, and show that relevant workloads are not affected badly. Scalac uses enough lists that it *might* be part of the workloads (though other people have much more clue than me on this).Yeah, fixing ListBuffer and then making Cons truly immutable sounds like the appropriate way forward.
On Thu, Sep 19, 2013 at 3:31 PM, √iktor Ҡlang <viktor...@gmail.com> wrote:
On Thu, Sep 19, 2013 at 3:29 PM, Paolo G. Giarrusso <p.gia...@gmail.com> wrote:
On Thursday, September 19, 2013 11:41:51 AM UTC+2, rklaehn wrote:What about making head and tail final and using something a temporary ArrayBuffer inside ListBuffer, and then create the list in the call to ListBuilder.result()?All operations that work with List in the traditional functional way (building lists by prepending new nodes) would not be affected in any way. If anything, they would become faster when head and tail is final. So the performance reduction would be confined to the ListBuilder. And I think it would not be that large. In the worst case some operations of List would have to be changed to not use ListBuffer, but that would be a good idea anyway.Note that List.map (and I guess flatMap) does *not* use the traditional approach. That is, every loop in a List-to-List for comprehension relies on efficient append (though they could be converted to using reverse, efficient prepend, and reverse again).I think that this would be the cleanest way to deal with this.
The idea makes sense, but ListBuffer supports insertion, so one shouldn't use ArrayBuffer. However, one can have separate implementations of ListBuffer (a mutable list) and List (a really immutable list), and create the list in ListBuilder.result(). This solution has the same thread-safety property of what you describe.
Making such code *thread-safe* would be much easier than the options we discussed, as one would expect; so it would be more maintainable than these alternatives.OTOH, with this code ListBuilder.result would take O(n) (alternatively, adding an element to a builder would take amortized O(1)), and would use additional temporary space.However, the authors of ListBuffer assumed the performance difference matters - this is even described in Programming in Scala, 22.3!So we just need somebody to implement and benchmark one of the alternatives, and show that relevant workloads are not affected badly. Scalac uses enough lists that it *might* be part of the workloads (though other people have much more clue than me on this).Yeah, fixing ListBuffer and then making Cons truly immutable sounds like the appropriate way forward.Careful: It would mean that *every* transformer on list (map, flatMap, filter, the works) would consume twice the memory and incur the copying overhead. They all use ListBuffer to do their work. This is not peanuts. It would mean we make the sequential case slower to get a guarantee for the parallel case that people so far did not seem to care about.
However, the authors of ListBuffer assumed the performance difference matters - this is even described in Programming in Scala, 22.3!
So we just need somebody to implement and benchmark one of the alternatives, and show that relevant workloads are not affected badly. Scalac uses enough lists that it *might* be part of the workloads (though other people have much more clue than me on this).Yeah, fixing ListBuffer and then making Cons truly immutable sounds like the appropriate way forward.Careful: It would mean that *every* transformer on list (map, flatMap, filter, the works) would consume twice the memory and incur the copying overhead. They all use ListBuffer to do their work. This is not peanuts. It would mean we make the sequential case slower to get a guarantee for the parallel case that people so far did not seem to care about.Cheers- Martin
On Thu, Sep 19, 2013 at 3:29 PM, Paolo G. Giarrusso <p.gia...@gmail.com> wrote:
Map and flatMap will create a completely new list. So for a long list you have to allocate many new tiny objects in any case. Having a transient ArrayBuffer or similar will not make things much worse. It might even make things faster. At least that is what my intuition says. We will have to benchmark it. But even if there is a small performance degradation, I think it would be worth it.On Thursday, September 19, 2013 11:41:51 AM UTC+2, rklaehn wrote:What about making head and tail final and using something a temporary ArrayBuffer inside ListBuffer, and then create the list in the call to ListBuilder.result()?All operations that work with List in the traditional functional way (building lists by prepending new nodes) would not be affected in any way. If anything, they would become faster when head and tail is final. So the performance reduction would be confined to the ListBuilder. And I think it would not be that large. In the worst case some operations of List would have to be changed to not use ListBuffer, but that would be a good idea anyway.Note that List.map (and I guess flatMap) does *not* use the traditional approach. That is, every loop in a List-to-List for comprehension relies on efficient append (though they could be converted to using reverse, efficient prepend, and reverse again).
I think that this would be the cleanest way to deal with this.
The idea makes sense, but ListBuffer supports insertion, so one shouldn't use ArrayBuffer. However, one can have separate implementations of ListBuffer (a mutable list) and List (a really immutable list), and create the list in ListBuilder.result(). This solution has the same thread-safety property of what you describe.
ListBuffer is currently backed by a single-linked list where you can modify the tails? Then inserting elements will currently be O(N) because you have to find the place to insert, which is O(N). So swapping the internal representation to an array would not make things worse.
And you might gain some performance for all traversal methods when head and tail are final.
Anyway, the only way to find out is to benchmark it.
--
I am highly confident that the use of an array backed buffer will in most situations be very fast and in many be faster than using ListBuffer.
On Thu, Nov 14, 2013 at 8:46 PM, Rex Kerr <ich...@gmail.com> wrote:
> On Thu, Nov 14, 2013 at 10:26 AM, Scott Carey <scott...@gmail.com> wrote:
>> I am highly confident that the use of an array backed buffer will in most
>> situations be very fast and in many be faster than using ListBuffer.
> Well, you can benchmark both the existing List and/or ListBuffer against
> ArrayBuffer, can't you?
>
> Also, I'm not sure why you're talking about serialization for an
> intermediate representation during a computation. Just don't serialize in
> the middle!
I am not sure, but I think the question is "Can you deserialize a
List?", not "Can you serialize a ListBuffer" — and a List is not
obviously an "intermediate representation during a computation". I
sense some disconnect here.
Still, you could probably replace a list by an array right before
serialization (via writeReplace/readReplace IIRC). But this would
destroy structural sharing: if you serialize both a.tail and a, and
then deserialize them, you get two distinct lists. This behavior is
acceptable correctness-wise if you pretend that lists don't have
identity (which is debatable*), but can be a problem space-wise.
This serialization pain has me thinking of a language feature request: [...]
And that might be faster than reflection, and surely more compatible than requiring a security manager. It's probably slower than writing to vars directly though. But I'd assume this isn't a problem until a well-written (micro?)benchmark shows otherwise with a realistic load, and I'd require such a proof to exist before considering a language feature.
Java serialization is not that fast anyway, so you shouldn't be using it in a performance-critical loop. Serialization tries to be compatible and robust, so we shouldn't use crazy hacks for serializing Lists.
With other serialization frameworks, which are actually optimized for performance the question *might* make more sense. There are many such frameworks for Java; for Scala, there are [picklers](http://lampwww.epfl.ch/~hmiller/pickling/) from Heather Miller et al.
2: use val, and reflection to mutate, which is difficult to use and very clumsy; most will choose 1 instead (see List, Vector, et. al)