15/06/2015

Interview Questions for Programmers by MK #3

Home

Question #3
You found the following code and were asked to refactor it if needed:
var sb = new StringBuilder();
sb.AppendLine("<root>")
sb.AppendLine(String.Format("   <node id="{0}"/>", 1));
sb.AppendLine(String.Format("   <node id="{0}"/>", 2));
sb.AppendLine(String.Format("   <node id="{0}"/>", 3));
//Many, many lines of code
sb.AppendLine("</root>");
What would you do and why?

Answer #3
It is not the best idea to create XML documents using string concatenation because it is error prone. Besides created documents are not validated in any way. In .NET we have a few possibilities to refactor this code.

I recommend to use XmlWriter in this case because we want to create a new document and we do not want to edit an existing one. However, if we also want to modify existing XML documents, the good choice will be XDocument or XmlDocument class.

In the case of small XML documents (when performance is not critical) it might be a good idea to use XDocument or XmlDocument even if we don't want to edit existing documents. Especially XDocument can be simpler in use than XmlWriter.

Comments #3
I remember that when I wanted to create an XML document in C# for the first time I did it by using string concatenation. The code worked ok and I was surprised that it didn't pass a review :)

12/06/2015

What do you know about low-level programming?

Home

Have you ever written anything in a low-level assembly language? The last time, I was in touch with low-level programming, was during my studies. I wrote something in x86 and MIPS assembly languages. It was not easy but I liked it and I think that every good developer should know basics of programming at low-level. Why I'm writing about that?

Recently, I've found a very good online game known as microcorruption which reminded me good old times. The goal od this game is to open a lock by exploiting bugs in the source code. In order to do so you have to use a debugger of MSP430 assembly language.


At the beginning, initial tasks seems to be very easy e.g. a password can be hardcoded in the source code. However, if you haven't worked with any low-level language for many years even so simple task can be challenging. Besides, every next task is more and more difficult.

microcorruption is a great game if you want to remind yourself things like registers, a calling convention, a stack, low-level addressing and many others.

I started playing and I cannot stop! I encourage you to try.

04/06/2015

I'm ashamed that I knew so little about...

Home

This post will not be related to programming. I want to write about something that I've read about recently. I think that it's extremely interesting. Besides it is important for me. I'm talking about Lvov school of mathematics, a group of brilliant Polish mathematicians who worked in Lvov before World War II and had a great impact on contemporary mathematic. You may be surprised that even famous people like John von Neumann visited Lvov in order to talk with them.

Have you heard about them? If you come from Poland there is a chance that you heard although it is not very well know topic. And it is a pity because Polish people should be proud because of their achievements. If you are not from Poland there is a bigger chance that you heard about people like Stefan Banach, Stanisław Ulam or Hugo Steinhaus. Just to mention 3 mathematicians who were a part of Lvov school of mathematics.

These were very special people. Stefan Banach established very important part of mathematic known as functional analysis, was the author of many theorems (e.g. Banach space), has his own planetoid 16856. He had written his PhD thesis within 6 months and after that he needed only 7 years to become a professor. I'm pretty sure that every mathematician knows his name.

Stanisław Ulam had took part in Project Manhattan and then worked on the hydrogen bomb. In the 40s wrote one of the first (if not the first) program playing chess. He also proposed Monte Carlo method. When Kennedy became a president in 1960 Ulam as an advisor was asked which important project should be started. He suggested an expedition to the moon what Kennedy approved!

Hugo Steinhaus did so many things that I don't know what to choose. He "discovered" Stefan Banach so without him Lvov school of mathematics could have never been created. He invented introwizor, ancestor of modern computed tomography, which was patented in many countries in Europe and in USA. One of his books Mathematical Snapshots, that was originally published in 1938, is still available on Amazon! He also worked on game theory. You can say that many people did it. However, Steinhaus had done so 20 years before someone used this term.

I'll stop now because I could write and write about them. Instead I'll cite 2 short anecdotes that show that these were really extraordinary people (based on Genialni. Lwowska szkoła matematyczna by Mariusz Urbanek, unfortunately available only in Polish).

Stefan Banach has never finished his studies, he didn't like bureaucracy, formalisms and official titles. Because of that he had a problem with his PhD. It wasn't important for him. He wanted to focus on mathematics. His friends decided to cheat him a little bit and one day they told him that some important people frrm the capital have a few questions and only he can help. He didn't have any problems to answer all these questions, but he didn't know that it was his examination for the degree of doctor ;) Thanks to this small fraud he received PhD title.

In Lvov there was a restaurant "Szkocka" ("Scotch") and mathematicians like to spend there a lot of time of course at talking about mathematics. Noise and bustle didn't bother them. They also had a habit to write down proofs and theorems on the table with a pencil. The problem was that on the next day tables were cleaned and all the work was lost. To solve a problem the owner of the restaurant was asked to set this table aside and not to clean it until everything will be transferred to paper. This was a task of students.

I hope that I convinced you that you should at least know what is Lvov school of mathematics (especially if you come from Poland or if you are mathematician). Personally, I'm ashamed that I knew so little about it before.

01/06/2015

Ray Tracing a Black Hole in C#

Home

A friend of mine Mikołaj Barwicki has published very interesting article about visualisation of black holes on codeproject. So far he received a grade 5 from 43 readers. It is a great result! If you interested in ray tracing, black holes, numerical analysis or parallel computing it is an article for you.


21/05/2015

What every blogger should do if using someone else's code #2

Home

This time I'd like to write about WPFLocalizationExtension library which makes localization of WPF applications easy. I've been using it for 4 years in my projects and it simply works. To be honest I've just realized that the version I'm using is considerably outdated. However, for all this time I haven't encountered problems that would make me to download a newer version of WPFLocalizationExtension.

I think that it is a quite good recommendation. So, if you work with WPF and you need to localize your application I encourage to give a chance to WPFLocalizationExtension.

15/05/2015

How to solve Transportation problem in Excel?

Home

I think that most of you have heard about Transportation problem. We have N factories and M mines/producers. Each factory needs and each mine can provide particular amount of resources. We have to transport these resources from mines to factories. What is obvious it costs money and this cost depends on the distance between factories and mines. We have to find such an allocation that will minimize this cost.

In order to solve this problem we can use linear programming and one of the most popular algorithms are simplex or stepping stone algorithm. However, today I will not write directly about them but I will show how to solve this problem in Excel. Yes, I'm talking about good old Excel. Surprised?

Excel has an Add-in called Solver which will do a job for us. I'll explain how to do it using a simple example with 3 factories and 3 mines. Here is a table that shows costs of transport between mines and factories. For example, if we want to move 10 units from Mine 1 to Factory 1 then a cost will be 10 *c11.

Transportation CostFactory 1Factory 2Factory 3
Mine 1c11c12c13
Mine 2c21c22c23
Mine 3c31c32c33

We also need another table with supplies and demands. Below is an example. The numbers is the first column shows how many resources each mine can provide and the numbers in the the first row shows how many resources are needed by each factory.

The last row and the last column show sums of allocated resources in each row and in each column. These columns are needed to easily configure Solver. In this example some resources have been already allocated and we need to optimally allocate remaining ones i.e. x12, x13....

Supply\Demand1505050Allocation sums
for mines
4010x12x1310
110x21x22x230
100x31x322020
Allocation sums
for factories
10020

We also we have to define limitations and a cost function. The first limitation is that found allocations should be non negative i.e.

x12, x13 ... >= 0

Besides we want to allocate all resources available in mines and each factory should receive required amount of resources i.e.

40 = 10 + x12 + x13
110 = x21 + x22 + x23
100 = x31 + x32 + 20
150 = 10 + x21 + x31
50 = x12 + x22 + x32
50 = x31 + x32 + 20

Because we have a column and a row with allocation sums it will be very easy to enter these allocations into Solver. It is also worth saying that in general these limitations can be different, for example we can have more resources than needed. Of course, in this case formulas above would be also different.

A cost function is also easy. We want to minimise the following sum which is equal to total cost of moving resources from mines to factories:

c11 * 10 + c12 * x12 + c13 * x13 + ....

Now we have everything to solve a problem in Excel. Firstly we have to enable Solver. To do so open Excel options, select Add-ins. Then find Solver on the list and confirm with OK (this procedure can vary in different versions of Excel).

