20.07.2018 Глобальный mutex
 
Есть сервер, есть программа, которая должна выполняться в одном экземпляре (например, работа с COM портом)
На сервере могут одновременно работать несколько пользователей ( RDP )
Использование Mutex решает проблему частично — т.е. блокируется запуск второго экземпляра программы, но только для пользователя, из-под которого эта программа запущена.
Т.е. в другой сессии рабочего стола эта программа запускается.
Как сделать, чтобы можно было запустить только один экземпляр для всей системы?

19.07.2018 NPOI Word картинка
 
https://stackoverflow.com/questions/23514781/add-image-to-word-docx-using-npoi-library-c-sharp

не понимаю этот код
во первых передаётся файл изображения и поток, почему не одно
во вторых как задать положение, место файла

18.07.2018 C# vNext nullable reference types
 
Кто еще не в курсе смотрите видос

https://youtu.be/CU5DEWZXgSI?t=39m19s

В новой версии будет опция, делающая ссылочные типы по умолчанию, не принимающими null.
Чтобы можно было передать null там где требуется string придется написать string?.
При обращении к optional-ref переменной компилятор будет проверять возможно ли обращение к null и ругаться.

18.07.2018 NPOI Word
 
Есть такая библиотека POI для Java и аналог для c# npoi. Не могу найти пример как поменять текст в Word по шаблону. Есть на java пример, но аналогов классов не нашёл, использовал reflector. Видел другие библиотеки npoi, какие-то дополнения, может в них?

17.07.2018 [ANN] linq2db.EntityFrameworkCore
 
Всем привет.

Мы наконец-то отрелизили первую версию сего гибрида и приглашаем попробовать.
Краткое введение тут linq2db.EntityFrameworkCore

Для чего это может пригодится:
  • вам нужно запустить linq запрос который, по каким-то загадочныы причинам, не работает в EF Core. Их много, включая проблемы с группировкой.
  • вам нужна скорость материализации обганяющая Dapper — смотреть картинки
  • вам нужна быстрая вставка тысяч или больше записей.
  • вам нужно удалить, изменить, скопировать тысячи записей по условию, когда ченж трекер делает обоим серверам нехорошо.
  • вам нужен быстренький linq запрос по системным таблицам, не добавлять же новые сущности в красивую, стройную, выплаканую неделями модель — не дай бог миграции завалятся.
  • вам нужен cross database запрос — мы это готовим.
  • вам нужны кастомные агрегаты, оконные функции, CTE и тд. — хоть кто-то знает SQL
  • вам нужна работа с временными таблицами.
  • вы устали писать хранимки на каждый «нестандартный» чих.

Все точно не перечислил, но главные преимущества надеюсь сюда попали.
Просьба писать о найденых недочетах или неудобствах на гитхаб

14.07.2018 Почему нельзя так писать?
 
в рамках одной функции
       var Fraction = new { pos = 0, neg = 0, zero = 0 };
       var result = arr.Select(a => a > 0 ? Fraction.pos++ : (a < 0 ? Fraction.neg++ : Fraction.zero++));

13.07.2018 как установить dotnet core 1.1.2 на ubuntu 18.04
 
Package 'liblldb-3.6' has no installation candidate
сам пакет могу скачать, но он не устанавливается на 18.04
нужели только 2.1 можно установить?

12.07.2018 как перехватить ответ с сервера
 
при помощи fiddler я получил ответ с веб сервиса, но в коде c# не могу этого сделать, потому что исключение в
clientSoap12.GetData(SearchINN);
There was an error while trying to deserialize parameter http://localhost/InfoTaxer:GetDataResponse. Please see InnerException for more details.
Inner Exception
'GeneratedMember' in type 'SearchINN.ServiceReference.EnumRefСтатусЗадолженности' cannot have EnumMemberAttribute attribute Value set to null or empty string.

