Re: D safety! New Feature?
On Friday, 5 August 2016 at 21:12:06 UTC, ag0aep6g wrote: On 08/05/2016 09:39 PM, Mark J Twain wrote: In the case of ImmutableQueue, There is no Enqueue! See, there is a difference between "not callable" and "does not exists". Ok, but what cool stuff is enabled by "does not exist" that doesn't work (as nicely) with "not callable"? As far as I can tell, there is no difference in practice. [...] `immutability` only logically makes something immutable, as it can easily be proved. One can cast out immutable and mutate very easily(take address, change values). Casting immutable away and then mutating is not allowed by the language. It has undefined behavior. After doing it, the program may see the old value, it may see the new value, it may crash, or it may behave completely crazy. Some example code that shows different outcomes of mutating immutable data: void main() { import std.stdio; immutable int i = 1; * cast(int*) &i = 2; writeln(i); /* prints "1" */ writeln(*&i); /* prints "2" */ immutable int* p = new int(3); *(cast(int*) p) = 4; writeln(*p); /* prints "4" */ immutable(char)[] s = "a"; (cast(char[]) s)[0] = 'b'; /* segfault on Linux */ writeln(s); /* prints "b" on Windows */ } That's not a valid D program at all, of course. All of the mutations are invalid. `Immutable` cannot be cast because there is no type relationship. You can cast between completely unrelated types no problem: struct MutableSomething { int value; void mutate(int newValue) { value = newValue; } } struct ImmutableSomething { int value; /* no mutate method here */ } void main() { auto i = ImmutableSomething(1); (cast(MutableSomething) i).mutate(2); import std.stdio; writeln(i.value); /* prints "2" */ } wow, that seems like a huge design issues. Change one value to double though and it won't work. It's not general and seems to be a design flaw. It is not proof of anything in this case though. Why? Because an Queue might not have the same size as an ImmutableQueue and your "proof" only works when they are the same size. [...] What I would say to you is, either try it on some examples, or don't. I'm asking you for an example, because I don't see the point of it. What kind of example? I have already given examples and proved that there is a functional difference. I will not continue this conversation because you refuse accept that. First you haven't given your criteria for me to prove anything to you to satisfy whatever it is you want. Second, I'm not here to waste my time trying to prove the merits of the tool. Again, either use it if you feel like it does something or don't. The method is not flawed in and of itself. It works(if it didn't, someone would have probably already shown it not to work). All you can do is assert a negative, which you can't prove yourself. So we end up chasing each other tails, which I refuse to do. If you want more proof then it is up to you, not me. I have my proof and it is good enough for me.
Re: D safety! New Feature?
On Thursday, 4 August 2016 at 18:58:18 UTC, ag0aep6g wrote: On 08/04/2016 08:22 PM, Mark J Twain wrote: The problem is that you have fixated on the *array* and not the general principle. The Array was an example. I'm having trouble understanding what you're getting at, so I'm trying to get it from the example you gave. If there's merit in your idea, then surely you can give a example where it provides benefit over the immutable keyword. Get Array out of your mind and think of them as general structures. It could be a queue. Ok. D has no built in queue, then what? You can still have an immutable queue, or a queue of immutable elements. Just like with arrays. What if it is a widget, then what? Immutable Widget vs Mutable Widget. Marking a widget immutable is not the same as having an ImmutableWidget. Can you see the difference? No. I assure you there is. Please show. The immutable keyword only prevents data manipulation, it does not change the interface. I'm still not sure what that means. An immutable object does not have mutating operations in its interface. A mutable object does. So the interfaces are different. For simple primitives, there is not much difference, but for larger complex types, the immutable keyword doesn't cut it. immutable Queue!int q1; ImmutableQueue!int q2; q1.Enqueue(x); // Compile time error if no tricks, but the error is further up the line inside Enqueue, when it actually modifies the data. Not true. Since Enqueue isn't marked const or immutable, it can't be called on an immutable object. The compiler rejects the call itself. It doesn't reject the mutation that happens inside Enqueue, because that's perfectly fine in a non-const, non-immutable method. In code: struct Queue { void Enqeue(int dummy) {} } void main() { Queue m; m.Enqeue(1); immutable Queue i; i.Enqeue(2); /* Error: mutable method test.Queue.Enqeue is not callable using a immutable object */ } We can cast away immutability and end up defeating the purpose and end up with run-time problems. You can break everything with casts, yes. q2.Enqueue(x); // Compile time error, Enqueue doesn't exist in ImmutableQueue. It doesn't exist for an immutable Queue, either. cannot cast away immutable. You can still cast from ImmutableQueue to MutableQueue. At most we can convert q2 to a mutable class, which is a copy, then replace q2 with the copy. There are difference and the second case is better. The error reporting is more accurate and no casting can be done to bypass immutability. We essentially get all this stuff for free if we simply use templates to build the hierarchy and separate the template in to different parts(immutable, mutable, etc). Now, an ImmutableQueue might not be hugely useful if we have no way to access the data, but it could provide [] access. Again, don't get bogged down in the specifics, I'm talking about general application here. The more complex the type and hierarchy the more useful such a method is and the less useful immutable keyword is. The immutable keyword is a blind, it only does one thing. Building immutability in to the type system itself allows the programmer to make immutable smarter and control exactly what it does. Sorry, but I still don't see what ImmutableWhatever does that `immutable Whatever` can't do. As far as I see, your example about having better error locations is wrong. Ok, Simple: immutable does not remove the interface! Regardless of how you are thinking about it, your exmaple, i still have Enqueue. Only the compiler has stopped compiling. struct Queue { void Enqeue(int dummy) {} } void main() { Queue m; m.Enqeue(1); immutable Queue i; i.Enqeue(2); /* Error: mutable method test.Queue.Enqeue is not callable using a immutable object */ } In the case of ImmutableQueue, There is no Enqueue! See, there is a difference between "not callable" and "does not exists". It seems maybe minor, and maybe it is, but immutability and "Immutability" are not exactly the same. I actually think they would work well together, and of course, a lot of overlap exist. `immutability` only logically makes something immutable, as it can easily be proved. One can cast out immutable and mutate very easily(take address, change values). `Immutable` cannot be cast because there is no type relationship. Any time a change has to be made to an Immutable object, a copy is created. Of course, one could call this a "long winded cast", but it's more safe and requires more verbosity, hence less ambiguity and therefore less problems. Again, there is a lot of overlap, I'm not claiming this replaces `immutable`. But it does things that immutable doesn't. I'm not even claiming it is perfect in and of itself. After all, it is somewhat arbitrary. One has to design the templates to be immutable, and if they are not then it means nothing and just repre
Re: Self Optimizing Code
On Thursday, 4 August 2016 at 11:35:41 UTC, crimaniak wrote: On Tuesday, 2 August 2016 at 22:06:38 UTC, Mark "J" Twain wrote: Instead, a better solution would be to use variables: if (n*length > m*capacity) expand(l*length) Some time ago I played with self-optimizing cache layer. Problem: Time to obtain cache items is unknown and server dependant. For example, network can be involved in this. Sometimes is it localhost, sometimes server on another continent. Time to cache it is unknown too. For example if memcached extension is not installed on this server then fallback to disk backend will slow cache performance very much. So we don't know is it worst to cache. Solution: cache measures own performance and acts accordingly. I did not think of this in terms of networking. I suppose it can be used there too but, as you mention, latency could be a factor. So I make next things: 1. Functional interface instead of save/load type interface. cacheLevel.get(functor_to_get_item) allow to measure item obtaining time. 2. Implement all measuring/controlling logic in separate class with interface AbstractStatist, in CacheLevel class make just hooks for it. So changing AbstractStatist implementation I can change work mode to measure statistics and use it or work as usual cache (EmptyStatist for all empty hooks). I make implementation to skip all items not worst caching (calcTime/calcHits*totalHits-totalTime < 0) but it's possible to make more smart things (like caching only most efficient items not exceeding cache size). If it is more complex, that what I described, it would have to be thought out a great deal. My goal was to simply have the program expose optimization points(variables) then allow an optimizer to change those to find better points. The program itself would be virtually unmodified. No code to interact with the optimization process except to use variables instead of constants(which is minimal and necessary). Exposing an interface for the program itself to guide the optimization process seems like a lot more work. But, of course, ultimately is better as it allows more information to flow in to the optimization process. But this design is beyond what I'm willing to achieve(this way could be months or years to get done right), while my method could take just a few hours to code up, and is rather general, although a bit dumb(fire and forget and hope for the best). In my experience I can draw some conclusions. 1. It need to separate measure mode and control mode. You can't have accurate statistics while changing system behavior according to current statistics state. 2. Statistics can be different for different applications but for specific application in specific conditions for most cases it can be approximated as constant. Yes, it is tricky to make the algorithm stable. This is why I think, for a simple optimizer, it would need to do this over long periods(months of program use). Because there are so many aberrations(other programs, user behavior, etc), these can only be statically removed by repetitive uses. Essentially "low pass the data to remove all the spikes" then compare the avg result with the previous. So for array allocating strategy more realistic scenario the next, I think: 1. Application compiled in some 'array_debug' mode then some statist trait added to array, collect usage statistics and writes optimal constants at the application exit. 2. Programmer configure array allocator in application according to these constants. 3. Application builds in release mode with optimal allocation strategy and without any statist overhead and works fast. Users are happy. This is more static profiling type of optimizations. I am talking about something a bit different. Both methods could be used together for a better result, but mine is for simplicity. We generally blindly set constants for things that affect performance. Let's simply turn those constants in to variables and let a global blind optimizer try to figure out better values than what we "blindly" set. There is no guarantee that it would find a better result and may even introduce program instability. But all this stuff can be somewhat measured by cpu and memory usage and given enough parameters, there is probably at least several optimal points the optimizer could find. Ultimately for just a little work(setting the variables and specifying their ranges and step, say), we could have most programs being created, in D for now at least, optimizing themselves(while they are being used by the user after they have been shipped) to some degree. This, I believe, is unheard of. It represents the next level in program optimizations. Imagine one day where a program could optimize itself depending on the hardware of the user, the users habits, etc. Well, this method attempts to get that ball rolling and does all those things in a general way(albeit ignorant, but may
Re: Self Optimizing Code
On Thursday, 4 August 2016 at 04:41:43 UTC, Chris Wright wrote: In your example, you have a size_t or double factor for array growth. If you set it to -1, you would have an unpleasant time. You need a way to specify the range of valid values. In more complex algorithms, you need a way to evaluate several parameters together to determine if they are valid. Yes, I mentioned that, but there is no ideal way to do this. I think it shouldn't be done in code since it clutters the code. The parameter tuples are the groupings, I think the compiler can infer groupings based on source code closeness(if 3 parameters show up on the same line, it's obviously save to assume they go together). Ultimately there is no big deal about grouping because one can view the parameter space of tuples as flat. Since you're not always optimizing for the same thing (time vs memory; wasted memory vs number of reallocations; for approximation algorithms, how good an approximation is generated), you need a way to specify that as well. Well, it depends. If performance is cpy cycles and memory, then they are relatively easy to get, although absolute accuracy would be required. Putting that in the language would be horribly complex. It would be much simpler for everyone if it were in a library instead. I could probably code up a basic version in a couple hours. Which I'll do for US$150 -- below market rate, especially since I'd continue supporting it. or you could pay me 150$ and I'd do it. But I have better things to do, and without compiler support, or a preprocessor, I'm not interested in the code complexity a library solution would have. Having to specify every parameter with some long sequence of characters just makes it messy. I would have to disagree about the language complexity though. At most the hardest part would be figuring out what syntax to use for symbols. $(10,0,100,1) Could be used. When ever the compiler comes across such a thing it simply 1. creates an "anonymous" variable with value 10. 2. Emits something like test.d#43(10,0,100,1), 0x043245 (file,line,default value,min,max,step,address) One could allow for group indexing or "closeness" by code. e.g., $(10,0,100,1,43) is of group 43. 0x043245 which is the address. That would be it on the compiler side. The optimizer then has enough information to work from. While such a method may lack enough information to be optimized effectively, in theory, it would be enough to find the optimal parameters in the long term. One could probably get away with a two or three line code for the variables that does the same. auto x = Opt_Var(10, 0, 100, 1, 43); then use x in the code. The hard part might be getting the relative address and absolute addresses setup properly for the optimizer. Might work but still a bit cluttery IMO. If you feel like implementing it for the benefit of humanity, be my guest. Else I'll end up trying in the future when I get around to it. Furthermore, your idea of changing compile-time constants without recompiling the program is unworkable. Constants are folded and inlined. That "* 2" might turn into "<< 1". A loop based on a constant can be unrolled. This is why I said they were variables. I said they were effectively constants. They obviously can't be constants. We have to have an address that holds the value so it can be changed by the optimizer. You suggested that we can do a random walk on values to find optimal values. This will find a local minimum, which might not be the globally optimal value. Something like simulated annealing would work better in the general case. Simulated annealing is a fair bit more complex than a random walk; it's not going to be implemented inside druntime. Well, there are many optimization methods, that isn't the point. The optimizer can be exposed for modification and different methods could be used. My main point was to introduce the idea. I have never seen it mentioned in literature before. The ability for a program to slowly optimize itself over it's life cycle seems to be a new and novel concept. The benefit of such a thing? Not sure. May only improve performance 1 or 2%. The real benefit? Who knows, but as time goes on it might become real useful and be part of the profiling process and find better ways to optimize programs that give better results.
Re: D safety! New Feature?
On Thursday, 4 August 2016 at 13:08:11 UTC, ag0aep6g wrote: On 08/03/2016 09:33 PM, Mark J Twain wrote: The built in array is mutable and exposes the same interface for the immutable copy. The only difference is that the immutable copy is marked immutable. I don't understand. An immutable array does not let you overwrite elements. A mutable array does. So they have different interfaces, don't they? My method changes the interface. An "immutable" type has an immutable interface while a mutable type has a mutable interface. For simple types, and value types, there is obviously no advantage... their interfaces are essentially empty/DNE. Can you show some example code where there is an advantage over built-in arrays? [...] As far as I see, "marking for reuse" is practically the same as freeing here. If Data were static, there could be a difference. For built-in arrays, you can mark an array for reuse by setting the length to 0 and calling assumeSafeAppend, like so: No again. Data in the example above is a local variable that allocates memory that would generally be free'ed at the end of the function call. Hence every call to foo results in a malloc/free pair for Data. By reusing the memory, if possible, one can potentially skip the malloc/free. Therefore instead of potentially thrashing the memory pool, after a few runs of foo, Data would generally not allocate. [...] Your assumeSafeAppend works while the object is in existence. This thing works between the existence of the object. The allocator (e.g. the GC) is free to reuse memory it obtained from the operating system. So repeatedly allocating and freeing the same amount of memory can already be faster than one might think. Of course, the compiler is also free to reuse stuff, if it's guaranteed that no reference escapes the function. I don't expect dmd to do stuff like this. ldc or gdc might. What do Mutable/ImmutableArray enable beyond this? What do they do that built-in arrays don't? Again, example code would help doofuses like me make sense of this. The problem is that you have fixated on the *array* and not the general principle. The Array was an example. Get Array out of your mind and think of them as general structures. It could be a queue. D has no built in queue, then what? What if it is a widget, then what? Immutable Widget vs Mutable Widget. Marking a widget immutable is not the same as having an ImmutableWidget. Can you see the difference? I assure you there is. The immutable keyword only prevents data manipulation, it does not change the interface. For simple primitives, there is not much difference, but for larger complex types, the immutable keyword doesn't cut it. immutable Queue!int q1; ImmutableQueue!int q2; q1.Enqueue(x); // Compile time error if no tricks, but the error is further up the line inside Enqueue, when it actually modifies the data. We can cast away immutability and end up defeating the purpose and end up with run-time problems. q2.Enqueue(x); // Compile time error, Enqueue doesn't exist in ImmutableQueue. cannot cast away immutable. At most we can convert q2 to a mutable class, which is a copy, then replace q2 with the copy. There are difference and the second case is better. The error reporting is more accurate and no casting can be done to bypass immutability. We essentially get all this stuff for free if we simply use templates to build the hierarchy and separate the template in to different parts(immutable, mutable, etc). Now, an ImmutableQueue might not be hugely useful if we have no way to access the data, but it could provide [] access. Again, don't get bogged down in the specifics, I'm talking about general application here. The more complex the type and hierarchy the more useful such a method is and the less useful immutable keyword is. The immutable keyword is a blind, it only does one thing. Building immutability in to the type system itself allows the programmer to make immutable smarter and control exactly what it does.
Re: Self Optimizing Code
On Thursday, 4 August 2016 at 01:54:57 UTC, Chris Wright wrote: On Wed, 03 Aug 2016 21:09:46 +, ikod wrote: This is not just profiling, but "Profile-Guided Optimization (PGO)" The person is talking about algorithms with tuning parameters (like array growth rate when appending) adjusting those parameters based on observed characteristics of the program. For instance, if it's observed that arrays of a given type tend to grow to ~1800 entries or stay under 100 entries, the runtime might automatically extend your array from length 128 to 2048 directly instead of growing to 256, then 512, then 1024, and finally 2048. A compiler would not be allowed to make these changes automatically. There are significant downsides. You need a lot of bookkeeping to determine what values you need. The values are not preserved across runs. You need to implement it separately for each algorithm. You generally only bother when the potential performance gains are great as an absolute measure and relative to the amount of bookkeeping you would have to do. For instance, with array appending, it's a terribly common operation, so you can only use this technique productively if gathering the data is dirt cheap. Close. I am only talking about adding the features to do such things and not having the compiler involved in the analysis. We can already do this sort of thing but it requires more boilerplate code and and the benefit may be negligible. What it does for us is allows us to change hard coded values into special variables that then can be manipulated(if desired, or left alone) while the program is executing. All the compiler does is take these special variables and stores them in chunks(tuples) and possibly stores some discriminatory info about them like the file and function they were used in. Then it is up to the optimizing algorithm do decide how to approach using them. If it does nothing, then the only performance hit is that variables were used instead of hard coded literals. A bit more info may be required for the optimizer to make good choices though. Since the data collected could be extremely large(thousands of tuples). This creates a very large parameter space and knowing what parameter changes created what performance changes is crucial(although a blind sort of parameter space montecarlo method could work, but would produce erratic behavior). The optimizations would persist between program executions simply by writing out all the current parameter values, else it would be difficult for the program to ever actually optimize itself. Neural networks could be used to find more optimized conditions. It could be extended to include more dynamic characteristics of a program, like what JIT does, but I think this adds far more complexity and would require much more compiler support and would then probably just end up with a JIT like environment. Another simple example. Suppose one has a sleep(10) in a thread. The 10 was "arbitrarily" chosen. Instead, if we could dosleep($10), 10 becomes a parameter. The compiler emits it to a file along with it's address(it is a global in the program address space). The optimizer then reads the file, attempts to modify this value with a slight perturbation, say to 11. Checks the performance impact(over many uses, to remove statistical deviations) and if it is more performant, it then uses that value as the default. It can then try 12 and repeat the process. While the algorithm is not advanced, it is produces a more optimal result than hard coding the value to 10, which then cannot change for the life of the program. To keep the program stable, such changes would have to occur very slowly. Better algorithms, and better profiling tools(which, say, could do wider sampling and better performance analysis), could hone in on good values from the get go. Then the program itself adjusts to the individual user case by analysis.
Re: Self Optimizing Code
On Wednesday, 3 August 2016 at 21:09:46 UTC, ikod wrote: On Wednesday, 3 August 2016 at 19:25:08 UTC, Mark "J" Twain wrote: On Wednesday, 3 August 2016 at 18:18:51 UTC, ikod wrote: On Tuesday, 2 August 2016 at 22:06:38 UTC, Mark "J" Twain wrote: Another new D feature here! Self-Optimizing Code is a fantastic research area that can lead to greater improvements in code, make them more responsive to individual applications, and requires very little effort. Looks like this: https://wiki.dlang.org/LDC_LLVM_profiling_instrumentation No, I don't think so. The best I can tell is this is just profiling. This would be having the program itself profile This is not just profiling, but "Profile-Guided Optimization (PGO)" Yes, but that is still profiling. It is static profiling while I am talking about dynamic profiling. It is similar, but it doesn't happen while the program is in use by the user. That is, simply, the programmer runs the profiler and it optimizes the code then ships it as final to the user. I am talking about the user running the program and the program optimizes itself. Big difference!
Re: D safety! New Feature?
On Wednesday, 3 August 2016 at 08:09:41 UTC, qznc wrote: On Tuesday, 2 August 2016 at 21:48:58 UTC, Mark Twain wrote: global ImmutableArray!int Data; MutableArray!int DataCopy = Data.Copy; // Creates a mutable copy of Data. ... Do work with DataCopy ... Data.Replace(DataCopy); // Makes a copy of DataCopy. I see the problem that you cannot compose this. Expand the example to two global arrays: global ImmutableArray!int Data1; global ImmutableArray!int Data2; MutableArray!int DataCopy1 = Data1.Copy; MutableArray!int DataCopy2 = Data2.Copy; ... Do work with DataCopy1 and DataCopy2 ... Data1.Replace(DataCopy1); // in between state is inconsistent => unsafe Data2.Replace(DataCopy2); I don't see this at all. What in between state are you talking about? Each object has it's own copy so no inconsistency is possible. If you are saying that Data1 and Data2 are suppose to be consistent as a pair, then your doing it wrong. You should combine them in to a single structure. The same problem would exist with other techniques. You have acted like the order of declaration matters but your example, given the information, can be written as global ImmutableArray!int Data1; MutableArray!int DataCopy1 = Data1.Copy; // Data1.Replace(DataCopy1); global ImmutableArray!int Data2; MutableArray!int DataCopy2 = Data2.Copy; // Data2.Replace(DataCopy2);
Re: D safety! New Feature?
On Wednesday, 3 August 2016 at 05:44:42 UTC, ag0aep6g wrote: On 08/02/2016 11:48 PM, Mark Twain wrote: global ImmutableArray!int Data; MutableArray!int DataCopy = Data.Copy; // Creates a mutable copy of Data. ... Do work with DataCopy ... Data.Replace(DataCopy); // Makes a copy of DataCopy. What benefit do ImmutableArray and MutableArray have over built-in arrays? The thing above can be done with built-in arrays: immutable(int)[] Data; int[] DataCopy = Data.dup; // Creates a mutable copy of Data. ... Do work with DataCopy ... Data = DataCopy.idup; // Makes a copy of DataCopy. The built in array is mutable and exposes the same interface for the immutable copy. The only difference is that the immutable copy is marked immutable. My method changes the interface. An "immutable" type has an immutable interface while a mutable type has a mutable interface. For simple types, and value types, there is obviously no advantage... their interfaces are essentially empty/DNE. [...] void foo() { MutableArray!int Data; scope(Exit) Data.Reuse(); } Reuse can simply mark the memory reusable rather then freeing it. This memory can then be reused the next time foo is called(or possibly use the stack for memory). As far as I see, "marking for reuse" is practically the same as freeing here. If Data were static, there could be a difference. For built-in arrays, you can mark an array for reuse by setting the length to 0 and calling assumeSafeAppend, like so: No again. Data in the example above is a local variable that allocates memory that would generally be free'ed at the end of the function call. Hence every call to foo results in a malloc/free pair for Data. By reusing the memory, if possible, one can potentially skip the malloc/free. Therefore instead of potentially thrashing the memory pool, after a few runs of foo, Data would generally not allocate. When one has hundreds of functions allocating and deallocating memory, this is a big deal. This is effectively caching or similar memory pooling. Your assumeSafeAppend works while the object is in existence. This thing works between the existence of the object.
Re: Self Optimizing Code
On Wednesday, 3 August 2016 at 18:18:51 UTC, ikod wrote: On Tuesday, 2 August 2016 at 22:06:38 UTC, Mark "J" Twain wrote: Another new D feature here! Self-Optimizing Code is a fantastic research area that can lead to greater improvements in code, make them more responsive to individual applications, and requires very little effort. Looks like this: https://wiki.dlang.org/LDC_LLVM_profiling_instrumentation No, I don't think so. The best I can tell is this is just profiling. This would be having the program itself profile itself and make modifications itself(of course, limited to these "special variables").
Re: Self Optimizing Code
On Wednesday, 3 August 2016 at 07:36:21 UTC, ketmar wrote: hello, you just invented JIT compiler. Um, no. JIT = Just in time compilation. The code is already compiled and in binary form. The only differnece is that hard coded values = literals in the binary, become variables. There is no recompliation. The variables are simply updated by an optimization routine. They could be manually updated by the user, for example, but the user is not intelligent with regards to these things(although an interface for the user could be used). Regardless, this is not JIT. Having seen some of your posts, I know you won't admit your ignorance here, but unless you are going to claim that DMD is JIT, then this is not JIT. Why? It can already be done in DMD without using any advanced techniques. The problem is that it requires the programmer to do more boilerplate code than is needed. This is more akin to GC, but instead of managing memory references, it manages variables. Instead of freeing stuff, it perturbs them. But does so in the background too... but does not need to "stop the world", since as far as the program is concerned, these special variables like literals(immutable at least).
Re: D safety! New Feature?
On Tuesday, 2 August 2016 at 21:48:58 UTC, Mark Twain wrote: [...] Another useful feature I forgot to mention is that we can cast between Immutable and Muttable types. class ImmutableTest { private MutableArray!int data; public ImmutableArray!int Data; this() { // Creates a view to data, but Data cannot modify data. Data.View(data); } public Add(int x) { data.Add(x); } } Data can then be exposed without worrying about it getting trampled on. Sense a MutableArray contains all the functionality of an ImmutableArray, there should be no problems with the interpretation.
Self Optimizing Code
Another new D feature here! Self-Optimizing Code is a fantastic research area that can lead to greater improvements in code, make them more responsive to individual applications, and requires very little effort. To demonstrate: In many programs, memory behavior is fixed to a large degree, if not completely. Take the typical dynamic data structure that over allocates the required memory to store it's data by some factor, usually 2. We might have code like if (length >= capacity) expand(2*length); These types of expressions are used through a program. They are hard coded values that are generally somewhat arbitrary. They definitely are not optimal in all cases. Instead, a better solution would be to use variables: if (n*length > m*capacity) expand(l*length) n,m, and l are not variable and can change. How is this useful you say? Because of mathematics! In a typical program we will have N tuples, where each tuple t_i is a parameter set, such as t_k(n,m,l). This forms a vector space and, due to mathematics, we can find optimal values for the different parameter sets by observing the outcome of small perturbations over long times. e.g., we intially start with our hard coded values as defaults. t_i_0(1,1,2) for our example above. A background process has this variable, valid ranges, and the ability to measure performance for the app. It then, in the background, perturbates t_i, say t_i_1(1.01, 1, 2), monitors the performance impact, and stores the results. This can be efficiently since the algorithm is quite simple, can be done without threading concerns(since the parameters are effectively immutable to app), and can run in the background just monitoring the app performance. Using statistics, we can find out if the change had any significant effect(remember, we are doing this over the long haul, so any deviations will cancel out). Eventually the program will optimize itself to run faster by choosing the values that best suit how the program is used during the optimization process(which can be done continually, and using better techniques than I have describe). To accomplish this task easily, all one needs to do is have special variables usage @Perf("Memory_Capacity") if ($n*length >= $m*capacity) expand($l*length); The compiler then stores values(n,m,l) in a list, in this case we named it, along with the file position, etc. This is the only extra stuff that is done to a program. The special variable symbols $ was arbitrary, not necessarily desirable. A BG thread is created that then does the optimization part. We might need to specify ranges for the values(the sample space for the algorithm, that could be done externally though).