I've already prepared a spreadsheet with all required equations and data for you. You can download it here (you have to download this file locally and do not use online Excel application). To run Solver go to Data tab and select Solver in Analysis category. Then select Solve button and all missing allocations will be populated. Easy, isn't it? Now, a few words about using Solver.

Here is a screenshot with Solver Parameters. A cell in a red circle contains a cost formula. This formula will be minimized (see a green rectangle). Yellow rectangle contains cells that will be modified by an algorithm and finally blue rectangle contains six formulas explained in the previous post.


The next screenshot shows additional options of Solver. You can display this window by pressing Options button in Solver Parameters window. I want to point 2 selected options. Assume Linear Model tells Solver that it deals with linear programming problem and Assume Non-Negative tells Solver that we are interested only in non-negative results.


As you can see much more options are available. I encourage you to experiment with them and also with different costs, limitations, number of mines/factories and problems.

06/05/2015

Interview Questions for Programmers by MK #2

Home

I prepared this question a long time ago in order to check knowledge of basic good practices.

Question #2
You have the following method which is responsible for updating a client in a database:
public void UpdateClient(
   int id,
   string name,
   string secondname,
   string surname,
   string salutation,
   int age,
   string sex,
   string country,
   string city,
   string province,
   string address,
   string postalCode,
   string phoneNumber,
   string faxNumber,
   string country2,
   string city2,
   string province2,
   string address2,
   string postalCode2,
   string phoneNumber2,
   string faxNumber2,
   ...)
{
   //...
}
Do you think that this code requires any refactoring? If yes, give your proposition. A database access technology doesn't matter in this question.

Answer #2
The basic problem with this method is that it has so many parameters. It is an error prone, difficult to maintain and use approach. I suggest to change the signature of this method in the following way:
public void UpdateClient(Client client)
{
   //...
}
Where Client is a class that models clients. It can look in the following way:
public class Client
{
   public int Id { get; set; }
   public string Name { get; set; }
   public string Secondname { get; set; }
   public string Surname { get; set; }
   public string Salutation { get; set; }
   public int Age { get; set; }
   public string Sex { get; set; }
   public Address MainAddress{ get; set; }
   public Address AdditionalAdddress { get; set; }
   /* More properties */
}
Address class contains details (country, city..) of addresses.

Comments #2
You may also write much more e.g.:
  • It may be good to introduce enums for properties like 'Sex' which can take values only from the strictly limited range.
  • UpdateClient method should inform a caller about the result of an update operation e.g. by returning a code.
However, the most important thing is to say that UpdateClient method shouldn't have so many parameters. Personally, if I see a code as above I immediately want to reduce the number of parameters. This question seemed and still seems to be very easy, however not all candidates were able to answer it. Maybe it should be more accurate. For example, I should have stressed that a candidate should focus ONLY on available code. What do you think?

27/04/2015

Interview Questions for Programmers by MK #1

Home

Do you know series of posts titled Interview Question of the Week on a SQL Authority blog? If not or if you don't know this blog at all you have to catch up. I learned a lot of from this series so I decided to start publishing something similar but to focus more on .NET and programming.

This is a first post from series which I called Interview Questions for Programmers by MK and in which I'm going to publish questions that I'd ask if I were a recruiter. Of course they are somehow based on my experience as a participant of many interviews.

Question #1
What is a meaning of using statement in the code below? What would you do if using keyword did not exist?
using(var file = File.OpenWrite(path))
{
   //...
}
Answer #1
In this example using statement is used to properly release resources (to call Dispose method) that are owned by an object of a class that implements IDisposable interface. It is a syntactic sugar and could be replaced by using try/finally block in the following way:
var file = File.OpenWrite(path);
try
{
   //...
}
finally
{
   if(file != null)
      file.Dispose();
}

23/04/2015

How to build predicates dynamically using expression trees

Home

I'm working at the application which finds so called execution patterns in logs recorded by IntelliTrace historical debugger. An execution pattern is a sequence of methods calls that is widely used in the application and it is a kind of automatically generated documentation. The part of the algorithm is filtering of found patterns based on criteria like the length of a pattern or the number of different methods in a pattern.

At the beginning I used only 2 criteria so it was easy to handle all possible combinations of them i.e. use the first criterion, use the second criterion, use both and used none. Then I added 3rd criterion and I thought that for 3 criteria I still don't need a generic mechanism. However, shortly it turned out that I want to handle 5 criteria what gives 32 of possible combinations. This time I did it once and for all.

I decided to use expression trees to dynamically build an expression that verifies any combination of criteria. The code is quite simple. Firstly we need an enum for all criteria.
[Flags]
public enum Crieria : byte
{
    None = 0,
    CriterionOne = 1,
    CriterionTwo = 2,
    All = CriterionOne | CriterionTwo
}
We also need a class that will represent patterns.
public class Pattern
{
    public int FieldOne { get; set; }
    public int FieldTwo { get; set; }
}
Now we can write a code that will dynamically build needed expressions. I assumed that every criterion has a corresponding static method that knows how to check if a current pattern fulfils it or not. The final expression produced by CreateExpression method will be of the following form pattern => predicate1(pattern) && predicate2(pattern) && predicate3(pattern)....
public static class FilterBuilder
{
    public static Func<Pattern, bool> CreateExpression(Crieria filteringMode)
    {
        var param = Expression.Parameter(typeof(Pattern));

        var subExpressions = new List<MethodCallExpression>();

        if ((filteringMode & Crieria.CriterionOne) != 0)
            subExpressions.Add(Expression.Call(typeof(FilterBuilder), nameof(CriterionOnePredicate), null, param));

        if ((filteringMode & Crieria.CriterionTwo) != 0)
            subExpressions.Add(Expression.Call(typeof(FilterBuilder), nameof(CriterionTwoPredicate), null, param));

        //Other criteria...

        if (subExpressions.Count == 0)
            return p => true;

        Expression finalExpression = subExpressions[0];
        for (var i = 1; i < subExpressions.Count; ++i)
            finalExpression = Expression.And(finalExpression, subExpressions[i]);

        return Expression.Lambda<Func<Pattern, bool>>(finalExpression, param).Compile();
    }

    public static bool CriterionOnePredicate(Pattern p)
    {
        return p.FieldOne > 0;
    }

    public static bool CriterionTwoPredicate(Pattern p)
    {
        return p.FieldTwo < 0;
    }
}
The code can be made even more generic but I'll leave it as an exercise. When I finished this code I started to worry about performance. It is critical for me because my application needs to process large amount of patterns efficiently. I made the following simple test in which dynamically generated and static functions are executed 1 million times.
var iterations = 1000000;

var predicate = FilterBuilder.CreateExpression(Crieria.All);
MeasureIt<Pattern>((p) => predicate(p), new Pattern(), iterations);

predicate = FilterBuilder.CreateExpression(Crieria.CriterionOne);
MeasureIt<Pattern>((p) => predicate(p), new Pattern(), iterations);

MeasureIt<Pattern>((p) =>
{
   FilterBuilder.CriterionOnePredicate(p);
   FilterBuilder.CriterionTwoPredicate(p);
}, new Pattern(), iterations );

MeasureIt<Pattern>((p) => FilterBuilder.CriterionOnePredicate(p), new Pattern(), iterations);
In order to measure time of calculations I used MeasureIt method from my earlier post and I received the following results:
Total time: 54
Total time: 27
Total time: 18
Total time: 12
Dynamically generated predicates are 2-3 times slower than static ones. However, we are still talking here about dozens of milliseconds in order to make 1 million calls. For me it is acceptable.

18/04/2015

What every blogger should do if using someone else's code #1

Home

The developer's work very often involves effective re-using of a code that was written by someone else. Sometime these are fully fledged libraries or frameworks but sometimes these are also small or even very small pieces of code found on a blog or a forum. All this stuff makes our life easier.

When I use someone else's code in my project I wonder if it is worth writing about it on my blog. And sometimes I hesitate. Why? Because it is a well know library and there is a lot about it in Internet. Sometimes because I don't have time to write a tutorial. And sometimes because I think that there is no much to write about,that it is a small piece of code so everybody can understand it on their own.