public enum EnumRefСтатусЗадолженности : int {

[System.Runtime.Serialization.EnumMemberAttribute(Value="")]
GeneratedMember = 0,

public Nullable<SearchINN.ServiceReference.EnumRefСтатусЗадолженности> debt {
get {
return this.debtField;
}
set
{
if (value == null)
this.debtField = SearchINN.ServiceReference.EnumRefСтатусЗадолженности.GeneratedMember;

по правде не очень разбираюсь в веб сервисах

хотел получить хотя бы xml из ответа

вот что с сервера приходит

<m:debt/>

Если убрать Value="" в коде ниже
public enum EnumRefСтатусЗадолженности : int {

[System.Runtime.Serialization.EnumMemberAttribute(Value="")]
GeneratedMember = 0,

то исключение
Invalid enum value '' cannot be deserialized into type 'SearchINN.ServiceReference.EnumRefСтатусЗадолженности'. Ensure that the necessary enum values are present and are marked with EnumMemberAttribute attribute if the type has DataContractAttribute attribute.

11.07.2018 REST клиент
 
Чем сегодня модно пользоваться для REST в .NET ?
Я тут собрал набор разных библиотек.
Буду рад услышать кто чем пользуется и почему.

  • RestSharp library: http://restsharp.org/
  • HttpClient: https://docs.microsoft.com/en-us/dotnet/api/system.net.http.httpclient?view=netstandard-2.0
  • ASP.NET Web client: https://www.nuget.org/packages/Microsoft.AspNet.WebApi.Client/
  • Refit: https://github.com/reactiveui/refit
  • DalSoft.RestClient: https://github.com/DalSoft/DalSoft.RestClient
  • ServiceStack Http Utils: http://docs.servicestack.net/http-utils

  • 10.07.2018 WCF Unrecognized message version.
     
    в wdsl xmlns:soap12bind="http://schemas.xmlsoap.org/wsdl/soap12/" то есть SOAP1.2

    в коде
    return Message.CreateMessage(reader, maxSizeOfHeaders, MessageVersion);

    System.ServiceModel.CommunicationException: 'Unrecognized message version.'
    В отладчике
    MessageVersion.Envelope {Soap12 (http://www.w3.org/2003/05/soap-envelope)}

    проект
    https://github.com/dsalodki/INNsearch.git

    10.07.2018 WCF как задать SOAP12 для BasicHttpBinding
     
    или можно использоавать что-то другое?

    https://www.screencast.com/t/wh7fmByY

    WSDL содержит xmlns:soap12bind="http://schemas.xmlsoap.org/wsdl/soap12/"

    использую asp.net core 2.0 не знаю как использовать web.config

                var binding = new BasicHttpBinding();
                //binding.MessageVersion.Envelope
                binding.MaxReceivedMessageSize = 1048576;
                binding.SendTimeout = TimeSpan.FromSeconds(30);
                binding.Security.Mode = System.ServiceModel.BasicHttpSecurityMode.TransportCredentialOnly;
                binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
                binding.Security.Transport.ProxyCredentialType = HttpProxyCredentialType.Basic;
                var endpoint = new EndpointAddress(new Uri(""));
                InfoTaxerPortTypeClient client = new InfoTaxerPortTypeClient(binding, endpoint);
                client.ClientCredentials.UserName.UserName = "";
                client.ClientCredentials.UserName.Password = "";
                Data result = await client.GetDataAsync(innSearch);

    07.07.2018 [EF Core] Выборка данных
     
    Положим есть следующая процедура

    var data = _db.Table.Where(...)
                        .OrderByDescending(...).ThenByDescending(...);
    
    foreach (var v in data)
    {
    ... 
    }


    EF генерит sql вида Select * from Table Where ...

    Вопрос: в этом случае, если записей в выборку попадает много EF вытащит все записи на клиента сразу или будет их как-то подгружать частями, освобождая память от уже обработанных?
    Будет ли поведение отличаться от случая если в запросе написать

    var data = _db.Table.Where(...)
                        .OrderByDescending(...).ThenByDescending(...).ToList();


    Зависит ли это от используемой СУБД?

    07.07.2018 Какую версию .NET требовать в своем установщике ?
     
    Работаю над плагином к офису, и возник вопрос, какую .net распространять с плагином (или требовать у пользователя).
    Как я понимаю, чем меньше версия, тем лучше (но некоторые фичи будут недоступны) — выше вероятность, что она уже окажется у пользователя.
    Как определить из visual studio, какая версия .net мне нужна? Обычно приложения включают .net framework installer нужной версии в свой .msi установщик, или лучше просить скачать с сайта майкрософта самостоятельно?

    06.07.2018 Автоматическая генерация исходников без ручной сборки проекта
     
    Всем привет!

    Подскажите — в какое место можно воткнуть генерацию кода по шаблону (в нашем случае это M4) таким образом, чтобы для этого не приходилось вручную пересобирать проект.

    Я себе это вижу так — кастомная MSBuild-таска, которая стартится при попытке разрешить зависимость, которая проверяет, что файл шаблона изменился и заново генерирует дабавленные в проект исходники. Плюс запускает FSWatcher, который отслеживает изменения в этом файле.

    Но меня не покидает ощущение того, что это велосипед и существует более простое и изящное решение, которое работает из коробки.
    Повторюсь, что вариант со сборкой проекта не подходит — у человека не должно быть неизвестных типов при первом открытии проекта для резолва которых ему необходимо пересобрать проект.

    29.06.2018 2D-Linq и оптимизация цифровых фильтров - 3
     
    Ну, вот и пятница.

    Для начала, напустим интриги:
    BenchmarkDotNet=v0.10.14, OS=Windows 10.0.15063.1155 (1703/CreatorsUpdate/Redstone2)
    Intel Core i7-6600U CPU 2.60GHz (Skylake), 1 CPU, 4 logical and 2 physical cores
    Frequency=2742189 Hz, Resolution=364.6722 ns, Timer=TSC
      [Host]       : .NET Framework 4.6.1 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.2671.0
      LegacyJitX86 : .NET Framework 4.6.1 (CLR 4.0.30319.42000), 32bit LegacyJIT-v4.7.2671.0
    
    Job=LegacyJitX86  Jit=LegacyJit  Platform=X86
    Runtime=Clr
    
         Method |     Mean |     Error |    StdDev | Scaled |    Gen 0 |    Gen 1 |    Gen 2 | Allocated |
    ----------- |---------:|----------:|----------:|-------:|---------:|---------:|---------:|----------:|
     UnsafeLinq | 4.256 ms | 0.0849 ms | 0.0978 ms |   0.43 | 570.3125 | 375.0000 | 375.0000 |   3.83 MB |
        Perfect | 9.989 ms | 0.1198 ms | 0.1120 ms |   1.00 | 156.2500 | 156.2500 | 156.2500 |   3.81 MB |

    Вот код метода UnsafeLinq:
    public int[,] UnsafeLinq() => from d in data select (d[-1, 0] + d[1, 0] + d[0, -1] + d[0, 1]) / 4;

    Всё то же самое!

    Чёрт возьми, Холмс, как? Linq оказался вдвое быстрее "идеального варианта"...

    А вот, примерно, так:

            public static Filter<int, int> GenerateUnsafeFilter(Kernel<int, int> kernel)
            {
                var km = KernelMeasure.Measure(kernel);
    
                var ab = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("FixedTest"), AssemblyBuilderAccess.RunAndCollect);
                var tb = ab.DefineDynamicModule("FixedTest", "FixedTest.dll").DefineType("Filter." + kernel.Method.Name, TypeAttributes.Class | TypeAttributes.Public);
                var dm = tb.DefineMethod("Filter", MethodAttributes.HideBySig | MethodAttributes.Public | MethodAttributes.Static, typeof(int[,]), new Type[] { typeof(object), typeof(int[,]) });
    
                var ilg = dm.GetILGenerator();
    
                var target = ilg.DeclareLocal(typeof(int[,]), true);
                var source = ilg.DeclareLocal(typeof(int[,]), true); // a replica of the arg1, to force the GC pinning
                var psource = ilg.DeclareLocal(typeof(int*));
                var ptarget = ilg.DeclareLocal(typeof(int*));
                var i_var = ilg.DeclareLocal(typeof(int));
                var j_var = ilg.DeclareLocal(typeof(int));
                var h_var = ilg.DeclareLocal(typeof(int));
                var w_var = ilg.DeclareLocal(typeof(int));
                var W_var = ilg.DeclareLocal(typeof(int));
    
                ilg.Emit(OpCodes.Ldarg_1); // source
                ilg.Emit(OpCodes.Ldc_I4_0);// 0
                ilg.Emit(OpCodes.Call, typeof(int[,]).GetMethod("GetLength", new Type[] { typeof(int) }));
                ilg.Emit(OpCodes.Stloc, h_var);
    
    
                ilg.Emit(OpCodes.Ldarg_1); // source
                ilg.Emit(OpCodes.Ldc_I4_1);// 0
                ilg.Emit(OpCodes.Call, typeof(int[,]).GetMethod("GetLength", new Type[] { typeof(int) }));
                ilg.Emit(OpCodes.Stloc, w_var);
    
                ilg.Emit(OpCodes.Ldloc, h_var);
                ilg.Emit(OpCodes.Ldloc, w_var);
                ilg.Emit(OpCodes.Newobj, typeof(int[,]).GetConstructor(new Type[] { typeof(int), typeof(int) }));
                ilg.Emit(OpCodes.Stloc, target);
    
                ilg.Emit(OpCodes.Ldloc, w_var);
                ilg.Emit(OpCodes.Stloc, W_var);
    
                ilg.EmitIncrease(h_var, -km.ymax);
                ilg.EmitIncrease(w_var, -km.xmax);
    
    
                ilg.Emit(OpCodes.Ldarg_1);
                ilg.Emit(OpCodes.Stloc_S, source); // pinning the source array
    
                ilg.EmitFor(i_var, 0 - km.xmin, h_var, () =>
                {
    
                    ilg.Emit(OpCodes.Ldloc, source);
                    ilg.Emit(OpCodes.Ldloc, i_var);
                    ilg.Emit(OpCodes.Ldc_I4, -km.ymin);
                    ilg.Emit(OpCodes.Call, _arrayAddressGetter);
                    ilg.Emit(OpCodes.Conv_U);
                    ilg.Emit(OpCodes.Stloc, psource);  // psource = &source[i, lb];
    
                    ilg.Emit(OpCodes.Ldloc, target);
                    ilg.Emit(OpCodes.Ldloc, i_var);
                    ilg.Emit(OpCodes.Ldc_I4, -km.ymin);
                    ilg.Emit(OpCodes.Call, _arrayAddressGetter);
                    ilg.Emit(OpCodes.Conv_U);
                    ilg.Emit(OpCodes.Stloc, ptarget); // ptarget = &target[i, lb]
    
                    ilg.EmitFor(j_var, 0 - km.ymin, w_var, () =>
                    {
                        ilg.Emit(OpCodes.Ldloc_S, ptarget); // :ptarget
    
                        var usilv = new UnsafeInliningILInstructionVisitor(ilg, i_var, j_var, psource, W_var);
                        new ILReader(kernel.Method).Accept(usilv);
                        //EmitDebug<int>(ilg);
    
                        ilg.Emit(OpCodes.Stind_I4);
    
                        ilg.EmitIncrease(psource, 4); // psource++
                        ilg.EmitIncrease(ptarget, 4); // ptarget++
                    });
                });
    
                ilg.Emit(OpCodes.Ldloc, target);
                ilg.Emit(OpCodes.Ret);
    
                var type = tb.CreateType();
                var mi = type.GetMethod("Filter");
                var inlined = (Func<object, int[,], int[,]>)mi.CreateDelegate(typeof(Func<object, int[,], int[,]>));
    
                int[,] filter(int[,] data) => inlined(kernel.Target, data);
                return filter;
            }

    Внимательный взгляд, конечно же, увидит, что у делегатов Filter и Kernel теперь появилось два параметра. Это отражение развития, которое идёт в параллельной ветке обсуждения, где тип выходного массива не обязательно совпадает с типом входного.
    Кроме того, в отличие от GenerateFilter, новая версия метода уже не-generic. В той версии C#, которая у меня, невозможно выразить констреинт "тип, от которого можно взять managed pointer", поэтому пришлось гвоздями прибить метод к int-у.
    Тем не менее, с задачей с4-фильтрации исходного массива метод справился на ура.
    Мы добавляем немного чёрной магии при помощи pinned переменных — результат сразу сохраняется в такой переменной, а входные данные приходится скопировать из arg.1 в переменную source.
    В цикле — развлекаемся адресной арифметикой, зная внутреннее устройство двумерных массивов в CLR.
    Таким образом, мы избегаем проверок на выход за границы массива, которые, судя по показаниям профайлера, съедают примерно 50% времени работы "безопасной" версии метода.
    Что характерно, пользовательский код остаётся вполне себе Safe — всё вуду происходит только "под капотом".

    Замена вызова при инлайне происходит тоже чуточку посложнее:
                protected override void EmitSourceAccessCode()
                {
                    //          Opcode                   Stack state (head left)
                    //                                     dy, dx, psource, ...
                    Target.Emit(OpCodes.Ldloc, _w);    // w, dy, dx, psource
                    Target.Emit(OpCodes.Mul);           // w*dy, dx, psource, ...
                    Target.Emit(OpCodes.Add);           // w*dy+dx, psource, ...
                    Target.Emit(OpCodes.Ldc_I4_4);      // 4, dy + w*dx, psource, ...
                    Target.Emit(OpCodes.Mul);           // 4 * (w*dy +  dx), psource, ...
                    Target.Emit(OpCodes.Add);           // psource + 4 * dy + 4*w*dx, ...
                    Target.Emit(OpCodes.Ldind_I4);      // psource[dy*w+dx]
                }

    Понятно, что программист-фанатик может написать версию этого кода и на C#, но это — скользкий путь: в коде всё меньше остаётся от исходной задачи, и всё больше добавляется подробностей реализации.
    Всё больше шансов запороть программу, внося изменения (например, меняя ядро фильтра), всё больше опасность разрушить память, вылетая за границы массива.

    Итак, принципиальная возможность "обогнать" ручное выпиливание циклов при помощи красивого Linq — продемонстрирована.
    В следующей серии я попробую показать, как Linq офигенен для решения более сложных задач
    Автор: Pavel Dvorkin
    Дата: 29.06 07:04
    , несмотря на сомнения скептиков.

    28.06.2018 EF Core. Разность строк одной таблицы
     
    Есть две таблицы. Мастер — деталь. В детали — число предметов на определенную дату.
    Как с помощью EF и Linq посчитать разницу предметов по датам? Т.е. фактически нужно вычесть из сегодняшнего числа вчерашнее. И так для каждой записи мастера. Записей может быть много.
    И при этом не положить ни сервер бд ни сервер приложения.
    СУБД — MariaDb

    28.06.2018 agsXMPP - не могу поймать исключение
     
    есть приложение, sta, .net-4.0, winforms
    в нем используется jabber клиент agsXMPP
    подписки на AppDomain.CurrentDomain.UnhandledException и Application.ThreadException есть.
    иногда, при проблемах со сетью из недр agsXMPP падает исключение, которое не где не отлавливается и не попадает в логи.
    приложение падает, в журнале событий появляется эксепшен.

    Приложение: ....
    Версия платформы: v4.0.30319
    Описание. Процесс был завершен из-за необработанного исключения.
    Сведения об исключении: System.NullReferenceException
    
    
    в agsXMPP.Xml.StreamParser.Push(Byte[], Int32, Int32)
       в agsXMPP.XmppConnection.SocketOnReceive(System.Object, Byte[], Int32)
       в agsXMPP.net.BaseSocket.FireOnReceive(Byte[], Int32)
       в agsXMPP.net.ClientSocket.EndReceive(System.IAsyncResult)
       в System.Net.LazyAsyncResult.Complete(IntPtr)
       в System.Net.ContextAwareResult.CompleteCallback(System.Object)
       в System.Threading.ExecutionContext.runTryCode(System.Object)
       в System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode, CleanupCode, System.Object)
       в System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)
       в System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
       в System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)
       в System.Net.ContextAwareResult.Complete(IntPtr)
       в System.Net.LazyAsyncResult.ProtectedInvokeCallback(System.Object, IntPtr)
       в System.Net.Sockets.BaseOverlappedAsyncResult.CompletionPortCallback(UInt32, UInt32, System.Threading.NativeOverlapped*)
       в System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32, UInt32, System.Threading.NativeOverlapped*)


