Pull to refresh
114
0
Карлен Симонян @szKarlen

Пользователь

допустим, имена переменных — это совпадение, учитывая, что сами названия весьма простые.
учтем объяснение Ионеску с приведенного видео его выступления.


но почему подход внутренней реализации критических секций реализовано один в один с описанием, доступным (на странице Displaying a Critical Section § Interpreting Critical Section Fields in Windows XP and Windows 2000)?


так, функция RtlInitializeCriticalSectionAndSpinCount инициализирует LockCount равным значению -1.
https://github.com/reactos/reactos/blob/master/sdk/lib/rtl/critical.c#L576
В других функциях так же используется формула для проверки взята ли блокировка или нет.


во-первых, это весьма специфическая вещь не играющая большой роли.
во-вторых, само API не предоставляет никаких контрактов по поводу изначальных значений полей структуры. тем более, что MS поменяла поведение в Vista+.


сам очевидный способ инициализировать значение LockCount — используя значение 0.
почему -1? это же совсем неочевидно.


Raymond Chen дает объяснение этому хаку: все из-за поведения функций InterlockedIncrement/Decrement и совместимости c кодом аж Windows 3.1 и Windows 95.


последний коммит в файл critical.c отвечающий за строку #576 был сделан в 2005 году. сам заголовок файла указывает


* PROGRAMMERS:     Alex Ionescu (alex@relsoft.net)
*                  Gunnar Dalsnes

согласно вики ReactOS (https://reactos.org/wiki/User:Alex_Ionescu) Ионеску родился в 86 году. Инфы про Gunnar Dalsnes нет.


если допустить, что человек возраста 19 и менее лет на момент коммита знал всю страшную историю совместимости винды (и соотвественно имена страшных людей, из-за которых MS сохраняла совместимость) и даже сталкивался с этим, что смог сам по себе реализовать не совсем очевидную вещь рады отладки… то выглядит это нереалистично.


очевидно, что это не тот случай clean-room reimplementation в данном конкретном месте.

yeah, Task.Yield was the main performance pitfall in the original benchmarks.
btw, I'll be waiting for your next post about this topic)

hi alexyakunin, good to see you here!
feel free to reference this post :)

конечно же, нет! прост это форматирование поехало с новыми стилями Хабра.

ибо нех@#, чтобы какая-то по́гань, ставящая себя выше других людей, пыталась еще сделать себя священным.

ну это можно переписать как next = f(next). по сути, я оставил этот странный кусок, чтобы максимально синтаксически приблизить к примеру на Go.

изначально я запускал пример на F# без Server GC ((
вот что у меня получилось после правки:


 Method |   Count |     Mean |    Error |   StdDev |
------- |-------- |---------:|---------:|---------:|
     C# | 1000000 | 592.1 ms | 97.91 ms | 15.18 ms |
     F# | 1000000 | 243.0 ms | 91.02 ms | 15.04 ms |

csharp, fsharp


чуть добавил конфигурации для


F#
module Test

open BenchmarkDotNet
open BenchmarkDotNet.Running
open BenchmarkDotNet.Attributes
open BenchmarkDotNet.Configs
open BenchmarkDotNet.Jobs
open BenchmarkDotNet.Columns

let f input = 
    async {
        let! await = input
        return 1 + await
    }

type DefaultConfig() as self =
    inherit ManualConfig()
    do
        self.Add(Job.RyuJitX64.WithTargetCount(4).WithGcServer(true).WithGcForce(false))
        self.Add(BaselineScaledColumn.ScaledStdDev)

[<Config(typedefof<DefaultConfig>)>]
type Worker() = 
    [<Benchmark>]
    member public x.DoWork() =
        let src = async { return -1 }
        let first = f src
        let mutable output = first

        for i = 1 to x.Count do
            let input = f output
            output <- input

        output
        |> Async.Ignore
        |> Async.RunSynchronously

    [<Params(1000000)>]
    member val Count = 0 with get, set

[<EntryPoint>]
let main argv =
    let summary = BenchmarkRunner.Run<Worker>()
    printfn "%A" summary
    0
и
C#
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Columns;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Order;
using BenchmarkDotNet.Running;
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Threading.Tasks.Schedulers;