Now, I think that it is a wrong approach. Maybe it is small, but I used it so it means that I din't have to design, write and test it on my own. Maybe I don't have time to write a tutorial but this code actually saved me time that I'm lacking.

So, what should I (we) do? Just write a short note on our blogs and say that we used this and this in our projects. At least in this way we can thank the authors.

To start. In WPF there is a class GridSplitter that allows user to resize rows and columns in a grid. However, I wanted the same functionality for DockPanel but WPF doesn't provide it. I decided that I'll wrote it but firstly I goggled for something like that and I found this article. The DockPanelSplitter class has 300 lines of code, it is not complex but works well. Give it a chance.

14/04/2015

What I've learned about .NET from security recommendations for C/C++ applications

Home

Some time ago I had an occasion to read about security tips and tricks for C/C++. I don't use C/C++ in my day to day work however it was interesting. I also started looking for information if these recommendations apply to .NET and thanks to that I learned a few new things.

ASLR

ASLR (Address Space Layout Randomization) is a security feature introduced in Windows Vista (it is also common in other operating systems) that causes that locations of executables/libraries/stack/heap in the memory are selected randomly. For example it minimizes a chance to perform a successful buffer-overflow attack.

ASLR is not turned on for all programs but only for these that are ASLR-compatible. It is controlled by a linker option /DYNAMICBASE besides it can be enabled/disabled by editbin tool. By default this flag is set to ON in Visual Studio.

The good information is that ASLR has been supported by ngen since .NET 3.5 SP1.

VirtualAlloc vs HeapAlloc

Another recommendation says that in order to allocate memory VirtualAlloc method should be used instead of HeapAlloc because the later can bypass ASLR (for details see also this article).

I asked a question on Stack Overflow how it is implemented in .NET and the answer is that .NET uses VirtualAlloc. However, my understanding is that we shouldn't be worried because CLR effectively provides its own ASLR.

DEP

DEP (Data Execution Prevention) is another security feature that doesn't allow one to execute areas of memory that are marked as not-executable. i.e. they contain data and not code. Similarly to ASLR there is a linker flag /NXCOMPACT that enable/disable this feature and it has been used in .NET framework since .NET 2.0 SP1.

It is also worth mentioning that in practise NXCOMPACT affects only 32 bit processes. 64bit process always use DEP and it is not possible to disable it (see also this article or this article). As to 32bit processes, I heard the recommendation to explicitly call SetProcessDEPPolicy function at the beginning of 32bit program (also in .NET) to assure that DEP will be used.

EncodePointer and Decode Pointer

Everybody knows what are events and delegates in .NET and we use them everyday. The equivalent of delegates in C/C++ are function pointers. I was really surprised when I read that it is not recommended to use them directly, for example as callbacks.

Instead, they should obfuscated and de-obfuscated when needed by using EncodePointer/DecodePointer functions. It is a concept somehow similar to ASRL. The goal of this technique is to make it difficult to predict a pointer value and override it so that it will point some malicious code.

I couldn't find information if .NET uses these functions internally so I asked a question on Stack Overflow. The answer is that probably .NET doesn't use them..

Safe Structured Exception Handling

Simplifying, structured exceptions are exceptions on the operating system level. Every structured exception has a handler that is executed when the exception occurs. It is important that it is potentially possible to override an address of this handler and perform an attack.

Safe SEH is a security mechanism that doesn't allow one to do so by providing a table of possible handlers. It is controlled via /SAFESEH linker flag but again it does matter only for 32 bit processes.

It seems to me that .NET doesn't use this flag because I found this flag disabled in the make file of Core CLR. However, one of guys who answered my question on Stack Overflow says that .NET uses a table lookup for exception handlers, not pointers on the stack, what gives the same result as SAFESEH.

08/04/2015

dotPeek as a Symbol Server

Home

I think that you must have heard about .NET Source Stepping feature in Visual Studio which allow a developer to debug .NET. It is a cool thing but my experience shows that it sometimes works and sometimes not. I think that it happens because Visual Studio can have problems to download appropriate version of symbols from Microsoft Symbol Server. However, recenly I've figured out that it can be done in a different way i.e. we can use the free .NET decompiler dotPeek from JetBrains. To be honest I've been using dotPeek since .NET Reflector is not free and it works great but for a long time I wasn't aware that dotPeek can play a role of a symbol server. When I told about this to my friends they were also surprised (in a positive way) so here is a short How-to.
  1. Run dotPeek.
  2. Select Tools -> Start Symbol Server
  3. By default a server will be available under  http://localhost:33417
  4. Start and configure Visual Studio.



  5. Now Visual Studio will be trying to download symbols and source  code from dotPeek.
  6. dotPeek will be generating them (symbols and source code) in flight by decompiling assemblies .
  7. Visual Studio caches symbols on the disk so  dotPeek doesn't have to be running all the time.
  8. It is worth mentioning that you can set a breakpoint in decompiled source code!
  9. If for some reasons Visual Studio will not download symbols from dotPeek you can enforce this from Modules or Call Stack windows in Visual Studio. To do so select Load Symbols command from the context menu.
  10. You have to remember about one drawback. If you enable external symbol server in Visual Studio it may cause that starting a debugging session will take considerable longer time because IDE will try to download symbols for all assemblies.

07/04/2015

Polish -> English

Home

It looks like that I haven't been blogging for 2 months and it is the longest  break I've ever had. I decided to put aside blogging because I wanted to focus on something else. However, after these 2 months I realized that I was simply missing blogging. Therefore I decided to return to writing posts and to change something in my blog. I've been thinking about writing in English for some time so finally I decided to do this step. Why?

First and foremost English is lingua franca of IT world. Majority of books, articles, blogs etc. are in English, all or almost all IT guys know English to some extent so writing in this language means potentially wider audience. Secondly, I'm aware that my English is far from being perfect so blogging in English will be a good occasion to improve my skills in this area.

For some bloggers a decision to start blogging in English might be difficult because of one thing. They write very, very well in their native language. I mean that everybody can write something. It might be useful, it may help someone, it might be interesting and it might be  written generally well, grammatically correctly, without typos but nonetheless it is not written like a good book. There are people who can do that but unfortunately I'm not one of them. However, it also means that I'm not afraid that my style of writing will lost a lot after switching to English ;)

Another reason behind my decision is of different nature. I remember a few occasions when I was asked about my blog by head hunters or interviewers. They were interested in what I'm writing about, one of them even used Google Translate to read my posts! So why not to make their life easier :)

Last but not least I'd like to thank Piotr Sowa from Coding By To Design blog for sharing his thoughts about blogging in English with me. Thanks Piotr!

As to technical aspects of blogging in English. I decided to translate the layout of blog but I'll leave old posts and labels (tags) as they are. Maybe, in future I'll translate the most popular and interesting of them but for now I'd like to focus on producing a new content. What do you think about this approach?

28/01/2015

Jak znaleźć brakujące indeksy w bazie danych?

Home

Optymalizacja bazy danych i zapytań to temat rozległy i szeroki jak morze i nie jedną książka napisano na ten temat. Ja dzisiaj napiszę o dosyć prostej technice pozwalającej znaleźć brakujące indeksy w bazie danych MSSQL. Zapewne każdy korzystający z MSSQL Management Studio wie, że można poprosić o wyświetlenie planu wykonania zapytania (opcje Dispaly Actual Execution Plan oraz Include Actual Execution Plan). Dodatkowo po wykonaniu zapytania MSSQL zasugeruje nam jakich indeksów brakuje.

Fajnie, ale co w sytuacji kiedy widzimy, że nasza aplikacja działa wolno. Mamy podejrzenie, że problem dotyczy bazy danych, ale przecież nie będziemy uruchamiali każdego możliwego zapytania w SSMS. W takiej sytuacji możemy de facto użyć tej samej funkcjonalności co w przypadku uruchamiania zapytania z SSMS. Mam tutaj na myśli Missing Indexes Feature, która jest cechą MSSQL, a nie środowiska SSMS. Informacje o brakujących indeksach silnik bazy danych odkłada mianowicie w kilku widokach systemowych z rodziny sys.dm_db_missing_index_*. Wystarczy więc uruchomić aplikację i zobaczyć jakie indeksy sugeruje nam MSSQL. Ja w tym celu używam zapytania, które znalazłem na blogu SQL Authority.