    я не понимаю, как я должен поймать это исключение. почему все приложение у меня падает?
    может подскажите — куда копать.
    у меня только две идеи:
    1. копать внутрь agsXMPP и что-то там фиксить
    2. работу с agsXMPP вывести в отдельный процесс.

    как-так, я не могу управлять всеми исключениями в управляемом приложении.
    может подскажите — куда копать? т.е. принципиально, нужно чтобы приложение не падало все.

    28.06.2018 Совмещение кода на C# и С++
     
    Пишу некое расширение для среды разработки. Ядро с логикой на С++, и к ней "фронтэнды" для разных IDE, в частности для QtCreator уже есть и надо делать для Visual Studio. Причем скорее всего на C#, поскольку большинство примеров аддинов для студии именно на C#.
    Вопрос — как это совместить с точки зрения организации кода?
    Опыта на C# почти нет, поэтому задаю вопрос в этом разделе.
    Разумеется крайне желательно удобство собрки и отладки. Идеально просто чтобы в одном проекте лежали файлы cs и cpp, но такое вроде нельзя. А как можно и как лучше сделать? Через dll и писать обертки в c#-коде? Очень уж не хочется писать вручную обертки, хочется чтобы все было автоматически.

    27.06.2018 2D-Linq и оптимизация цифровых фильтров - 2
     