namespace Test
{
    [OrderProvider(SummaryOrderPolicy.FastestToSlowest)]
    [Config(typeof(Config))]
    public class Worker
    {
        private class Config : ManualConfig
        {
            public Config()
            {
                Add(Job.RyuJitX64.WithTargetCount(4).WithGcServer(true));
                Add(BaselineScaledColumn.ScaledStdDev);
            }
        }

        async Task<int> f(Task<int> input)
        {
            return 1 + await input; // return output
        }

        [Benchmark]
        public void DoWork()
        {
            Task.Factory.StartNew(() =>
            {
                var tcs = new TaskCompletionSource<int>();
                (var left, var right) = ((Task<int>)null, f(tcs.Task));
                for (var i = 0; i < Count; i++)
                {
                    left = f(right);
                    right = left;
                }
                tcs.SetResult(-1);
            }, CancellationToken.None, TaskCreationOptions.None, new StaTaskScheduler(2)).Wait();
        }

        [Params(1000000)]
        public int Count { get; set; }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var summary = BenchmarkRunner.Run<Worker>();
            Console.WriteLine(summary);
        }
    }
}

занятно! я попробую это дело прогнать у себя тоже.


p.s.
очевидно, что можно просто скопировать исходник StataskScheduler из ParallelExtensionsExtra прямо в проект :D

можно поинтересоваться характеристиками Вашей тестовой среды?
хотелось бы увидеть в таком случае результаты Go-бенчмарка как базиса тоже.

Думаю, рекомендовать книги Рихтера — вещь очевидная :)
Из блогов/публикаций настоятельно рекомендую PFX Team. Stephen Toub с командой весьма подробно разбирают подходы и принципы использования TPL и все, что с ним связано.
Серия статей по C# async от Stephen Cleary (автора библиотеки AsyncEx) также интересна.

мысль здравая. я и сам по возможности рекомендую библиотечку для бенчмарков.
При этом у меня не возникает чувство стыда из-за её неиспользования в данном случае.


вычислять персентили и т.п. по отношению к примеру на Go (выступающим базисом), где всего-то используется пакет time, выглядит, по-моему, неспортивно.
Особенно при выдимой невооруженным глазом разнице примерно в 2 раза в плане производительности.

.NET потребляет примерно ~300 MB в пике.

у меня Ваш F# пример выдает ~850-900 ms:


Может быть, изначально использовался неоптимизированный C#-пример?

Боюсь, ValueTask здесь не поможет: мы создаем цепочку вызовов, а не просто возвращаем скалярное либо др. примитивное значение.

так и причем здесь учителя?! не думаю, что люди


Не знали на кого пойти работать

просто идут работать на копеечную работу.


и вообще, обиженный — детектед.

Я все ждал, когда же в статье будет сравнение RandomAccess и SequentialScan, но его нет :(
Кстати, размеры буферов для чтения тоже влияют на производительность.


Сравнение количества потоков и используемого API (синхронное против асинхронного) лишь показывает, что мы в итоге упремся в ограничение роста производительности, т.е. закон Амдала. (кстати, тесты с 64/128/256 потоками наглядно это демонстрируют).

речь идет про кэш инструкций.

уважаемый Master_Dante!


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


… полу-невежда,
Полу-подлец, но есть надежда,
Что будет полным наконец.
©
это Micro Framework. он должен выполняться на устройствах, где памяти раз-два и обсчелся. По возможностям никакого отношения к стандартному CLR не имеет отношения.
про RMW и блокировку шины я подразумевал
>мгновенный сброс кеш-линий (или эмуляция оного для юзера)

То, что вы написали — правильно, но никак не отвечает на мой вопрос. Кеш-линии и так будут инвалидироваться при операциях записи. Мой поинт в том, что не каждая попытка захвата будет сбрасывать кеш-линию.

False sharing на уровне кешей L1 и L2 будет все равно.
Причем же тут «вина» блокировок? Обычно реализации и так используют выровненный доступ по размеру кеш-линии, чтобы случайно не аффектить приложение.

Information

Rating
Does not participate
Location
Москва, Москва и Московская обл., Россия
Registered
Activity