Przykład z życia. Ostatnio musiałem zoptymalizować pewne obliczenia i postąpiłem dokładnie jak napisałem wyżej. Uruchomiłem w aplikację, zmierzyłem czas obliczeń, zapisałem czas ich uruchomienia i zakończenia, a następnie wyświetliłem listę sugerowanych indeksów do utworzenia. Było ich 6. Na początek odrzuciłem te o niskiej wartości w kolumnie Avg_Esitmated_Impact. Z pozostałych indeksów 2 różniły się tym, że jeden miał klauzulę INCLUDE, a drugi nie. Stwierdziłem, że w pierwszym podejściu skupię się na jednym.

W dalszej kolejności wykonałem testy aby zobaczyć jaki uzysk daje założenie każdego z tych 3 indeksów, a także 2 z nich czy wszystkich 3. Okazało się, że zastosowanie jednego z nich skrócił czas obliczeń o ponad 30%, a pozostałe dwa o małe kilka. Dla rzetelności testy powtórzyłem, a wyniki uśredniłem. Na koniec dokładnie przeanalizowałem proponowany indeks i porównałem go do indeksów już utworzonych na tabeli. Okazało się, że istniał już bardzo podobny indeks. Konkretnie, MSSQL zaproponował coś takiego:
CREATE INDEX IX_Test ON dbo.Table(Col_1, Col_2) INCLUDE (Col_4);
A istniejący indeks wyglądał tak:
CREATE INDEX IX_Test ON dbo.Table(Col_1, Col_2, Col_3);
Wystarczyło, wieć go zmodyfikować w następujący sposób:
CREATE INDEX IX_Test ON dbo.Table(Col_1, Col_2, Col_3) INCLUDE (Col_4);
Na koniec sprawdziłem jak taka modyfikacja wpływa na operacje wstawiania/aktualizacji danych do/w docelowej tabeli. W tym celu napisałem zapytania wstawiające setki tysięcy rekordów do tej tabeli, a także takie, które modyfikuje kolumnę Col_4.. Wyniki pokazały niewielkie spadek wydajności. Był on znacznie mniejszy niż zysk przy odczycie danych, a po drugie wiedziałem, że w praktyce omawiana tabela jest częściej czytana niż modyfikowana.

Przy pracy z Missing Indexes Feature warto wiedzieć o kilku dodatkowych rzeczach. MSSQL może nam zasugerować wiele brakujących indeksów i nie koniecznie wszystkie muszą dotyczyć zapytać wykonanych przez nas. Aby wyeliminować ten problem sugeruję wykonywanie takich ćwiczeń na dedykowanej bazie danych. Przydatne będą też kolumny last_user_seek oraz last_user_scan z widoku sys.dm_db_missing_index_group_stats. Zawierają one informacje o tym kiedy dany brakujący indeks był potrzebny. Po pierwsze podany czas możemy porównać z czasem uruchomienia/zakończenia obliczeń i odrzucić te indeksy, które nie mieszczą się w tym zakresie. Po drugie te czasy mogą zgrubnie wskazać, w którym momencie działania aplikacji występuje problem. Napisałem, że przy wyborze indeksów do dalszej analizy bazowałem na kolumnie Avg_Esitmated_Impact. Trzeba na to jednak uważać. Ta wartość to tylko pewne przybliżenie i może nas wyprowadzić na manowce. Z 3 indeksów jakie wybrałem do dalszej analizy największy zysk miał ten o najmniej wartości w tej kolumnie.

Końcowa uwaga jest taka, że Missing Indexes Feature to pomocna rzecz, ale nie jest to magiczna formuła, która rozwiąże wszystkie problemy za nas. Ma też swoje ograniczenia, o których należy wiedzieć.

Podsumowując:
  • MSSQL sugeruje brakujące indeksy.
  • Brakujące indeksy można odczytać z bazy danych.
  • Testy wydajności należy powtórzyć kilka razy.
  • Testy wydajności dobrze wykonywać w dedykowanym do tych celu środowisku.
  • Missing Indexes Feature to nie magiczna formuła i ma swoje ograniczenia.
  • Proponowane brakujące indeksy należy zawsze poddać analizie i porównać do istniejących indeksów.
  • Należy pamiętać, że indeksy spowalniają operacje aktualizacji i wstawiania danych.
  • Wartość w kolumnie Avg_Esitmated_Impact należy traktować ostrożnie.

22/01/2015

DateTime.Parse vs DateTime.ParseExact

Home

Ostatnio spotkałem się z taka sytuacją. Ktoś zdefiniował sobie dość specyficzny format daty na swoje potrzebny. Wszystko było ok kiedy był on używany do zamiany typu DateTime na ciąg znaków. Problemy zaczęły się przy próbie wykonania operacji odwrotnej. Okazało się, że rodzina metod DateTime.(Try)Parse sobie z tym nie radzi. Rozwiązaniem okazało się użycie metod z rodziny DateTime.(Try)ParseExact, która od tych wcześniejszych różni się tym, że jawnie wymaga podania formatu, który ma zostać użyty przy parsowaniu.

Postawione pytanie brzmiało czemu DateTime.(Try)Parse nie działa w tym przypadku, a w innych tak? Moja odpowiedź jest taka. Nie wiem czemu podjęto taką decyzję, ale DateTime.(Try)Parse nie obsługuje wszystkich możliwych formatów i to nawet jeśli kultura używana przez aplikację zawiera wszystkie potrzebne informacje. Oto fragment z dokumetnacji:

If you parse a date and time string generated for a custom culture, use the ParseExact method instead of the Parse method to improve the probability that the parse operation will succeed. A custom culture date and time string can be complicated, and therefore difficult to parse. The Parse method attempts to parse a string with several implicit parse patterns, all of which might fail.

A to jeszcze jeden:

The Parse and TryParse methods do not fully iterate all strings in patterns when parsing the string representation of a date and time. If you require a date and time string to have particular formats in a parsing operation, you should pass the array of valid formats to the DateTime.ParseExact...

W skrócie DateTime.(Try)Parse jest z założenia "upośledzone" i nie umie wszystkiego. Dlaczego? Może dlatego, że obsłużenie wszystkich możliwych przypadków jest bardzo trudne? A może dlatego, że gdyby napisano kombajn obsługujący wszystko to działałby wolno? To tylko zgadywanie, ale trzeba pamiętać, że:

Jeśli używamy własnych formatów daty i czasu to zaleca się użycie DateTime.(Try)ParseExact.

16/01/2015

Kodować jak w NASA

Home

Kolega podesłał mi link do ciekawego artykułu na temat 10 zasad stosowanych w NASA, aby pisać naprawdę bezpieczny, czytelny i dobry kod. Zasady te w oryginale dotyczą języka C i zacząłem się zastanawiać czy da się je zastosować do .NET'a. Na zielono zaznaczyłem te zasady, które moim zdaniem da się użyć w .NET'cie, na pomarańczowo te dyskusyjne, a na czerwono takie, których się nie da.

Stosuj tylko bardzo proste konstrukcje sterujące przepływem sterowania w programie. Nie używaj goto i rekursji.

Mi ta zasada przypomina inną Keep it simple stupid, czyli nie starajmy się na siłę pokazać jakimi super programistami jesteśmy i piszmy możliwie prosto. To, że bez goto można się obejść to oczywiste. Bardzo dyskusyjny jest natomiast zakaz użycia rekursji. Autor zasady argumentuje to tym, że brak rekursji ułatwia pracę analizatorom kodu źródłowego, a także dowodzenie poprawności kodu. Ciężko mi z tym dyskutować, bo nie wiem jakiego analizatora używa NASA i nie spotkałem się też z dowodzeniem poprawności kodu w .NET. Osobiście uważam, że rekursja jest przydatna i w wielu przypadkach algorytmy zapisane rekurencyjnie są po prostu łatwiejszych do zrozumienia. Trzeba jednak uważać o czym już pisałem w tym albo w tym artykule.

Wszystkie pętle powinny mieć sztywno określoną górną granicę liczby iteracji.