    Продолжаем размышления
    Автор: Sinclair
    Дата: 25.06 14:16
    .
    По факту, в прошлый раз мы получили только первую половину заголовка — 2D-Linq.
    Теперь поговорим об оптимизации.
    Отставание в 2.3 — 2.7 раза вызвано, судя по всему, наличием косвенного вызова. Как хорошо известно, вызовы делегатов инлайнингу не подлежат.
    Мотивация для этого устарела примерно лет десять назад, но ограничения, с которыми сталкивается JIT, всё те же.
    Поэтому внутри нашего плотного цикла идут постоянные косвенные вызовы всё одного и того же kernel, переданного нам снаружи.
    В отличие от JIT и C#, мы точно знаем, сколько раз будет исполняться этот один и тот же делегат; нам может оказаться выгодным динамически построить код, похожий на наш PerfectFourNeighborAverage, и исполнить его.
    Сказано — сделано.

    Опытные джедаи наверняка бы заменили сигнатуру метода Select так, чтобы она принимала Expression<Kernel<T>> вместо делегата, и на лету собрали бы код итерирования из циклов и выражений.
    К сожалению, моей квалификации на это не хватило, поэтому работать мы будем на уровне MSIL.
    Оболочка решения — метод GenerateFilter():
    public static Filter<T> GenerateFilter<T>(Kernel<T> kernel)
    {
      var km = KernelMeasure.Measure(kernel);
    
      DynamicMethod dm = new DynamicMethod("Filter"+kernel.Method.Name, typeof(T[,]), new Type[] { typeof(object), typeof(T[,]) });
    
      var ilg = dm.GetILGenerator();
    
      var result_var = ilg.DeclareLocal(typeof(T[,]));
      var i_var = ilg.DeclareLocal(typeof(int));
      var j_var = ilg.DeclareLocal(typeof(int));
      var h_var = ilg.DeclareLocal(typeof(int));
      var w_var = ilg.DeclareLocal(typeof(int));
    
      ilg.Emit(OpCodes.Ldarg_1); // source
      ilg.Emit(OpCodes.Ldc_I4_0);// 0
      ilg.Emit(OpCodes.Call, typeof(T[,]).GetMethod("GetLength", new Type[] { typeof(int) }));
      ilg.Emit(OpCodes.Stloc, h_var);
    
    
      ilg.Emit(OpCodes.Ldarg_1); // source
      ilg.Emit(OpCodes.Ldc_I4_1);// 1
      ilg.Emit(OpCodes.Call, typeof(T[,]).GetMethod("GetLength", new Type[] { typeof(int) }));
      ilg.Emit(OpCodes.Stloc, w_var);
    
      ilg.Emit(OpCodes.Ldloc, h_var);
      ilg.Emit(OpCodes.Ldloc, w_var);
      ilg.Emit(OpCodes.Newobj, typeof(T[,]).GetConstructor(new Type[] { typeof(int), typeof(int) }));
      ilg.Emit(OpCodes.Stloc, result_var);
    
      ilg.EmitIncrease(h_var, -km.ymax);
      ilg.EmitIncrease(w_var, -km.xmax);
    
      ilg.EmitFor(i_var, 0-km.xmin, h_var, () =>
      {
        ilg.EmitFor(j_var, 0-km.ymin, w_var, () =>
        {
          ilg.Emit(OpCodes.Ldloc, result_var);
          ilg.Emit(OpCodes.Ldloc, i_var);
          ilg.Emit(OpCodes.Ldloc, j_var);
          var ki = new KernelInliningILInstructionVisitor<T>(ilg, i_var, j_var); // самое интересное - вот тут!
          new ILReaderkernel.Method).Accept(ki);
    
          ilg.Emit(OpCodes.Call, typeof(T[,]).GetMethod("Set", new Type[] { typeof(int), typeof(int), typeof(T) }));
        });
      });
    
