lunditoph-siago / Sia.NET

Modern ECS framework for .NET
BSD 3-Clause "New" or "Revised" License
8 stars 1 forks source link

[Bug]: Unexpected Action behaviour #1

Closed Sieluna closed 7 months ago

Sieluna commented 7 months ago

Description

Throw an index out of range exception by following code.

public partial record struct Component1(int Value);

public partial record struct Component2(int Value);

public partial record struct Padding1();

public partial record struct Padding2();

public partial record struct Padding3();

public partial record struct Padding4();

public sealed class MultiThreadUpdateSystem()
    : SystemBase(matcher: Matchers.Of<Component1, Component2>())
{
    public override void Execute(World world, Scheduler scheduler, IEntityQuery query)
    {
        query.ForSliceOnParallel((ref Component1 c1, ref Component2 c2) => {
            c1.Value += c2.Value;
        });
    }
}

public static void Run(World world)
{
    int entityCount = 10000;
    for (int i = 0; i < entityCount; ++i) {
        switch (i % 4)
        {
            case 0:
                world.CreateInArrayHost(Bundle.Create(new Component1(), new Component2 { Value = 1 }, new Padding1()));
                break;
            case 1:
                world.CreateInArrayHost(Bundle.Create(new Component1(), new Component2 { Value = 1 }, new Padding2()));
                break;
            case 2:
                world.CreateInArrayHost(Bundle.Create(new Component1(), new Component2 { Value = 1 }, new Padding3()));
                break;
            case 3:
                world.CreateInArrayHost(Bundle.Create(new Component1(), new Component2 { Value = 1 }, new Padding4()));
                break;
        }
    }
    var schduler = new Scheduler();
    SystemChain.Empty
        .Add<MultiThreadUpdateSystem>()
        .RegisterTo(world, schduler);
    schduler.Tick();
}

Version

v1.8.9

sicusa commented 7 months ago

Cannot reproduce the exception in my computer. Could you paste the full call stack here?

Sieluna commented 7 months ago

Try this one: https://github.com/sicusa/Ecs.CSharp.Benchmark/blob/master/source/Ecs.CSharp.Benchmark/SystemWithTwoComponentsMultipleComposition/Sia.cs It will throw error when execute benchmark.

Procedural:

Run console app in cmd:

Ecs.CSharp.Benchmark.exe -f *SystemWithTwoComponentsMultipleComposition.Sia_MultiThread*

Output:

// **************************
// Benchmark: SystemWithTwoComponentsMultipleComposition.Sia_MultiThread: DefaultJob [EntityCount=100000]
// *** Execute ***
// Launch: 1 / 1
// Execute: dotnet 74dc4ab9-1698-4d8c-842d-1818e5189f7a.dll --anonymousPipes 1456 1396 --benchmarkName "Ecs.CSharp.Benchmark.SystemWithTwoComponentsMultipleComposition.Sia_MultiThread(EntityCount: 100000)" --job Default --benchmarkId 0 in {Home}\Ecs.CSharp.Benchmark\source\Ecs.CSharp.Benchmark\bin\Debug\net8.0\74dc4ab9-1698-4d8c-842d-1818e5189f7a\bin\Release\net8.0
// BeforeAnythingElse
// Benchmark Process Environment Information:
// BenchmarkDotNet v0.13.12
// Runtime=.NET 8.0.3 (8.0.324.11423), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI
// GC=Concurrent Workstation
// HardwareIntrinsics=AVX-512F+CD+BW+DQ+VL+VBMI,AES,BMI1,BMI2,FMA,LZCNT,PCLMUL,POPCNT VectorSize=256
// Job: DefaultJob
Failed loading Assembly: System.Security.Permissions
Failed loading Assembly: System.Threading.AccessControl
Assemblies loaded: 311, duration: 283 ms, engine-dependants: [Friflo.Engine.ECS.dll (7),  Ecs.CSharp.Benchmark.dll (7)]
OverheadJitting  1: 1 op, 176800.00 ns, 176.8000 us/op
System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
 ---> System.IndexOutOfRangeException: Index was outside the bounds of the array.
   at Sia.RunnerBarrier.Wait()
   at Sia.RunnerBarrier.WaitAndReturn()
   at Ecs.CSharp.Benchmark.SystemWithTwoComponentsMultipleComposition.SiaContext.MultiThreadUpdateSystem.Execute(World world, Scheduler scheduler, IEntityQuery query) in {Home}\Ecs.CSharp.Benchmark\source\Ecs.CSharp.Benchmark\SystemWithTwoComponentsMultipleComposition\Sia.cs:line 38
   at Sia.SystemLibrary.<>c__DisplayClass14_1`1.<Register>b__7()
   at Sia.Scheduler.Tick()
   at Ecs.CSharp.Benchmark.SystemWithTwoComponentsMultipleComposition.Sia_MultiThread() in {Home}\Ecs.CSharp.Benchmark\source\Ecs.CSharp.Benchmark\SystemWithTwoComponentsMultipleComposition\Sia.cs:line 89 
   at BenchmarkDotNet.Autogenerated.Runnable_0.WorkloadActionNoUnroll(Int64 invokeCount) in {Home}\Ecs.CSharp.Benchmark\source\Ecs.CSharp.Benchmark\bin\Debug\net8.0\74dc4ab9-1698-4d8c-842d-1818e5189f7a\74dc4ab9-1698-4d8c-842d-1818e5189f7a.notcs:line 311
   at BenchmarkDotNet.Engines.Engine.RunIteration(IterationData data)
   at BenchmarkDotNet.Engines.EngineFactory.Jit(Engine engine, Int32 jitIndex, Int32 invokeCount, Int32 unrollFactor)
   at BenchmarkDotNet.Engines.EngineFactory.CreateReadyToRun(EngineParameters engineParameters)
   at BenchmarkDotNet.Autogenerated.Runnable_0.Run(IHost host, String benchmarkName) in {Home}\Ecs.CSharp.Benchmark\source\Ecs.CSharp.Benchmark\bin\Debug\net8.0\74dc4ab9-1698-4d8c-842d-1818e5189f7a\74dc4ab9-1698-4d8c-842d-1818e5189f7a.notcs:line 176
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
   at System.Reflection.MethodBaseInvoker.InvokeDirectByRefWithFewArgs(Object obj, Span`1 copyOfArgs, BindingFlags invokeAttr)
   --- End of inner exception stack trace ---
   at System.Reflection.MethodBaseInvoker.InvokeDirectByRefWithFewArgs(Object obj, Span`1 copyOfArgs, BindingFlags invokeAttr)
   at System.Reflection.MethodBaseInvoker.InvokeWithFewArgs(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters)
   at BenchmarkDotNet.Autogenerated.UniqueProgramName.AfterAssemblyLoadingAttached(String[] args) in {Home}\Ecs.CSharp.Benchmark\source\Ecs.CSharp.Benchmark\bin\Debug\net8.0\74dc4ab9-1698-4d8c-842d-1818e5189f7a\74dc4ab9-1698-4d8c-842d-1818e5189f7a.notcs:line 57
Sieluna commented 7 months ago

You can add this file under Sia.Examples

using System.Diagnostics;
using Sia;

namespace Sia_Examples;

public partial record struct Component1(int Value);

public partial record struct Component2(int Value);

public partial record struct Padding1();

public partial record struct Padding2();

public partial record struct Padding3();

public partial record struct Padding4();

public class Fail
{
    [AfterSystem<MultiThreadUpdateSystem>]
    public class DebugLogSystem()
        : SystemBase(matcher: Matchers.Of<Component1, Component2>())
    {
        public override void Execute(World world, Scheduler scheduler, IEntityQuery query)
        {
            foreach (var entity in query) {
                Console.WriteLine("Result: " + entity.Get<Component1>().Value);
            }
        }
    }

    public sealed class MultiThreadUpdateSystem()
        : SystemBase(matcher: Matchers.Of<Component1, Component2>())
    {
        public override void Execute(World world, Scheduler scheduler, IEntityQuery query)
        {
            query.ForSliceOnParallel((ref Component1 c1, ref Component2 c2) => {
                c1.Value += c2.Value;
            });
        }
    }

    public static void Run(World world)
    {
        int entityCount = 100;
        for (int i = 0; i < entityCount; ++i) {
            switch (i % 4)
            {
                case 0:
                    world.CreateInArrayHost(Bundle.Create(new Component1(), new Component2 { Value = 1 }, new Padding1()));
                    break;

                case 1:
                    world.CreateInArrayHost(Bundle.Create(new Component1(), new Component2 { Value = 1 }, new Padding2()));
                    break;

                case 2:
                    world.CreateInArrayHost(Bundle.Create(new Component1(), new Component2 { Value = 1 }, new Padding3()));
                    break;

                case 3:
                    world.CreateInArrayHost(Bundle.Create(new Component1(), new Component2 { Value = 1 }, new Padding4()));
                    break;
            }
        }

        var schduler = new Scheduler();

        SystemChain.Empty
            .Add<MultiThreadUpdateSystem>()
            .Add<DebugLogSystem>()
            .RegisterTo(world, schduler);

        schduler.Tick();
    }
}

Add append this delegate under Program.cs

Invoke(Fail.Run);

Same index out of range will be throw.

sicusa commented 7 months ago

fixed ~