Znowu dyskusyjna sprawa i znowu ma to służyć temu, aby można było udowodnić, że pętla się kiedyś zakończy. Dać się pewnie da tak pisać, ale wygodne to to pewnie nie jest. Ponieważ w .NET'ie nie piszemy oprogramowania dla statków kosmicznych tą zasadę bym pominął.

Nie stosuj dynamicznej alokacji pamięci (w szczególności garbage collector'a) już po zainicjowaniu aplikacji.

No cóż w .NET bez garbage collector'a się nie da. Można próbować minimalizować tworzenie nowych obiektów w czasie działania aplikacji, ale tak na co dzień to właściwie po co? To, co powinno się rozważyć, to załadowanie do pamięci danych niezmiennych (referencyjnych, słownikowych czy jak to zwał) i korzystanie z nich przez cały czas życia aplikacji.

Metody powinny być możliwie krótkie tj. nie więcej niż 60 linii kodu.

Czy 60 to dobre ograniczenie? Można dyskutować, ale z pewnością metody powinny być możliwie krótkie bo to podnosi ich czytelność. Po drugie jeśli metoda jest krótka to znaczy, że robi jedną konkretną rzecz, a nie kilka lub kilkanaście.

Średnia liczba asercji na metodę powinna wynosić minimum 2. Asercje powinny zabezpieczać przez sytuacjami, które w ogóle nie powinny wystąpić i nie mieć efektów ubocznych.

Co do tego jak pisać asercje to się zgadzam, ciężko mi natomiast powiedzieć, czy 2 to dobra średnia asercji na metodę. W komentarzu do tej zasady autor pisze, że przeciętnie testy jednostkowe wykrywają 1 błąd na 10-100 linii kodu, a asercje jeszcze zwiększają szansę na wykrycie błędów. Ja to rozumiem tak, że sugeruje się używanie tego i tego. Ok, ale ja bym jednak explicite wspomniał potrzebę testów jednostkowych czy ogólnie testów automatycznych w tej zasadzie.

Zmienne powinny mieć możliwe mały zasięg.

Czyli nie stosujemy globalnego stanu, nie re-używamy tej samej zmiennej do różnych celów itd.

Wyniki zwracane przez metody powinny być zawsze sprawdzane przez metodę wołającą. Każda wołana metoda powinna zawsze sprawdzać parametry wejściowe.

Innymi słowy nie ufamy nikomu, nie stosujemy konwencji (na przykład takiej, że jeśli metoda zwraca kolekcję to nigdy nie zwróci null'a, ale pustą kolekcję) itp. Ja jednak lubię konwencję, a parametry wejściowe staram się weryfikować głównie dla metod publicznych i chronionych. Wyniki zwracane przez metody weryfikuję natomiast przede wszystkim przy użyciu zewnętrznych bibliotek.

Należy ograniczyć użycie pre-procesora, makr i kompilacji warunkowej.

Preprocesora i makr nie mamy w .NET, ale możliwość kompilacji warunkowej już tak. Czemu ich nie używać? Ponieważ utrudniają przewidzenie wyniku kompilacji. Autor zasady podaje taki przykład. Załóżmy, że w programie mamy 10 dyrektyw kompilacji warunkowej. Załóżmy, że używają innych warunków. Daje 2^10 = 1024 różnych możliwych wyników kompilacji tego samego kodu, które mogą działać inaczej!

Należy ograniczyć użycie wskaźników. W szczególności stosujemy co najwyżej jeden poziom dereferencji. Wskaźniki na funkcje nie są dozwolone.

Autor ponownie argumentuje, że brak wskaźników ułatwia weryfikację kodu. Tą zasadę ciężko jednak przełożyć na .NET. Niby z typowych wskaźników też można korzystać, ale nie jest to często używane. Jeśli natomiast pod słowo ''wskaźnik'' w tej regule podstawimy ''referencja'', a pod ''wskaźnik na funkcję'' terminy ''delegat'' lub ''wyrażenia lambda'' to okaże się, że w .NET nie możemy zrobić właściwie nic. Podsumowując ta zasada nie ma zastosowania do .NET'a.

Kod musi się kompilować i to bez ostrzeżeń. Przynajmniej raz dziennie kod źródłowy musi przejść weryfikację przy pomocy narzędzi do statycznej analizy kodu z zerową liczbą ostrzeżeń.

To, że kod musi się kompilować to oczywiste. Jeśli chodzi o ostrzeżenia to moim zdaniem w przypadku projektów prowadzonych od zera liczba ostrzeżeń rzeczywiście powinna być równa 0. Jeśli uważamy, że jakieś ostrzeżenia są "głupie" to trzeba odpowiedzieć sobie na pytanie czy używamy dobrych narzędzi? W przypadku tzw. kodu zastanego sprawa jest trudniejsza, ale te 10 zasad służy m.in. właśnie temu, aby nie tworzyć takiego kodu.

Jak widać nie wszystkie z tych zasad da się zastosować w .NET czy przy tworzeniu aplikacji typowo biznesowych. Z drugiej strony są firmy, które w .NET piszą oprogramowanie medyczne. Niektóre z tych zasad wydają się bardzo drakońskie, ale jak już pisałem NASA w swoich projektach osiąga wynik 0 błędów na tysiąc linii produkcyjnego kodu.

Polecam też zapoznanie się z oryginalnym dokumentem NASA’s 10 rules for developing safety-critical code.

08/01/2015

Czy sposób pisania pętli for ma wpływ na wydajność?

Home

Przy okazji codziennej prasówki natknąłem się na ten artykuł na temat wydajności pętli for w JavaScript'cie dla różnych przeglądarek. W skrócie chodzi o to czy powinniśmy pisać pętlą for tak:
for (int i = 0; i < array.Length; i++)
   ...
A może raczej tak, czyli zapamiętać długości tablicy w zmiennej pomocniczej i nie odczytywać jej przy każdym przebiegu pętli:
for (int i = 0, len = array.Length; i < len; i++)
   ...
Z ciekawości sprawdziłem czy taka mikro optymalizacja ma jakiekolwiek znaczenie w przypadku programowania na platformę .NET. Do zmierzenia czasu użyłem takiej metody pomocniczej:
public static void MeasureIt<T>(Action<T> action, T arg, int noOfIterations)
{
   var sw = new Stopwatch();
   sw.Start();

   for (var i = 0; i < noOfIterations; ++i)
      action(arg);

   sw.Stop();
   Console.WriteLine("Total time: " + sw.ElapsedMilliseconds);
}
Następnie wykonałem następujący test:
var noOfIterations = 100000;
var noOfElements = 10000;
var array = Enumerable.Repeat(1, noOfElements).ToArray();
//Przypadek 1
MeasureIt(arg =>
{
   var total = 0;
   for (var i = 0; i < arg.Length; i++)
      total += a[i];  
}, array, noOfIterations);
//Przypadek 2
MeasureIt(arg =>
{
   var total = 0;
   for (int i = 0, len = arg.Length; i < len; i++)
      total += a[i];
}, array, noOfIterations);
Dla rzetelności testy uruchomiłem wielokrotnie. Czas wykonywania obliczeń w obu przypadkach wynosił około 320ms z dokładnością do kilku milisekund. Czasami pierwsze podejście było szybsze, a czasami drugie. Z jednej strony czegoś takiego się spodziewałem. Z drugiej strony sądziłem, że jednak zaobserwuję pewien zysk w drugim przypadku. Dlaczego?

Otóż jeśli spojrzymy na kod pośredni wygenerowany przez kompilator to te dwie implementacje bynajmniej nie są identyczne. W pierwszym przypadku długość tablicy odczytywana jest wielokrotnie na koniec pętli, a w drugim tylko raz przed właściwą pętlą. Najwidoczniej jest to jednak tak szybka operacja, że się nie liczy (w IL do odczytania długości jednowymiarowej tablicy istnieje dedykowana instrukcja ldlen).

Dodatkowo przeprowadziłem podobny test dla użycia listy generycznej List<T>. Tym razem różnice były już zauważalne i wynosiły około 27% na rzecz zapamiętania liczby elementów listy w dodatkowej zmiennej. Średnio pierwsza wersja wykonywała się 957ms, a druga 752ms. Wynika to z tego, że aby odczytać liczbę elementów w liście należy odczytać właściwość Count czyli metodę get_Count. Na poziomie IL jest to robione przy pomocy instrukcji callvirt (w telegraficznym skrócie służącej do wołania metod na rzecz obiektów), a nie dedykowanej (i pewnie zoptymalizowanej) instrukcji ldlen jak ma to miejsce w przypadku tablic. Pomimo tych różnic uważam jednak, że w codziennej praktyce programistycznej nie należy się tym przejmować gdyż różnice w czasach obliczeń, w porównaniu do dużej liczby iteracji (100000), są zbyt małe.

24/12/2014

Życzenia świąteczne

Home

Kolejne Święta Bożego Narodzenia już tuż tuż. W tym roku czekam na nie jeszcze bardziej niż zwykle, bo to pierwsze wydarzenie tego rodzaju dla naszej córeczki. Wszystkim czytającym mojego bloga i nie tylko życzę dużo radości w gronie bliskich Im osób, a w Nowym Roku ciekawych wyzwań zawodowych oraz zapału do nauki i doskonalenia się.

Serdecznie Pozdrawiam,
Michał Komorowski

21/12/2014

Czego prawdopodobnie nie wiedzieliście o Excel'u

Home

Sądzę, że wielu z Was otarło się na studiach o programowanie liniowe oraz algorytm sympleks. Ja uczyłem się o tym na przedmiocie zwanym w skrócie POBO, co rozwija się dumnie brzmiące Podstawy badań operacyjnych. Od czasów studiów nie zajmowałem się tym zagadnieniem, aż do dzisiaj. Pomagając siostrze w rozwiązywaniu zadań na studia dowiedziałem się o możliwościach Excel'a, których w ogóle nie byłem świadomy, a są naprawdę super i każdy ma do nich dostęp. Mam tutaj na myśli dodatek Solver, który, między innymi, implementuje algorytm sympleks w bardzo przystępnej formie. Tyle tytułem wstępu. Spójrzmy na prosty przykład.

Zaczynamy od uruchomienia Excel'a. Następnie klikamy tą fikuśną okrągłą ikonę w lewym górnym roku okna i wybieramy Opcje programu Excel. Dalej przechodzimy do zakładki Dodatki i klikamy przycisk Przejdź.



W oknie, jakie się pojawi, wybieramy Dodatek Solver i zatwierdzamy.



Po zatwierdzeniu w zakładce Dane na wstążce pojawi się nowa opcja.



Teraz spróbujmy rozwiązać przykładowe proste zadanie. Załóżmy, że mamy 5 fabryk i chcemy znaleźć lokalizację centrum dystrybucyjnego tak aby suma odległości od wszystkich fabryk była minimalna. Dodatkowe ograniczenie jest takie, że odległość od każdej z fabryk nie może być większa niż 60. Położenia fabryk podane są we współrzędnych kartezjańskich. Odległość pomiędzy fabrykami, a centrum obliczamy przy pomocy standardowego wzoru. Sytuacja początkowa wygląda tak. Dla ułatwienia naniosłem położenia fabryk i początkowe położenie centrum na wykres.



Teraz uruchamiamy Solver. Jako komórkę celu wybieram pole z sumą odległości i zaznaczam, że tą wartość chcę minimalizować. Jako komórki zmieniane wybieram współrzędne centrum. Dodajemy też ograniczenie na odległość każdej z fabryk od centrum. Na koniec uruchamiam obliczenia i klikam Rozwiąż.



Wynik końcowy wygląda w następujący sposób:



To tylko wierzchołek góry lodowej. Dodatek Solver ma dużo większe możliwość i wiele opcji. Można go wykorzystać do harmonogramowania, zdefiniować wiele ograniczeń, ustalić maksymalny czas obliczeń, dokładność uzyskanego wyniku i wiele więcej. Sądzę, że warto sobie zapamiętać, że Excel ma takie możliwości i w razie potrzeby doczytać i douczyć się jak z tego korzystać.

08/12/2014

Wykrywanie nieużywanych elementów bibliografii

Home

Moim zdaniem LaTeX ma wspaniałą obsługę cytowań i bibliografii. Jest to jeden z powodów, dla którego tak lubię go używać np.: bardzo łatwo znaleźć cytowania w formacie rozumianym przez LaTeX'a, nie musimy martwić się o formatowanie, odpowiednie posortowanie czy numerowanie.

Domyślnie jest dodamy jakąś pozycję do bazy danych odnośników literaturowych, a jej nie zacytujemy to w finalnym dokumencie wygenerowanym przez LaTeX'a (np.: PDF) zostanie ona pominięta. Może to być zachowanie pożądane lub nie. Jeśli nie jest z pomocą przychodzi komenda \nocite, w szczególności jej forma \nocite{*}, która powoduje, że cokolwiek dodamy do spisu odnośników to znajdzie się to w finalnym dokumencie w sekcji z bibliografią. W pewnym momencie możemy jednak chcieć uporządkować bazę odnośników i sprawdzić co używamy, a czego nie. Przy dużym dokumencie, z dziesiątkami lub setkami cytowań nie jest to sprawa oczywista.

W takiej sytuacji z pomocą przychodzi, odkryty przeze mnie ostatnio, pakiet refcheck. Po jego włączeniu dla każdego nieużywanego elementu bibliografii, ale także dla każdej nieużywanej etykiety zostanie wygenerowane ostrzeżenie. Dodatkowo nieużywane elementy zostaną oznaczone w wygenerowanym dokumencie przy pomocy etykiet na marginesie dokumentu.

Kolejny raz okazuje się, że czegokolwiek bym sobie nie zamarzył to istnieje już pakiet, który to zapewnia :)

05/12/2014

Latex i drzewa

Home

Wczoraj już późno w nocy zamarzyło mi się umieścić w dokumencie tworzonym w LaTeX'u rysunki drzew binarnych. Początkowo myślałem o narysowaniu ich w jakimś programie graficznym, a następnie wyeksportowaniu do jpg z czym LaTeX już sobie poradzi. Potem pomyślałem jednak, że to nie ma sensu bo z pewnością już ktoś miał taki problem i stworzył odpowiedni pakiet gotowy do użycia. Nie pomyliłem się. Bardzo szybko znalazłem pakiet qtree i już 5 minut później miałem w swoim artykule piękne drzewka. Przykład użycia:.
\Tree[.{L0 - Root} 
 [.{L1 - Left child} 
  [.{L2 - Left child} ] 
  [.{L2 - Right child} ]]
 [.{L1 - Right child} ]]
Efekt końcowy wygląda natomiast następująco:



LaTeX z pewnością jest trudniejszy w użyciu niż taki Word, ale jak już się go poznasz to zrobisz wszystko. Społeczność około LaTeX'owa stworzyła tyle różnych pakietów, że nie pozostaje nic innego jak brać i korzystać.

02/12/2014

Zabawy z domenami aplikacyjnymi

Home

Proponuję zabawę z serii co zostanie wypisana na ekran. Mamy dwie klasy jak poniżej. Pierwsza z nich przekazywana jest pomiędzy domenami przez referencję, a druga przez wartość. Interfejs ITest ma charakter pomocniczy i nie ma znaczenia.
public interface ITest
{
   int Value { get; set; }
   void Start();
}

public class MarshalByRefObjectClass : MarshalByRefObject, ITest
{
   public int Value { get; set; }

   public void Start()
   {
      Console.WriteLine(AppDomain.CurrentDomain.FriendlyName);
      Value++;
   }
}

[Serializable]
public class MarshalByValueClass : ITest
{
   public int Value { get; set; }

   public void Start()
   {
      Console.WriteLine(AppDomain.CurrentDomain.FriendlyName);
      Value++;
   }
}
Mamy również następujący kod, w którym testuję jak zachowuja się:
  • Obiekty przekazywane przez wartość i przez referencję.
  • Utworzone w bieżącej (przy pomocy konstruktora) oraz w innej domenie (przy pomocy AppDomain.CreateInstanceFromAndUnwrap).
  • Przy wywołaniu na nich metody bezpośrednio oraz przy pomocy AppDomain.Callback.
Co daje łączenie 2 x 2 x 2 = 8 możliwości. Do testowania używam takiej metody pomocniczej:
private static void Test(AppDomain app, ITest test, bool doCallBack)
{
   if (doCallBack)
      app.DoCallBack(test.Start);
   else
      test.Start();

   Console.WriteLine(test.Value);
}
A to właściwy test, w którym najpierw tworzę obiekty przekazywane przez wartość/referencję lokalnie i w nowej domenie. Następnie wywołuję dla nich metodę Start i odczytuję właściwość Value.
var app = AppDomain.CreateDomain("TestDomain");

var asm = Assembly.GetExecutingAssembly();
var byRef = new MarshalByRefObjectClass();
var byRef1 = new MarshalByRefObjectClass();
var byRef2 = (MarshalByRefObjectClass)app.CreateInstanceFromAndUnwrap(asm.CodeBase, typeof(MarshalByRefObjectClass).FullName);
var byRef3 = (MarshalByRefObjectClass)app.CreateInstanceFromAndUnwrap(asm.CodeBase, typeof(MarshalByRefObjectClass).FullName);

var byValue = new MarshalByValueClass();
var byValue1 = new MarshalByValueClass();
var byValue2 = (MarshalByValueClass)app.CreateInstanceFromAndUnwrap(asm.CodeBase, typeof(MarshalByValueClass).FullName);
var byValue3 = (MarshalByValueClass)app.CreateInstanceFromAndUnwrap(asm.CodeBase, typeof(MarshalByValueClass).FullName);

Test(app, byRef, true);
Test(app, byRef1, false);
Test(app, byRef2, true);
Test(app, byRef3, false);

Test(app, byValue, true);
Test(app, byValue1, false);
Test(app, byValue2, true);
Test(app, byValue3, false);
Pytanie brzmi co zostanie wypisane na ekranie? Pokaż/Ukryj odpowiedź

Obiekty przekazywane przez referencję wypiszą na ekran nazwę domeny w jakiej zostały utworzone. Nawet jeśli wywołanie jest inicjowane w innej domenie to zostanie przekazne do domeny orginalej ponieważ pracujemy z proxy do obiektu. W przypadku obiektów przekazywanych przez wartość na ekran zostanie wypisana nazwa domeny w jakiej następuje wywołanie.

Jeśli chodzi o wartość wypisaną na ekran to obiekty przekazywane przez referencję zawsze wypiszą ten sam wynik = 1 ponieważ zarówno wywołanie metody Start jak i odczyt Value dotyczy tego samego obiektu. W przypadku obiektów przekazywanych przez wartość w niektórych przypadkach możemy otrzymać zero. Stanie się tak kiedy wywołanie Start nastąpi w innej domenie niż ta, w której odczytujemy właściwość Value. A dzieje sie tak ponieważ obie czynność dotyczą de facto innych obiektów.
Sandbox.vshost.exe
1
Sandbox.vshost.exe
1
TestDomain
1
TestDomain
1
TestDomain
0
Sandbox.vshost.exe
1
TestDomain
0
Sandbox.vshost.exe
1

25/11/2014

Trochę o wynagradzaniu w IT 2

Home

W tym poście będę kontynuował tematykę wynagradzaniu programistów i napiszę o tzw. bonusach na wejście oraz pakietach akcji (znanych też pod nazwą RSU - Restricted Stock Unit). W Polsce są to rzeczy, powiedziałbym, egzotyczne. O pakietach akcji to pewnie jeszcze sporo osób słyszało, ale o bonusie na wejście to pewnie mało kto.

Tak jak napisałem w poprzednim poście, obie te rzeczy są dodatkiem do systemu poziomów. Sprawiają, że jest bardziej elastyczny i dają pole do negocjacji, czego w systemie poziomów nie ma. Bonus na wejście, po pierwsze, pozwala przyciągnąć pracownika, jeśli goła pensja wynikająca z poziomu jest za niska, a jednak chcemy go zatrudnić. Bonus na wejście wypłacany jest w całości z pierwszą pensją albo proporcjonalne co miesiąc przez 1 lub 2 lata. Czasami jest tak, że w pierwszy roku bonus wynosi X, a w drugim mniej. Bonus potrafi stanowić bardzo ważną część pensji, nawet 30%!

Ktoś zapyta, a po co taki bonus skoro od razu można dać więcej? No właśnie nie można, bo system poziomów jest po to, aby się go trzymać. Jeśli robimy wyjątki to nie ma on sensu. Sensu nie ma też sztuczne zawyżanie poziomu, to byłoby jeszcze gorsze. W tym momencie dochodzimy do drugiej funkcji bonusu. Tak jak napisałem jest on czasowy i stanowi ważną część pensji. Po upływie 2 lat potencjalnie możemy więc ''stracić'' kawał grosza, o ile się nie wykażemy i nie awansujemy na kolejny poziom. Czyli ten bonus w założeniu działa dodatkowo jako motywator.

Teraz trochę o akcje. Ten element wynagrodzenia ma za zadanie związać pracownika z firmą. Chodzi o to, że na wejściu dostajesz na przykład pakiet 50 akcji, ale nie możesz ich od razu sprzedać. Dopiero po upływie 1 roku uzyskujesz dostęp na przykład do 10% z nich, po upływie 2 roku kolejne 15%, a w każdym kolejnym roku kolejne 25%. Systemy różnią się w zależności od firmy. Co więcej, co roku możemy dostać kolejny pakiet akcji. Załóżmy, że na wejściu dostaliśmy 50 akcji, a potem co roku po 50. Pracujemy już 5 i do tej pory nie sprzedawaliśmy niczego, a więc na koniec 5 roku mamy przypisanych 250 akcji i według moich obliczeń możemy sprzedać 170 z nich. A, jeśli pracujemy dla takiego Google, będzie to stanowić na dzisiaj około 90 tysięcy $, a akcje warte kolejne 42 tysiące $ będą na nas czekać i wciąż możemy dostawać kolejne porcje.

Z tego co wiem, zdarza się tak, że niektórzy pracownicy dostają co roku w akcjach nawet więcej niż ich pensja. Zauważcie, że celowo w pierwszych latach dostaje się mało akcji, aby ich nie marnować na pracowników, którzy się nie sprawdzą lub szybko odejdą. Równocześnie odchodząc z pracy po kilku latach mamy świadomość, że tyle, a tyle akcji na nas jeszcze czeka i jak tu zmienić pracę w takiej sytuacji ;)