      ilg.Emit(OpCodes.Ldloc, result_var);
      ilg.Emit(OpCodes.Ret);
    
      var inlined = (Func<object, T[,], T[,]>)dm.CreateDelegate(typeof(Func<object, T[,], T[,]>));
      return (data) => inlined(kernel.Target, data);
    }

    В целом — ничего военного: метод просто порождает примерно такой же MSIL-код, который генерирует для PerfectFourNeighborAverage() компилятор C#:
    — вычисляем размеры исходного массива
    — создаём целевой массив такого же размера
    — уменьшаем переменные "ширины" и "высоты" на соответствующие размеры ядра фильтра
    — строим два вложенных цикла.
    Для упрощения порождения кода написан класс ILWriter с парой хелпер-методов вроде EmitFor и EmitIncrease, которые прячут под капот рутинную работу по назначению меток и типовым последовательностям загрузки переменной/загрузки слагаемого/сложению/сохранению.

    Интересная часть — встраивание кода ядра в код нашего метода:
          var ki = new KernelInliningILInstructionVisitor<T>(ilg, i_var, j_var); 
          new ILReader(kernel.Method).Accept(ki);

    Она основана на пакете ILReader, который позволяет выполнять обход инструкций MSIL-потока.
    Класс KernelInliningILInstructionVisitor реализует следующую стратегию обхода:
    1. Все инструкции копируются в переданный ему ILGenerator.
    2. Каждая скопированная инструкция снабжается меткой (ilg.MarkLabel()), и её смещение запоминается во внутренней таблице. Сделано это для того, чтобы аккуратно скопировать различные branch-инструкции: при чтении потока меток нет, есть только смещения.
    3. Инструкция ret выбрасывается при копировании; результат метода просто остаётся на стеке — в точности так, как было бы после реального вызова
    4. Делается ещё один трюк: замена операции.
      В первоначальном варианте кода метода Select в kernel передавалась ссылка на IRelativeAccess2d. В принципе, можно было бы её так и оставить — сконструировать локальную переменную, обновлять её поля на каждой итерации, и заменить все инструкции ldard.1 на соответствующий ldloc. Пусть дальше JIT разбирается.
      Но из врождённой паранойи и недоверию к JIT, я заменяю вызовы метода IRelativeAccess2d<T>.get_Item на вызовы метода T[,].Get().
    При этом нам известно, что в момент вызова у нас в стеке лежат значения arg.1, dx, dy. Перед вызовом их надо заменить на data, i+dx, j+dy.
    Волшебным образом data у нас как раз лежит в arg.1 генерируемого метода, поэтому его можно не трогать. А для того, чтобы заменить аргументы на стеке, в метод эмиттится следующая последовательность инструкций:
    Target.Emit(OpCodes.Stloc, dy); // заводим ещё одну переменную для временного хранения dy. стек после операции: [data, dx].
    Target.Emit(OpCodes.Ldloc, i); // [data, dx, i]
    Target.Emit(OpCodes.Add);      // [data, i+dx]
    Target.Emit(OpCodes.Ldloc, j); // [data, i+dx, j]
    Target.Emit(OpCodes.Ldloc, dy); // [data, i+dx, j, dy]
    Target.Emit(OpCodes.Add);       // [data, i+dx, j+dy]
    Target.Emit(OpCodes.Call, _arrGetter); // [data[i+dx, j+dy]]

    Последнее упражнение — привязка к контексту. Метод в kernel может быть любым, в том числе и методом экземпляра (в нашем случае так и есть, несмотря на то, что замыкать нечего). Его код запросто может обратиться к this, поэтому нельзя просто встроить его as is. Поэтому у порождаемого DynamicMethod не один параметр, а два — первым выступает Target делегата, переданного в Kernel. Поэтому из GenerateFilter возвращается не сам DynamicMethod, а локальная функция, добавляющая к data оригинальный Target:
      return (data) => inlined(kernel.Target, data);

    Теперь метод Select выглядит до неприличия простым:
    public static T[,] Select<T>(this T[,] source, Kernel<T> kernel) => GenerateFilter(kernel)(source);

    Ну, а теперь — к десерту:
    // * Summary *
    BenchmarkDotNet=v0.10.14, OS=Windows 10.0.15063.1088 (1703/CreatorsUpdate/Redstone2)
    Intel Core i7-6600U CPU 2.60GHz (Skylake), 1 CPU, 4 logical and 2 physical cores
    Frequency=2742190 Hz, Resolution=364.6720 ns, Timer=TSC
      [Host]       : .NET Framework 4.6.1 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.2650.0
      LegacyJitX86 : .NET Framework 4.6.1 (CLR 4.0.30319.42000), 32bit LegacyJIT-v4.7.2650.0
    
    Job=LegacyJitX86  Jit=LegacyJit  Platform=X86
    Runtime=Clr
    
       Method |     Mean |     Error |    StdDev | Scaled | ScaledSD |     Gen 0 |    Gen 1 |    Gen 2 | Allocated |
    --------- |---------:|----------:|----------:|-------:|---------:|----------:|---------:|---------:|----------:|
      Perfect | 10.42 ms | 0.2342 ms | 0.2505 ms |   1.00 |     0.00 |  156.2500 | 156.2500 | 156.2500 |   3.81 MB |
     SafeLinq | 10.50 ms | 0.2057 ms | 0.2368 ms |   1.01 |     0.03 |  156.2500 | 156.2500 | 156.2500 |   3.81 MB |
         Linq | 30.95 ms | 0.6184 ms | 1.0160 ms |   2.97 |     0.12 | 9687.5000 | 250.0000 | 250.0000 |  22.81 MB |

    Итого, быстродействие порождённого метода — в пределах стат.погрешности по отношению к "идеальному" варианту!
    Даже кэшировать порождённый динамический метод нам не нужно — при таких размерах массивов генерация и JIT занимает пренебрежимо малое время.

    Но стоит ли останавливаться на достигнутом? В следующей серии мы посмотрим, можно ли выполнить from d in data select (d[-1, 0] + d[1, 0] + d[0, 1] + d[0, -1]) / 4 быстрее, чем "простейший двойной цикл с одним оператором внутри", который "Пишется "на автомате", почти не думая, так как подобное любой опытный программист писал много раз."

    25.06.2018 2D-Linq и оптимизация цифровых фильтров
     
    Этот пост, по-хорошему, заслуживает место во flame.comp, т.к. побудительным мотивом изначально являлся вопрос alex_public, заданный в недрах флейма в том форуме:

    Но могу для разнообразия подкинуть ещё один примерчик для понимания. Простейшая, но очень нужная во многих областях задачка: имеем двухмерный массив чисел и надо получить из него новый массив, в котором каждая точка является средним от своих соседей (4-ёх или 8-ми, не суть). Что об этом скажет linq? )))

    (https://rsdn.org/forum/flame.comp/7137817.1
    Автор: alex_public
    Дата: 07.05 20:18
    )

    Но, поскольку речь идёт об пределах возмжностей кода на C#, я всё же запощу это сюда — в интересах тех, кого тоже волнует вопрос о производительности "математического" кода на дотнете, а перечитывать топики глубиной за сотню постов терпения нет.

    Итак, что об этом скажет Linq? Вопрос, понятное дело, с подковыркой — типа, да Linq же — это убогая надстройка над IEnumerable, которую потом и соплями прикрутили к RDBMS. А тут у нас — принципиально другой материал, неперечислимый и, в отличие от, скажем, графов, к перечислениям несводимый.

    Лично моими устами об этом Linq скажет следующее:
    int[,] FourNeighborAverage(int[,] data) => from d in data select (d[-1, 0] + d[1, 0] + d[0, 1] + d[0, -1]) / 4;


    Как это работает?
    Ну, для начала у нас есть public static класс Array2d, где определён вот такой вот extension метод:
    public static T[,] Select<T>(this T[,] source, Kernel<T> kernel)

    То есть выражение d => (d[-1, 0] + d[1, 0] + d[0, 1] + d[0, -1]) / 4 трактуется как Kernel<int>:
    public delegate T Kernel<T>(IRelativeAccess2d<T> point);

    В свою очередь, интерфейс IRelativeAccess2d<T> определён весьма незатейливо:
    public interface IRelativeAccess2d<T>
    {
       T this[int dx, int dy] { get; } 
    }

    Теперь всё становится понятным: в наш Kernel передаётся интерфейс для доступа к "соседним" относительно текущего элементам массива, что позволяет нам описывать выражения типа "среднее от своих четырёх соседей".

    Реализовать наш метод Select не очень трудно. Наивная реализация выглядит так:
    public static T[,] Select<T>(this T[,] source, Kernel<T> kernel)
    {
      var h = source.GetLength(0);
      var w = source.GetLength(1);
      var result = new T[h, w];
      for (int i = 0; i < h; i++)
        for (int j = 0; j < w; j++)
          result[i, j] = kernel(new RelativeArrayAccess2d<T>(source, i, j));
      return result;
    }

    структура RelativeArrayAccess2d<T> слишком тривиальна, чтобы её приводить — она хранит ссылку на массив source и координаты текущего элемента; при обращении по this[dx, dy] мы прибавляем относительные координаты к текущим и возвращаем элемент.
    Этого кода уже достаточно для того, чтобы реализовать копирование массива в стиле linq:
    from d in data select d[0, 0];

    К сожалению, первый же запуск с ядром усреднения вылетит с ArrayIndexOutOfBoundsException. Логично — мы пробуем обращаться за пределы исходного массива. Суровые программисты на плюсах на такие мелочи внимания не обращают — ну, или требуют от программиста ручного указания размеров "каёмки", в которой фильтр не работает.
    Правильное решение этой задачи — довольно сложно: нам придётся учиться подпиливать фильтр на ходу таким образом, чтобы он корректно работал на границах массива (у элемента рядом со "стороной" прямоугольника не 4, а 3 соседа, а у углового элемента — только 2. Строго говоря, именно их и нужно усреднять).
    Но пока что мы обойдёмся решением в стиле С++ — ограничением области вывода фильтра.
    Сделать это не очень сложно — мы просто один раз вызовем kernel со специальной реализацией IRelativeAccess, которая запоминает, куда идёт обращение:
    private sealed class KernelMeasure<T> : IRelativeAccess2d<T>
    {
      ...
      T IRelativeAccess2d<T>.this[int x, int y]
      {
        get
        {
          xmin = Math.Min(xmin, x);
          xmax = Math.Max(xmax, x);
          ymin = Math.Min(ymin, y);
          ymax = Math.Max(ymax, y);
          return default(T);
        }
      }
    }

    Вообще говоря, это будет работать не для всех типов Kernel — например, мы могли использовать Math.Random() для выбора смещения. Но для практически интересных случаев, когда смещения всегда постоянны, этот подход сработает.
    Итого, у нас получается более-менее работоспособный код, который позволяет удобно записывать формулы фильтрации:
    public static T[,] Select<T>(this T[,] source, Kernel<T> kernel)
    {
      var h = source.GetLength(0);
      var w = source.GetLength(1);
      var km = new KernelMeasure<T>();
      kernel(km); // провели измерения
      var t = 0 - km.xmin;
      var b = h - km.xmax;
      var l = 0 - km.ymin;
      var r = w - km.ymax;
      var result = new T[h, w];
    
      for (int i = t; i < b; i++)
        for (int j = l; j < r; j++)
          result[i, j] = kernel(new RelativeArrayAccess2d<T>(source, i, j));
      return result;
    }

    На этом можно было бы и остановиться, если бы не ехидный комментарий в том же флейме на 30 уровней выше
    Автор: alex_public
    Дата: 28.03 17:37
    :

    Ну как же, если мы говорим про работу с коллекциями (а не про СУБД), то обычный императивный код (типа for() ... ) никуда не делся. И при этом он по прежнему является как наиболее обобщённым, так и наиболее производительным.

    CHALLENGE ACCEPTED!
    По поводу "обобщённости" спорить смысла нет — очевидно, что приведённый from d in data select... можно без изменений заставить работать не только с 2d-массивом, но и с произвольной структурой, которая представляет из себя двумерную сетку, индексированную целочисленными координатами. Например, мы можем работать с "изображением" из double с размерами миллион на миллион, то есть имея 10^12 точек или около 7 терабайт. Перебор их циклом for упрётся даже не во время, а в размер памяти и диска локальной машины. Так что мы пилим всё на тайлы, раскидываем по нескольким тысячам серверов, отправляем код для исполнения, дожидаемся, мёрджим. Ну, то есть получаем опять хэндл чудовищного "холста" для дальнейших операций — например, edge-detect, а потом ещё свёртка, а потом понижение размерности, и уже в конце получаем что-нибудь приемлемое для памяти локальной машины. И всё — в компактном и удобном для чтения (и написания) виде.

    Вопрос у нас к тому пункту, где про "наиболее производительным".
    Давайте напишем тот идеальный императивный код, который бы написал на C# программист, не доверяющий всем этми новомодным линкам, с их "динамикой и рефлексией":
    static int[,] PerfectFourNeighborAverage(this int[,] source)
    {
      var h = source.GetLength(0);
      var w = source.GetLength(1);
      var result = new int[h, w];
    
      for (int i = 1; i < h-1; i++)
        for (int j = 1; j < w-1; j++)
          result[i, j] = (source[i, j-1] + source[i, j+1] + source[i-1, j] + source[i+1,j])/4;
      return result;
    }

    Возьмём случайный массив размером 1000*1000, и попробуем сравнить производительность нашего "обобщённого" кода со "специфичным":
    BenchmarkDotNet=v0.10.14, OS=Windows 10.0.15063.1088 (1703/CreatorsUpdate/Redstone2)
    Intel Core i7-6600U CPU 2.60GHz (Skylake), 1 CPU, 4 logical and 2 physical cores
    Frequency=2742190 Hz, Resolution=364.6720 ns, Timer=TSC
      [Host]     : .NET Framework 4.6.1 (CLR 4.0.30319.42000), 32bit LegacyJIT-v4.7.2650.0
      DefaultJob : .NET Framework 4.6.1 (CLR 4.0.30319.42000), 32bit LegacyJIT-v4.7.2650.0
    
         Method |      Mean |     Error |    StdDev | Scaled | ScaledSD |
    ----------- |----------:|----------:|----------:|-------:|---------:|
        Perfect | 15.008 ms | 0.1706 ms | 0.1512 ms |   1.00 |     0.00 |
           Linq | 33.423 ms | 0.6599 ms | 0.8345 ms |   2.23 |     0.06 |

    Ну, на первый взгляд, linq справился не так уж и плохо — всего лишь в 2.23 раза медленнее.
    Тем не менее, эта незначительная цена за удобную абстракцию легко может отпугнуть фанатов тактовыжимания.

    Поэтому в следующей серии я попробую показать, что можно с этим сделать, ничего не меняя в коде бенчмарка. То есть те самые преимущества, которые бесплатны для прикладного программиста — от него потребуется разве что перекомпилировать свой проект. Никаких хинтов в коде — ни императивного развёртывания циклов, ни ручной векторизации, ни даже умелой расстановки "декларативных" прагм.
    Только хардкор, только голый from d in data select (d[-1, 0] + d[1, 0] + d[0, 1] + d[0, -1]) / 4.