0:00:24.810,0:00:25.859 HARY KRISHNAN: So, thank you very much 0:00:25.859,0:00:28.099 for being here on a Saturday evening, this[br]late. 0:00:28.099,0:00:30.430 My talk got pushed to the last, but I 0:00:30.430,0:00:34.540 appreciate you being here, first. My name's[br]Hari. I 0:00:34.540,0:00:36.910 work at MavenHive. So this is a talk about 0:00:36.910,0:00:43.530 Ruby memory model. So before I start, how[br]many 0:00:43.530,0:00:46.559 of you have heard about memory model and know 0:00:46.559,0:00:51.909 what it is? Show of hands, please. OK. Let's 0:00:51.909,0:00:55.150 see where this talk goes. So why I did 0:00:55.150,0:00:58.839 I come up with this talk topic. So I 0:00:58.839,0:01:01.809 started my career with Java, and I spent a 0:01:01.809,0:01:04.860 lot many years with Java, and Java has a 0:01:04.860,0:01:08.890 very clearly documented memory model. And[br]it kind of 0:01:08.890,0:01:10.500 gets to you because with all that, you don't 0:01:10.500,0:01:14.049 feel safe enough doing multi-threaded programming[br]at all. So 0:01:14.049,0:01:17.710 with Ruby, we've always been talking about,[br]you know, 0:01:17.710,0:01:21.290 doing multi-process for multi-process parallelism, 0:01:21.290,0:01:24.450 rather than multi-threaded parallelism, 0:01:24.450,0:01:28.710 even though the language actually supports,[br]you know, multi-threading 0:01:28.710,0:01:30.799 semantics. Of course we know it's called single-threaded[br]and 0:01:30.799,0:01:34.259 all that, but I just got curious, like, what 0:01:34.259,0:01:36.499 is the real memory model behind Ruby, and[br]I 0:01:36.499,0:01:39.149 just wanted to figure that out. So this talk 0:01:39.149,0:01:42.439 is all about my learnings as I went through, 0:01:42.439,0:01:46.350 like, various literatures, and figured out,[br]and I tried 0:01:46.350,0:01:48.289 to combine, like, get a gist of the whole 0:01:48.289,0:01:50.509 thing. And cram it into some twenty minutes[br]so 0:01:50.509,0:01:52.340 that I could, like, probably give you a very 0:01:52.340,0:01:55.600 useful session, like, from which you can further[br]do 0:01:55.600,0:02:01.069 more digging on this, right. So when I talked 0:02:01.069,0:02:03.420 to my friends about memory model, the first[br]thing 0:02:03.420,0:02:05.540 that comes up to their mind is probably this 0:02:05.540,0:02:10.139 - heap, heap, non-heap, stack, whatever. I'm[br]not gonna 0:02:10.139,0:02:14.069 talk about that. I'm not gonna talk about[br]this 0:02:14.069,0:02:17.450 either. It's not about, you know, optimizing[br]your memory, 0:02:17.450,0:02:21.040 or search memory leeks, or garbage collection.[br]This talk 0:02:21.040,0:02:23.330 is not about that either. So what the hell 0:02:23.330,0:02:27.370 am I gonna talk about? First, a quick exercise. 0:02:27.370,0:02:31.360 So let's start with this and see where it 0:02:31.360,0:02:35.760 goes. Simple code. Not much to process late[br]in 0:02:35.760,0:02:38.890 the day. There's a shared variable called[br]'n', and 0:02:38.890,0:02:42.030 there are thousand threads over that, and[br]each of 0:02:42.030,0:02:45.379 those threads want to increment that shared[br]variable hundred 0:02:45.379,0:02:49.379 times, right. And what is the expected output?[br]I'm 0:02:49.379,0:02:51.200 not gonna question you, I'm just gonna give[br]it 0:02:51.200,0:02:55.180 away. It's 100,000. It's fairly straightforward[br]code. I'm sure 0:02:55.180,0:02:57.200 all of you have done this, and it's no 0:02:57.200,0:03:01.680 big deal. So what's the real output? MRI is 0:03:01.680,0:03:05.319 very faithful, it gives you what you expected.[br]100,000, 0:03:05.319,0:03:08.720 right. So what happens next? I'm running it[br]on 0:03:08.720,0:03:12.569 Rubinius. This is what you see. And it's always 0:03:12.569,0:03:15.760 going to be a different number every time[br]you 0:03:15.760,0:03:19.140 run it. And that's JRuby. It gives you a 0:03:19.140,0:03:22.629 lower number. Some of you may be guessing[br]already, 0:03:22.629,0:03:24.489 and you probably know it, why it gives you 0:03:24.489,0:03:28.159 a lower number. So why all this basic stupid 0:03:28.159,0:03:31.230 code and some stupid counter over here, right?[br]So 0:03:31.230,0:03:34.189 I just wanted to get a really basic example 0:03:34.189,0:03:36.299 to explain the concept of increment is not[br]a 0:03:36.299,0:03:40.040 single instruction, right. The reason why[br]I'm talking about 0:03:40.040,0:03:43.390 this is, I love Ruby because the syntax is 0:03:43.390,0:03:46.629 so terse, and it's so simple, it's so readable, 0:03:46.629,0:03:49.310 right. But it does not mean every single instruction 0:03:49.310,0:03:52.140 on the screen is going to be executed straight 0:03:52.140,0:03:54.810 away, right. So at least, to my junior self, 0:03:54.810,0:03:56.599 this is the first advice I would give, when 0:03:56.599,0:04:00.590 I started, you know, multi-threaded programming.[br]So at least 0:04:00.590,0:04:05.980 three steps. Lowered increments store, right.[br]That's, even further, 0:04:05.980,0:04:09.879 really simple piece of code like, you know,[br]a 0:04:09.879,0:04:12.879 plus equals to, right. So this is what we 0:04:12.879,0:04:15.750 really want to happen. You have a count, you 0:04:15.750,0:04:18.399 lowered it, you increment it, you stored it.[br]Then 0:04:18.399,0:04:21.019 the next thread comes along. It lowers it,[br]increments 0:04:21.019,0:04:23.220 it, stores it. You have the next result which 0:04:23.220,0:04:25.750 is what you expect, right. But we live in 0:04:25.750,0:04:28.260 a world where threads don't want to be our 0:04:28.260,0:04:31.470 friend. They do this. One guy comes along,[br]reads 0:04:31.470,0:04:33.920 it, increments it. The other guy also reads[br]the 0:04:33.920,0:04:37.440 older value, increments it. And both of them[br]go 0:04:37.440,0:04:40.020 and save the same value, right. So this is 0:04:40.020,0:04:42.120 a classic case of lost update. I'm sure most 0:04:42.120,0:04:44.060 of you have seen it in the database world. 0:04:44.060,0:04:46.770 But this pretty much happens a lot in the 0:04:46.770,0:04:48.860 multi-threading world, right. But why did[br]it not happen 0:04:48.860,0:04:51.620 with MRI? And what did you see the right 0:04:51.620,0:04:53.190 result?? [00:04:52]? That, I'm sure a lot[br]of you 0:04:53.190,0:04:55.580 know, but let's step, let's part that question[br]and 0:04:55.580,0:05:00.500 just move a little ahead. So, as you observed 0:05:00.500,0:05:03.770 earlier, a lot of reordoring happening in[br]instructions, right. 0:05:03.770,0:05:07.210 Like, the threads were context-switching,[br]and they were reordering 0:05:07.210,0:05:11.139 statements. So where does this reordering[br]happen? Reordering can 0:05:11.139,0:05:14.740 happen at multiple levels. So start from the[br]top. 0:05:14.740,0:05:18.150 You have the compiler, which can do simple[br]optimizations 0:05:18.150,0:05:20.780 like look closer?? [00:05:20]. Even that can[br]change the 0:05:20.780,0:05:23.990 order of your statements in your code, right.[br]Next, 0:05:23.990,0:05:27.680 when the code gets translated to, you know,[br]machine-level 0:05:27.680,0:05:30.639 language, goes to core, and your CP cores[br]are 0:05:30.639,0:05:34.430 at liberty, again, to reorder them for performance.[br]And 0:05:34.430,0:05:37.020 next comes the memory system, right. The memory[br]system 0:05:37.020,0:05:39.669 is like the combined global memory, which[br]all the 0:05:39.669,0:05:42.490 CPUs can read, and also they're individual[br]caches. But 0:05:42.490,0:05:45.840 why do CPUs have caches? They want to, memory 0:05:45.840,0:05:47.710 is slow, so they want to load, reload all 0:05:47.710,0:05:50.080 the values, refactor it, keep it in the cache, 0:05:50.080,0:05:52.710 again improve performance. So even the memory[br]system can 0:05:52.710,0:05:55.940 conspire against you and reorder the loads[br]and stores 0:05:55.940,0:05:59.380 after the memory registers. And that can cause[br]reordering, 0:05:59.380,0:06:03.319 right. So this is really, really crazy. Like,[br]I'm 0:06:03.319,0:06:07.550 a very stupid programmer, who works at the[br]programming 0:06:07.550,0:06:10.599 language level. I don't really understand[br]the structure of 0:06:10.599,0:06:13.169 the hardware and things like that. So how[br]do 0:06:13.169,0:06:15.550 I keep myself abstracted from all this, you[br]know, 0:06:15.550,0:06:21.550 really crazy stuff? So that's essentially[br]a memory model. 0:06:21.550,0:06:23.930 So what, what is a memory model? A memory 0:06:23.930,0:06:27.180 model describes the interactions of threads[br]through memory and 0:06:27.180,0:06:28.970 their shared use of data. So this is straight 0:06:28.970,0:06:30.919 out of Wikipedia, right. So if you just read 0:06:30.919,0:06:34.610 it first, either you're gonna think it's really[br]simple, 0:06:34.610,0:06:38.069 and probably even looks stupid, but otherwise[br]you might 0:06:38.069,0:06:40.789 not even understand. So I was the second category. 0:06:40.789,0:06:43.879 So what does this all mean? So when there 0:06:43.879,0:06:48.580 are so many complications with the reordering,[br]the reads 0:06:48.580,0:06:51.129 and writes of memory and things like that,[br]as 0:06:51.129,0:06:54.759 a programmer you need certain guarantees from[br]the programming 0:06:54.759,0:06:56.840 language, and the virtual machine you're working[br]on top 0:06:56.840,0:07:01.039 of, to say this is how multi-threaded shared,[br]I 0:07:01.039,0:07:03.979 mean, multi-threaded access to shared memory[br]is going to 0:07:03.979,0:07:05.940 work. These are the basic guarantees and these[br]are 0:07:05.940,0:07:09.310 the simple rules of how the system works.[br]So 0:07:09.310,0:07:13.160 you can reliably work code against that, right.[br]So 0:07:13.160,0:07:15.139 in, in effect, a memory model is just a 0:07:15.139,0:07:21.479 specification. Any Java programmers here,[br]in the house? Great. 0:07:21.479,0:07:25.860 So how many of you know about JSR 133? 0:07:25.860,0:07:31.270 The memory model, double check locking - OK.[br]Some 0:07:31.270,0:07:37.280 people. Single term issue? OK - some more[br]hands. 0:07:37.280,0:07:39.620 So Java was the first programming language[br]which came 0:07:39.620,0:07:43.360 up with a concept called memory model, right.[br]Because, 0:07:43.360,0:07:45.610 the first thing is, right ones?? [00:07:45][br]won't run 0:07:45.610,0:07:48.110 anywhere. It had to be predictable across[br]platforms, across 0:07:48.110,0:07:51.740 reimplementations, and things like that. So[br]the, there had 0:07:51.740,0:07:54.650 to be a JSR which specified what is the 0:07:54.650,0:07:56.860 memory model that it can code against so that 0:07:56.860,0:08:02.129 your multi-threaded code works predictably,[br]and deterministically across platforms 0:08:02.129,0:08:08.520 and across virtual machines. Right? So essentially[br]that's where 0:08:08.520,0:08:11.280 my, you know, whole thing started. I had gone 0:08:11.280,0:08:14.509 through the Java memory model, and was pretty[br]much 0:08:14.509,0:08:16.960 really happy that someone had taken the pain[br]to 0:08:16.960,0:08:18.590 write it down in clear terms so that you 0:08:18.590,0:08:25.590 don't have to worry about multi-threading.[br]Hold on, sorry. 0:08:28.020,0:08:34.669 Sorry about that. Cool. So. Memory model gives[br]you 0:08:34.669,0:08:40.610 rules at three broad levels. Atomicity, visibility[br]and ordering. 0:08:40.610,0:08:43.039 So atomicity is as simple as, you know, variable 0:08:43.039,0:08:47.030 assignment. Is a variable assignment an indivisible[br]unit of 0:08:47.030,0:08:49.520 work, or not? The rules around that, and it 0:08:49.520,0:08:52.370 also talks about rules around, can you assign[br]hashes, 0:08:52.370,0:08:55.070 send arrays indivisibly and things like that.[br]These rules 0:08:55.070,0:08:57.670 can change based on every alligned version,[br]and things 0:08:57.670,0:09:01.940 like that. Next is visibility. So in that[br]example 0:09:01.940,0:09:05.040 which you talked about, I mean, we saw two 0:09:05.040,0:09:07.310 threads trying to read the same value. Essentially[br]they 0:09:07.310,0:09:09.390 are spying on each other. And it was not 0:09:09.390,0:09:11.529 clear at what point the data had to become 0:09:11.529,0:09:14.860 visible to each of those threads. So essentially[br]visibility 0:09:14.860,0:09:18.240 is about that. And that is ensured through[br]memory 0:09:18.240,0:09:21.800 barriers and ordering, which is the next thing.[br]So 0:09:21.800,0:09:25.120 ordering is about how the loads and stores[br]are 0:09:25.120,0:09:28.600 sequenced, or, you know, let's say you want[br]to 0:09:28.600,0:09:30.720 write a piece of code, critical section as[br]you 0:09:30.720,0:09:32.880 call it. And you don't want the compiler to 0:09:32.880,0:09:35.510 do any crazy things to improve performance.[br]So you 0:09:35.510,0:09:38.140 say, I make it synchronized, and it has to 0:09:38.140,0:09:40.399 behave in a, behave in a nice serial?? [00:09:40] 0:09:40.399,0:09:44.730 manner. So that ?? manner is ensured by ordering. 0:09:44.730,0:09:47.940 Ordering is a really complex area. It talks[br]about 0:09:47.940,0:09:50.850 causality, logical clocks and all that. I[br]won't go 0:09:50.850,0:09:54.250 into those details. But I've been worrying[br]you with 0:09:54.250,0:09:58.070 all this, you know, computer science basics[br]and all 0:09:58.070,0:10:00.010 this. Why the hell am I talking about it 0:10:00.010,0:10:02.430 in a Ruby conference? Ruby is single-threaded,[br]anyway. Why 0:10:02.430,0:10:05.640 the hell should I care about it, right? OK. 0:10:05.640,0:10:09.120 Do you really think languages like Ruby are[br]thread 0:10:09.120,0:10:14.940 safe? Show of hands, anyone? So thread safety,[br]I'm 0:10:14.940,0:10:18.600 talking only about Ruby - maybe Python. GIL[br]based 0:10:18.600,0:10:25.600 languages. Are they thread safe? No? OK. In[br]fact 0:10:25.700,0:10:30.649 they're not. Having single-threaded does not[br]mean it's thread-safe, 0:10:30.649,0:10:33.670 right. Threads can switch context, and based[br]on how 0:10:33.670,0:10:36.079 the language has been implemented and how[br]often the 0:10:36.079,0:10:38.529 threads can switch context, and at what point[br]they 0:10:38.529,0:10:44.010 can switch, things can go wrong, right. And[br]another 0:10:44.010,0:10:46.040 pretty popular myth - I don't think many people 0:10:46.040,0:10:49.389 believe it here, in this audience at least.[br]I 0:10:49.389,0:10:52.440 don't have concurrency problems because I'm[br]running on single 0:10:52.440,0:10:55.690 core. Not true. Again, threads can switch[br]context and 0:10:55.690,0:10:58.630 run on the same core and still have dirty 0:10:58.630,0:11:02.800 reads and things like that. So concurrency[br]is all 0:11:02.800,0:11:05.550 about interleavings, right. Again, goes back[br]to reordering. I 0:11:05.550,0:11:07.870 think I've been talking about this too often.[br]And 0:11:07.870,0:11:11.950 let's not, again, worry with that. It's about[br]interleavings. 0:11:11.950,0:11:15.620 We'll leave it at that. So let's, before we 0:11:15.620,0:11:19.240 understand more about, you know, the memory[br]model and 0:11:19.240,0:11:21.019 what it has to do with Ruby, let's just 0:11:21.019,0:11:25.060 understand a little bit about threading in[br]Ruby. So 0:11:25.060,0:11:28.100 all of you know, green threads, as of 1.8, 0:11:28.100,0:11:31.430 there was only one worse thread, which was[br]being 0:11:31.430,0:11:35.220 multiplexed with multiple Ruby threads, which[br]were being scheduled 0:11:35.220,0:11:38.980 on it through global interpreter lock. 1.9[br]comes along, 0:11:38.980,0:11:41.200 there is a one to one mapping between the 0:11:41.200,0:11:43.660 Ruby thread and OS thread, but still the Ruby 0:11:43.660,0:11:46.620 thread cannot use the OS thread unless it[br]has 0:11:46.620,0:11:50.980 the global VM lock as its call now. The 0:11:50.980,0:11:55.750 JVL acquire. So does having a Global Interpreter[br]Lock 0:11:55.750,0:12:00.709 make you thread safe? It depends. It does[br]make 0:12:00.709,0:12:03.260 you thread safe in a way, but let's see. 0:12:03.260,0:12:05.329 So how does GIL work? This is a very 0:12:05.329,0:12:08.510 simplistic representation of how GIL works.[br]So you have 0:12:08.510,0:12:12.120 two threads here. One is already holding the[br]GIL. 0:12:12.120,0:12:15.519 So it's, it's working with the OS thread.[br]And 0:12:15.519,0:12:18.820 now when there is another thread waiting on[br]it, 0:12:18.820,0:12:21.190 waiting on the GIL to do its work, it 0:12:21.190,0:12:22.510 sends a, it wakes up the timer thread. Time 0:12:22.510,0:12:26.790 thread is, again, another Ruby thread. The[br]timer thread 0:12:26.790,0:12:30.410 now goes and interrupts the thread holding[br]the GIL, 0:12:30.410,0:12:32.040 and if the GIL, if the thread holding the 0:12:32.040,0:12:34.889 GIL is done with whatever it's doing - I'll 0:12:34.889,0:12:36.550 get to it in a bit - it just 0:12:36.550,0:12:40.320 releases the lock, and now thread two can[br]take 0:12:40.320,0:12:42.829 over and do its thing. Well this is the 0:12:42.829,0:12:48.329 basic working that at least I understood about[br]GIL. 0:12:48.329,0:12:50.300 But there are details to this, right. It's[br]not 0:12:50.300,0:12:57.300 as simple as what we saw. So, when you 0:12:57.779,0:13:00.930 initialize a thread, or create a thread in[br]Ruby, 0:13:00.930,0:13:03.100 you pass it a block of code. So how 0:13:03.100,0:13:06.240 does that work? You take a block of code, 0:13:06.240,0:13:07.769 you put it inside the thread. What the thread 0:13:07.769,0:13:10.480 does is usually it acquires a JVL and a 0:13:10.480,0:13:14.019 block?? [00:13:11]. It executes the block[br]of code. It 0:13:14.019,0:13:17.089 releases the, returns and releases the lock,[br]right. So 0:13:17.089,0:13:19.470 essentially this is how it works. So during[br]that 0:13:19.470,0:13:21.899 period of executation of the block, no other[br]thread 0:13:21.899,0:13:24.380 is allowed to work. So that makes you almost 0:13:24.380,0:13:28.110 thread safe, right? But not really. If that's[br]how 0:13:28.110,0:13:30.600 it's going to work, what if that thread is 0:13:30.600,0:13:33.899 going to hog the GIL, and not allow any 0:13:33.899,0:13:35.760 other thread to work? So there has to be 0:13:35.760,0:13:38.430 some kind of lock fairness, right. So that's[br]where 0:13:38.430,0:13:41.180 the timer thread comes in and interrupts it.[br]OK. 0:13:41.180,0:13:43.130 Does that mean the thread holding the GIL[br]immediately 0:13:43.130,0:13:45.190 gives it up, and says here you go, you 0:13:45.190,0:13:48.740 can start and work with it? Not really. Again 0:13:48.740,0:13:51.389 the thread holding the GIL will only release[br]the 0:13:51.389,0:13:53.920 GIL if it is at a context to its 0:13:53.920,0:13:57.019 boundary. What that is, is fairly complicated.[br]I don't 0:13:57.019,0:13:59.920 want to go into the details. I think people 0:13:59.920,0:14:02.540 who here know a lot better C than me, 0:14:02.540,0:14:05.110 and are deep C divers really, they can probably 0:14:05.110,0:14:08.670 tell you, you know, how, at what the GIL 0:14:08.670,0:14:11.040 can get released. If a C thread, a C 0:14:11.040,0:14:13.269 code makes a call to Ruby code, can it 0:14:13.269,0:14:15.449 or can it not release the GIL? All those 0:14:15.449,0:14:18.399 things are there, right. So all these complexities[br]are 0:14:18.399,0:14:21.360 really, really hard to deal with. I came across 0:14:21.360,0:14:25.139 this blog by Jesse Storimer. It's excellent[br]and I 0:14:25.139,0:14:27.440 strongly encourage you to go through the two-part[br]blog 0:14:27.440,0:14:30.990 about, you know, nobody understands GIL. It's[br]really, really 0:14:30.990,0:14:33.550 important, if you're trying to do any sort[br]of 0:14:33.550,0:14:39.740 multi-threaded programming in Ruby. So do[br]you still think 0:14:39.740,0:14:42.740 Ruby is thread safe because it's got GIL?[br]I'm 0:14:42.740,0:14:48.740 talking about MRI, essentially. So the thing[br]is, we 0:14:48.740,0:14:51.630 can't depend on GIL, right. GIL is not documented 0:14:51.630,0:14:54.050 anywhere that this is exactly how it works.[br]This 0:14:54.050,0:14:56.079 is when the timer thread wakes up. These are 0:14:56.079,0:14:59.310 the time slices alotted to the thread acquiring[br]the 0:14:59.310,0:15:03.190 JVL. There is no documentation around at what[br]point 0:15:03.190,0:15:04.860 the GIL can be released, can it not be 0:15:04.860,0:15:07.009 released, and things like that. There's no,[br]it's not 0:15:07.009,0:15:10.259 predictable, and if you depend on it, what[br]could 0:15:10.259,0:15:13.139 also happen is even within MRI, when you're[br]moving 0:15:13.139,0:15:15.920 from version to version, if something changes[br]in GIL, 0:15:15.920,0:15:22.220 your code with behave nondeterministically.[br]And what about language 0:15:22.220,0:15:25.209 in Ruby implementations that don't even have[br]a GIL? 0:15:25.209,0:15:27.009 So obviously that's the big problem, right.[br]If you 0:15:27.009,0:15:29.610 write a gem or something which has to be 0:15:29.610,0:15:32.079 multi-threaded, and if you're depending on[br]the GIL to 0:15:32.079,0:15:34.769 do its thing to keep you safe, then obviously 0:15:34.769,0:15:38.550 it cannot work on Rubinius and JRuby. Let[br]that 0:15:38.550,0:15:41.310 alone, even, even if you give that up, even 0:15:41.310,0:15:44.360 with MRI, it's not entirely correct to say[br]that 0:15:44.360,0:15:47.490 you're thread safe, because there is a GIL[br]that 0:15:47.490,0:15:52.660 will ensure that only one thread is running.[br]So 0:15:52.660,0:15:54.610 what did I find out? Ruby really does not 0:15:54.610,0:15:57.350 have a documented memory model. It's pretty[br]much similar 0:15:57.350,0:16:00.480 to Python. It doesn't have a clearly documented[br]memory 0:16:00.480,0:16:05.279 model. What is the implication of that? So[br]as 0:16:05.279,0:16:07.540 I mentioned previously, a memory model is[br]like a 0:16:07.540,0:16:10.769 specification. This is exactly how the system[br]has to 0:16:10.769,0:16:14.600 provide a certain minimum guarantee to the[br]users of 0:16:14.600,0:16:17.730 the language, right, regarding multi threaded[br]access to shared 0:16:17.730,0:16:22.500 memory. Now, basically if I don't have a written 0:16:22.500,0:16:23.720 down memory model, and I am going to write 0:16:23.720,0:16:26.540 a Ruby implementation to model, I have the[br]liberty 0:16:26.540,0:16:29.509 to choose whatever memory model I want. So[br]the 0:16:29.509,0:16:32.889 code, if you're writing against MRI, may not[br]essentially 0:16:32.889,0:16:36.720 work right on my, you know, my implementation[br]of 0:16:36.720,0:16:41.339 Ruby. That's the big implication, right. So[br]Ruby right 0:16:41.339,0:16:45.769 now depends on underlying virtual machines.[br]Even after ER, 0:16:45.769,0:16:47.699 you have bad code compilations, so even MRI[br]is 0:16:47.699,0:16:50.839 almost like a VM. So that has no specification 0:16:50.839,0:16:52.959 for a memory model, but it does have something, 0:16:52.959,0:16:55.279 right, internally. If you have to go through[br]the 0:16:55.279,0:16:58.130 C code and understand. It's not guaranteed[br]to remain 0:16:58.130,0:17:01.079 the same from version to version, as I understand, 0:17:01.079,0:17:05.069 right. And obviously JRuby and Rubinius, they[br]depend on 0:17:05.069,0:17:08.260 JVM and LLVM respectively. And they all have[br]a 0:17:08.260,0:17:11.819 clearly documented memory model. You could[br]have a read 0:17:11.819,0:17:15.260 at it. And the only thing is, if Ruby 0:17:15.260,0:17:18.079 had an implementation - sorry, a specification[br]for a 0:17:18.079,0:17:22.220 memory model, it could be, you know, implemented[br]using 0:17:22.220,0:17:27.599 the constructs available on JVM and LLVM.[br]But this 0:17:27.599,0:17:29.450 is what we have. We don't have much to 0:17:29.450,0:17:33.200 do. What do we do under the circumstances?[br]We 0:17:33.200,0:17:36.640 have to engineer our code for thread safety.[br]We 0:17:36.640,0:17:40.120 can't bask under the safety that, there is[br]a 0:17:40.120,0:17:42.410 GIL and so it's going to help me keep 0:17:42.410,0:17:44.530 my code thread safe. So even I can write 0:17:44.530,0:17:47.690 multiple, you know, multi threaded code without[br]actually worrying 0:17:47.690,0:17:51.290 about serious synchronization issues and things[br]like that. It's 0:17:51.290,0:17:54.500 totally not the right thing to do. I think 0:17:54.500,0:17:57.370 any which way, Ruby is a language I love, 0:17:57.370,0:17:59.710 and I'm sure all of you love, so. And 0:17:59.710,0:18:02.670 it's progressing my leaps and bounds, and[br]eventually we're 0:18:02.670,0:18:04.840 going to write more and more complex systems[br]with 0:18:04.840,0:18:09.390 Ruby. And who knows, we might have true parallelism 0:18:09.390,0:18:13.980 very soon, right. So why, still, stay in the 0:18:13.980,0:18:17.210 same mental block that we don't want to write, 0:18:17.210,0:18:20.480 you know, thread safe code that's anyway single[br]threaded. 0:18:20.480,0:18:22.150 We might as well get into the mindset of 0:18:22.150,0:18:26.130 writing proper thread safe code, and try and[br]probably 0:18:26.130,0:18:29.500 come up with a memory model, right. But I 0:18:29.500,0:18:31.700 think for now we just start engineering code[br]for 0:18:31.700,0:18:36.860 thread safety. Simple Mutex, I'm sure all[br]of you 0:18:36.860,0:18:39.580 know, but it's really, really important for[br]even a 0:18:39.580,0:18:44.090 stupid operation like a plus equals two. So[br]simple 0:18:44.090,0:18:46.970 things which are noticed in Ruby code bases[br]and 0:18:46.970,0:18:50.530 Rails code bases as well, like generally,[br]is, there 0:18:50.530,0:18:52.920 is like a synchronized, you know, a section[br]of 0:18:52.920,0:18:56.260 the code has lots of synchronization and everything.[br]It's 0:18:56.260,0:18:58.530 really safe. But we leave an innocent accessor[br]lying 0:18:58.530,0:19:00.760 around, and that causes a lot of, you know, 0:19:00.760,0:19:04.360 pain, like debugging those issues. And general[br]issues like, 0:19:04.360,0:19:08.020 you know, state mutations, inside methods[br]is really a 0:19:08.020,0:19:10.270 bad idea. So if you're looking for issues[br]around 0:19:10.270,0:19:12.200 multi threading, this might be a good place[br]to 0:19:12.200,0:19:14.350 start. So I just listed a few of them 0:19:14.350,0:19:16.310 here. I didn't want to make a really dense 0:19:16.310,0:19:19.210 talk with all the details. You can always[br]catch 0:19:19.210,0:19:20.940 me offline and I can tell you some of 0:19:20.940,0:19:23.600 my experiences and probably even listen to[br]you and 0:19:23.600,0:19:25.980 learn from you about some of the issues that 0:19:25.980,0:19:28.820 we can solve by actually writing proper thread[br]safe 0:19:28.820,0:19:33.080 code in Ruby. I came across a few gems 0:19:33.080,0:19:35.090 which were really, really nice. Both of them[br]happen 0:19:35.090,0:19:38.680 to be written by headius. The first one is 0:19:38.680,0:19:40.730 atomic. Atomic is almost trying to give you[br]the 0:19:40.730,0:19:44.970 similar constructs like the Java utility concurrent[br]package. It 0:19:44.970,0:19:51.300 tries to, it's kind of compatible across MRI,[br]JRuby, 0:19:51.300,0:19:53.800 and Rubinius, which is also a really nice[br]thing. 0:19:53.800,0:19:56.560 So you have atomic integers and atomic floats,[br]which 0:19:56.560,0:19:59.900 do increments actually in an atomic way, which[br]is 0:19:59.900,0:20:02.460 excellent. And then there is thread_safe library,[br]which also 0:20:02.460,0:20:04.590 has a few thread safe data structures. I'm[br]trying 0:20:04.590,0:20:06.570 to play around with these libraries right[br]now, but 0:20:06.570,0:20:09.150 they may be a good, you know, starting point 0:20:09.150,0:20:10.780 if you are trying to do higher level constructs 0:20:10.780,0:20:15.620 for concurrency. And that's pretty much it.[br]I'm open 0:20:15.620,0:20:21.820 to take questions. Thank you. And before anything[br]I 0:20:21.820,0:20:23.420 really would like to thank you all, again[br]for 0:20:23.420,0:20:27.140 being here for the talk, and thank the GCRC 0:20:27.140,0:20:31.410 organizers, you know, they've done a great[br]job with 0:20:31.410,0:20:38.410 this conference. A big shout out to them. 0:20:46.470,0:20:46.510 V.O.: Any questions? 0:20:46.510,0:20:46.540 H.K.: Yeah? 0:20:46.540,0:20:46.560 QUESTION: Hey. 0:20:46.560,0:20:46.590 H.K.: Hi. 0:20:46.590,0:20:47.520 QUESTION: If, for example, if a Ruby code[br]is running 0:20:47.520,0:20:51.530 in the JVM, in JRuby, how does, because none 0:20:51.530,0:20:53.810 of the Ruby code is written in a thread 0:20:53.810,0:20:56.580 safe way. How do, how does it internally manage 0:20:56.580,0:20:58.750 - does it actually, yeah, yesterday Yogi talked[br]about 0:20:58.750,0:21:00.940 the point that ActiveRecord is not actually[br]thread safe. 0:21:00.940,0:21:03.520 Can you explain it in detail like in a 0:21:03.520,0:21:04.460 theoretical way? 0:21:04.460,0:21:06.560 H.K.: OK. What is thread safety in 0:21:06.560,0:21:09.010 general, right? Thread safety is about how[br]the data 0:21:09.010,0:21:13.280 is consistently maintained after multi-threaded[br]access to that shared 0:21:13.280,0:21:17.130 data, right. So Ruby essentially has a GIL[br]because 0:21:17.130,0:21:19.620 internal implementations are not thread safe,[br]right. That's why 0:21:19.620,0:21:22.110 you want to have a GIL to protect you 0:21:22.110,0:21:25.840 from those problems. But as far as JRuby is 0:21:25.840,0:21:29.280 concerned, or Rubinius is concerned, the implementation[br]itself is 0:21:29.280,0:21:31.930 not written in C. JRuby is written in Ruby 0:21:31.930,0:21:34.400 again, I mean JRuby itself, and Rubinius is[br]written 0:21:34.400,0:21:37.660 in Ruby. And some of these actual internal[br]constructs 0:21:37.660,0:21:40.580 are thread safe when compared to MRI. I haven't 0:21:40.580,0:21:43.190 actually taken a look in detail into the code 0:21:43.190,0:21:47.520 of these code bases, but if they are implemented 0:21:47.520,0:21:50.000 properly, you can be thread safe - internally,[br]at 0:21:50.000,0:21:53.340 least - so, which means, the base code of 0:21:53.340,0:21:55.720 JRuby itself might be thread safe. It's only[br]not 0:21:55.720,0:21:58.200 thread safe because the gems on top of it, 0:21:58.200,0:22:01.050 which are trying to run. They may have, like, 0:22:01.050,0:22:04.890 thread safety issues, right. Does that answer[br]your question, 0:22:04.890,0:22:05.840 like, or- ? 0:22:05.840,0:22:08.200 QUESTION: About thread safety?? [00:22:09]. 0:22:08.200,0:22:11.720 H.K.: Sure, sure. So those gems will not work.[br]That's 0:22:11.720,0:22:13.840 the point. Like what I want to convey here, 0:22:13.840,0:22:16.910 is whatever gems we are offering, and whatever[br]code 0:22:16.910,0:22:18.780 we are writing, we might get it - it's 0:22:18.780,0:22:20.240 a good idea to get into the habit of 0:22:20.240,0:22:22.860 writing thread safe code, so that we can actually 0:22:22.860,0:22:25.460 encourage a truly parallel Ruby, right. We[br]don't, we 0:22:25.460,0:22:27.530 don't have to stay in the same paradigm of 0:22:27.530,0:22:31.520 OK we have to be single threaded. 0:22:31.520,0:22:37.010 QUESTION: So Mutex based thread management[br]is one way. 0:22:37.010,0:22:40.060 There's also like actors and futures and things[br]like that. 0:22:40.060,0:22:41.890 And there's a gem called cellulite- 0:22:41.890,0:22:42.680 H.K.: Yup. 0:22:42.680,0:22:45.040 QUESTION: That, combined with something called[br]Hamster, 0:22:45.040,0:22:46.390 which makes everything immutable- 0:22:46.390,0:22:46.840 H.K.: Yup. 0:22:46.840,0:22:47.960 QUESTION: Is another way to do it. 0:22:47.960,0:22:48.160 H.K.: Yup. 0:22:48.160,0:22:49.070 QUESTION: Have you done it or like, 0:22:49.070,0:22:49.950 what's your experience with that? 0:22:49.950,0:22:53.130 H.K.: Yeah, I have tried out actors, with[br]revactor, 0:22:53.130,0:22:54.330 and lockless concurrency is 0:22:54.330,0:22:56.830 something I definitely agree is a good idea.[br]But 0:22:56.830,0:23:01.440 I'm specifically talking about, you know,[br]lock-based concurrency, like, 0:23:01.440,0:23:04.530 Mutex-based concurrency. This area is also[br]important because it's 0:23:04.530,0:23:07.960 not like thread mutable state is bad. It is, 0:23:07.960,0:23:10.770 it is actually applicable in certain scenarios.[br]When we 0:23:10.770,0:23:13.360 are working in this particular paradigm, we[br]still need 0:23:13.360,0:23:19.170 the safety of a memory model. Any other questions? 0:23:19.170,0:23:26.170 QUESTION: Thanks for the talk Hari. It was[br]really 0:23:28.200,0:23:28.650 good. 0:23:28.650,0:23:29.550 H.K.: Thanks. 0:23:29.550,0:23:31.140 QUESTION: Is there a way that 0:23:31.140,0:23:35.050 you would recommend to test if you have done 0:23:35.050,0:23:37.850 threading properly or not? I mean, I know,[br]bugs 0:23:37.850,0:23:38.420 that come out- 0:23:38.420,0:23:38.610 H.K.: Right. 0:23:38.610,0:23:38.980 QUESTION: Like I have 0:23:38.980,0:23:41.680 written bugs that come out of badly written,[br]you 0:23:41.680,0:23:43.750 know, not thread safe code, as. 0:23:43.750,0:23:44.510 H.K.: So- 0:23:44.510,0:23:47.190 QUESTION: Like, ?? [00:23:46] so, you catch[br]them. 0:23:47.190,0:23:51.510 H.K.: At least, my opinion, and a lot of people[br]have 0:23:51.510,0:23:53.960 done research in this area, their opinion[br]also is 0:23:53.960,0:23:57.600 that it's not possible to write tests against[br]multi 0:23:57.600,0:24:00.480 threaded code where there is shared data.[br]Because it's 0:24:00.480,0:24:04.230 nondeterministic and nonrepeatable. The kind[br]of results you get, 0:24:04.230,0:24:06.920 you can only test it against a heuristic.[br]For 0:24:06.920,0:24:09.430 example, if you have a deterministic use case[br]at 0:24:09.430,0:24:11.620 the top level, you can probably test it against 0:24:11.620,0:24:14.490 that. But exact test cases can never be written 0:24:14.490,0:24:16.070 for this. 0:24:16.070,0:24:19.240 V.O.: Any more questions? 0:24:19.240,0:24:26.240 H.K.: Cool. All right. Thank you so much.