Co o tym myślicie? Ja mam mieszane uczucia, szczególnie jeśli chodzi o bonus na wejście. Z drugiej strony to pewnie dlatego, że jestem przyzwyczajony do konkretnej kwoty w umowie, którą staram się maksymalizować. Przy systemie z bonusem na wejście i dodatkowymi akcjami bazowa pensja może być niższa, niż gdyby tych bonusów nie było i dochodzi też element niepewności. Jak zawsze jest coś za coś.

20/11/2014

Trochę o wynagradzaniu w IT

Home

W Polsce system wynagrodzeń w IT przeważnie wygląda tak. Przychodzi się do firmy i na wejściu dostaje się tyle, ile wynegocjowało się na początku. W niektórych firmach co roku można też liczyć na wyrównanie inflacyjne (z reguły kilka procent), a jeśli się posiedzi w firmie dłużej lub awansuje to jest szansa na większą podwyżkę. Czasami jest też tak, że pensje są zamrożone nawet prze kilka lat. Według mojego i ludzi z jakim rozmawiałem doświadczenia o dużą podwyżkę najłatwiej zmieniając pracę. Dodatkowe benefity to przeważnie opieka medyczna i karta Multisport lub podobna. Bywają też inne, ale, moim zdaniem, jeśli już są, to pełnią rolę wisienki na torcie niż czegoś co mogłoby rzeczywiście kogoś skusić. Oczywiście szczegóły zależą od firmy, ale moim zdaniem ten system jest mocno nieokreślony i bardzo dużo zależy od umiejętności negocjowania i momentu, kiedy przyszło się do firmy.

Mi do gustu przypadł system stosowany w niektórych, bo w cale nie wszystkich, firmach na zachodzie. Zacznę od tego, że opiera się na systemie poziomów, a od zdobytego poziomu bezpośrednio zależy nasza pensja. Nie jest to regułą, ale ja akurat spotkałem się ze skalą zaczynającą się w okolicach 8 oraz kończącą się w okolicach 20 i zawsze się zastanawiam z czego to wynika. Nawet o to pytałem, ale odpowiedzi były, jak dla mnie, mgliste. Na przykład spotkałem się z stwierdzeniem, że z powodów psychologicznych po poziom 1 to brzmi źle.

Ostatnio, całkiem przez przypadek, natknąłem się na kilka artykułów na ten temat napisanych, między innymi, przez Joel'a Spolsky'ego jednego z założycieli portalu stackoverflow.com. W firmie Joel'a algorytm liczenia poziomu jest funkcją 3 zmiennych tj. doświadczenia (liczonego w latach), umiejętności (ocenianych w zakresie 0-6) oraz zakresu obowiązków (również ocenianego w zakresie 0-6). A możliwe kombinacje zostały ujęte w taką tabelkę:



Ważne jest to, że pracowniczy na danym poziomie zarabiają tyle samo względem kwoty bazowej, która aktualizowana jest co jakiś czas. Czyli na przykład na poziomie dziewiątym zarabiasz X, na dziesiątym X+10%, a na jedenastym X+20% itd. Kwota X nie podlega dyskusji. Jeśli chce się zarabiać więcej to najłatwiej osiągnąć to przez zwiększenie swoich umiejętności. Zauważcie, że według powyższej tabelki wraz z kolejnymi latami doświadczenia poziom rośnie powoli w porównaniu do wzrostu umiejętności i zakresu obowiązków.

Istotne jest to, że osiągniecie maksymalnego poziomu nie oznacza, że programista przestaje być programistą i staje się menadżerem. Wprost odwrotnie, to oznacza, że jest wyjebistym programistą i będzie nadal robił to w czym jest najlepszy. Natomiast jak zaczynasz bez żadnego doświadczenia to dostajesz co prawda najniższy poziom, ale po pierwszym roku możesz skoczyć o 5 oczek do przodu, czyli tyle co ma ktoś z 15 latami doświadczenia, ale nie jest tak dobry jak ty.

Jak dla mnie takie postawienie sprawy to super rzecz. Każdy wie, na co może liczyć i mniej więcej przewidzieć, jak będzie wyglądała jego pensja w przyszłości. Można się pewnie czepiać, że ocena umiejętności jest subiektywna, że ktoś może nas nam zaniżyć ocenę... Ja na taki zarzut odpowiem tak: nie ma systemu idealnego i uważam, że ten jest dużo lepszy od tego co opisałem na początki postu. Natomiast jeśli uważasz, że ktoś może Ci celowo zaniżyć ocenę to lepiej bierz nogi za pas i zmień pracę.

Co o tym myślicie? Czy taki system mógłby przyjąć się w Polsce?

Na koniec trochę linków:
W kolejnym poście napiszę o systemie bonusów na wejście oraz opcji na akcje, które stanowią ważne uzupełnienie czystego systemu poziomów, a są bardzo mało znane w Polsce.

11/11/2014

Domeny aplikacyjne i wątki

Home

Jakiś czas temu odpowiedziałem na pytanie na stackoverflow.com dotyczące kończenia pracy wątków z zewnętrznej biblioteki w sytuacji kiedy powinny już zakończyć pracę, a jednak tego nie zrobiły. Moja odpowiedź została co prawda skrytykowana, zresztą słusznie, ale dzięki temu dowiedziałem się rzeczy, która mi wcześniej umknęła.

W mojej odpowiedzi zasugerowałem, że skoro z jakiegoś powodu musimy użyć zewnętrznej biblioteki i ona nie działa to ja bym ją załadował do oddzielnej domeny aplikacyjnej i w tej nowej domenie uruchomił też obliczenia (wątki). Taką domeną można natomiast w dowolnym momencie "odładować" przy pomocy AppDomain.Unload. Napisałem nawet przykład pokazujący, że to działa. W czym więc problem?

Otóż wywołanie AppDomain.Unload może się nie powieść i zostanie wtedy rzucony wyjątku CannotUnloadAppDomainException. Powodów mogą być trzy, ale nas interesuje jeden. Kiedy wołamy AppDomain.Unload i wewnątrz domeny są aktywne jakieś wątki to AppDomain.Unload spróbuje je ubić przy pomocy metody Thread.Abort. Jeśli się uda to ok, ale Thread.Abort może nie być w stanie ubić wątku i wtedy zostanie wygenerowany wzmiankowany powyżej wyjątek.

Kiedy Thread.Abort nie zadziała? Dokumentacja wspomina o dwóch przypadkach.
  • Kiedy wątek wykonuje kod niezarządzany.
  • Kiedy wątek wykonuje właśnie kod wewnątrz bloku finally. To można bardzo łatwo symulować przez umieszczenie wywołania Thread.Sleep(100000) wewnątrz bloku finally.

08/11/2014

Jako programista przyzwyczaiłem się do...

Home

Jakiś czas temu rozmawiałem z kolegą z branży i w pewnym momencie powiedział on zdanie, które utkwiło mi w pamięci. Parafrazując stwierdził, że:

"Jako programista przyzwyczaiłem się do tego, że się ze mną rozmawia."

Powiedział to w kontekście pracy w firmie, dla kogoś. Chodziło o to, że nawet jeśli coś mu się nie podobało, ale musiał to zrobić bo ktoś podjął taką decyzję, to nie bolało tak bardzo jeśli ktoś z nim wcześniej o tym porozmawiał. Mam tutaj na myśli zarówno sprawy techniczne co nasuwa się samo, ale również te "miękkie" na przykład związane z organizacją pracy.

Jak się nad tym chwilę zastanowiłem to stwierdziłem, że z pewnymi wyjątkami mam podobne doświadczenie i w sumie takie podejście wydało mi się oczywiste. Pytanie czy tak jest wszędzie? Może po prostu mieliśmy szczęście. Czy macie podobne wrażenia, a może wprost przeciwnie?

01/11/2014

Własne komendy w menu kontekstowym Windows'a

Home

Kiedy napiszę jakieś narzędzie to często chcę mieć do niego łatwy dostęp z poziomu menu kontekstowego Windows'a. Już od dłuższego czasu robię to przez dodanie odpowiednich wpisów do rejestru systemowego. W ogólności grzebanie w rejestrze nie jest bardzo przyjemne i istnieje ryzyko, że się coś popsuje. Dlatego też korzystam z następujących wypróbowanych wzorców.

Jeśli polecenie ma być dostępne dla wszystkich plików
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\*\Shell\COMMAND_NAME]
[HKEY_CLASSES_ROOT\*\Shell\COMMAND_NAME\command]
@="COMMAND"
Jeśli polecenie ma być dostępne dla określonych plików
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\.EXTENSION\Shell\COMMAND_NAME]
[HKEY_CLASSES_ROOT\.EXTENSION\Shell\COMMAND_NAME\command]
@="COMMAND"
Jeśli polecenie ma być dostępne dla katalogów
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\Directory\Shell\COMMAND_NAME]
[HKEY_CLASSES_ROOT\Directory\Shell\COMMAND_NAME\command]
@="COMMAND"

[HKEY_CLASSES_ROOT\Directory\Background\Shell\COMMAND_NAME]
[HKEY_CLASSES_ROOT\Directory\Background\Shell\COMMAND_NAME\command]
@="COMMAND"
W powyższych wzorcach COMMAND_NAME oznacza nazwę opcji jaka pojawi się w menu kontekstowym np.: Shred, EXTENSION to rozszerzenie pliku np.: txt i w końcu COMMAND to skrypt/polecenie jakie ma zostać wykonane po wybraniu przez użytkownika określonej opcji z menu kontekstowego np.: T:\\bin\\shred -n 10 -v -z \"%1\". Zwróćcie uwagę, że przy podawaniu ścieżki stosuję podwójny ukośnik tj. \\ oraz, że cudzysłów wewnątrz polecenia jest poprzedzony ukośnikiem tj. \". Na koniec uzupełnione wzorce należy zapisać w pliku z rozszerzeniem *.reg i przez podwójne kliknięcie na takim pliku dodać go do rejestru. Zmiany będą widoczne natychmiast.