{"id":13677511,"url":"https://github.com/genaray/ZeroAllocJobScheduler","last_synced_at":"2025-04-29T11:31:03.190Z","repository":{"id":63061336,"uuid":"564978972","full_name":"genaray/ZeroAllocJobScheduler","owner":"genaray","description":"A high-performance alloc free c# Jobscheduler. ","archived":false,"fork":false,"pushed_at":"2024-07-08T21:11:07.000Z","size":169,"stargazers_count":163,"open_issues_count":3,"forks_count":14,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-10-11T11:09:25.202Z","etag":null,"topics":["csharp","ecs","engine","game","game-development","gamedev","godot","jobs","monogame","multithreading","net6","net7","netstandard21","unity","unity3d"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/genaray.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.MD","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-11-12T00:58:39.000Z","updated_at":"2024-10-08T00:56:17.000Z","dependencies_parsed_at":"2023-10-16T07:02:44.819Z","dependency_job_id":"ac378e31-95f1-4377-80b5-1dde937c3557","html_url":"https://github.com/genaray/ZeroAllocJobScheduler","commit_stats":{"total_commits":12,"total_committers":2,"mean_commits":6.0,"dds":"0.41666666666666663","last_synced_commit":"18734cf2758de07968b6abafc1ef61e16d7f7368"},"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/genaray%2FZeroAllocJobScheduler","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/genaray%2FZeroAllocJobScheduler/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/genaray%2FZeroAllocJobScheduler/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/genaray%2FZeroAllocJobScheduler/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/genaray","download_url":"https://codeload.github.com/genaray/ZeroAllocJobScheduler/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251493751,"owners_count":21598161,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["csharp","ecs","engine","game","game-development","gamedev","godot","jobs","monogame","multithreading","net6","net7","netstandard21","unity","unity3d"],"created_at":"2024-08-02T13:00:43.257Z","updated_at":"2025-04-29T11:31:02.926Z","avatar_url":"https://github.com/genaray.png","language":"C#","funding_links":[],"categories":["GamePlay","Open Source Repositories"],"sub_categories":["Skill","Job System"],"readme":"# ZeroAllocJobScheduler\n[![Maintenance](https://img.shields.io/badge/Maintained%3F-yes-green.svg?style=for-the-badge)](https://GitHub.com/Naereen/StrapDown.js/graphs/commit-activity)\n[![Nuget](https://img.shields.io/nuget/v/ZeroAllocJobScheduler?style=for-the-badge)](https://www.nuget.org/packages/ZeroAllocJobScheduler/)\n[![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg?style=for-the-badge)](https://opensource.org/licenses/Apache-2.0)\n![C#](https://img.shields.io/badge/c%23-%23239120.svg?style=for-the-badge\u0026logo=c-sharp\u0026logoColor=white)\n\nA high-performance alloc-free C# job scheduler.  \nSchedules and executes jobs on a set of worker threads with automatic pooling of internal handles.\n\n# Usage\n\n```csharp\n\npublic class HeavyCalculation : IJob\n{\n  public void Execute()\n  {\n    Thread.Sleep(50);  // Simulate heavy work\n    Console.WriteLine(\"Done\");\n  }\n}\n\n// Create a new Scheduler, which you should keep the lifetime of your program. This is the only API call that will allocate or generate garbage.\nvar scheduler = new JobScheduler(new JobScheduler.Config()\n{\n    // Names the process \"MyProgram0\", \"MyProgram1\", etc.\n    ThreadPrefixName = \"MyProgram\",\n\n    // Automatically chooses threads based on your processor count\n    ThreadCount = 0,\n\n    // The amount of jobs that can exist in the queue at once without the scheduler spontaneously allocating and generating garbage.\n    // Past this number, the scheduler is no longer Zero-Alloc!\n    // Higher numbers slightly decrease performance and increase memory consumption, so keep this on the lowest possible end for your application.\n    MaxExpectedConcurrentJobs = 64,\n\n    // Enables or disables strict allocation mode: if more jobs are scheduled at once than MaxExpectedConcurrentJobs, it throws an error.\n    // Not recommended for production code, but good for debugging allocation issues.\n    StrictAllocationMode = false,\n});\n\n// You need to pool/create jobs by yourself. This will, of course, allocate, so cache and reuse the jobs.\nvar firstJob = new HeavyCalculation();  \nvar firstHandle = scheduler.Schedule(firstJob); // Schedules job locally\n\nscheduler.Flush();                              // Dispatches all scheduled jobs to the worker threads\nfirstHandle.Complete();                         // Blocks the thread until the job is complete.\n\n// Call Dispose at program exit, which shuts down all worker threads\nscheduler.Dispose();                \n```\n\n# Dependencies\n\nTo set a sequential dependency on a job, simply pass a created `JobHandle` to `JobScheduler.Schedule(job, dependency)`.\n\n```csharp\nvar handle1 = scheduler.Schedule(job1);\nvar handle2 = scheduler.Schedule(job2, handle1);    // job2 will only begin execution once job1 is complete!\nscheduler.Flush();\n```\n\n# Multiple dependencies\n\nUse `Scheduler.CombineDependencies(JobHandle[] handles)` to get a new handle that depends on the handles in parallel. That handle can then be passed into future `Schedule` call as a dependency itself!\n\n```csharp\n// You must create the array of handles, and handle caching/storage yourself.\nJobHandle[] handles = new JobHandle[2];\n\nhandles[0] = Scheduler.Schedule(job1);\nhandles[1] = Scheduler.Schedule(job2);\nJobHandle combinedHandle = Scheduler.CombineDependencies(handles);          // Combines all handles into the array into one\n\nvar dependantHandle = Scheduler.Schedule(job3, combinedHandle);             // job3 now depends on job1 and job2.\n                                                                            // job1 and job2 can Complete() in parallel, but job3 can only run once both are complete.\n\ndependantHandle.Complete();                                                 // Blocks the main thread until all three tasks are complete.\n```\n\n\n# Bulk complete\n\nRather than using `CombineDependencies()`, if you just need to block the main thread until a list of handles are complete, you can use this syntax:\n\n```csharp\nJobHandle.CompleteAll(JobHandle[] handles);                     // Waits for all JobHandles to finish, and blocks the main thread until they each complete (in any order)\nJobHandle.CompleteAll(List\u003cJobHandle\u003e handles);\n```\n\nOr, if you don't want to maintain a list or array, you can just call `handle.Complete()` on all your handles, in any order.\n\n\n# Parallel-For support\n\nInstead of `IJob`, you may extend your class from `IJobParallelFor` to implement foreach-style indexing on a job. This is useful for when you have very many small operations, and it would be inefficient to schedule a whole job for each one; for example, iterating through a giant set of data.\n\nDefine an `IJobParallelFor` like so:\n\n```csharp\npublic class ManyCalculations : IJobParallelFor\n{\n  // Execute will be called for each i for the specified amount\n  public void Execute(int i)\n  {\n    // ... do some operation with i here\n  }\n\n  // Finish will be called once all operations are completed.\n  public void Finish()\n  {\n    Debug.Log(\"All done!\");\n  }\n\n  // BatchSize is a measure of how \"complicated\" your operations are. Detailed below.\n  public int BatchSize =\u003e 32;\n\n  // Restrict the number of spawned jobs to decrease memory usage and overhead. Keep this at 0 to use the Scheduler's number of active threads (recommended).\n  public int ThreadCount =\u003e 0;\n}\n\n```\n\nRun your `IJobParallelFor` with this syntax:\n\n\n```csharp\nvar job = new ManyCalculations();\nvar handle = scheduler.Schedule(job, 512); // Execute will be called 512 times\n\n\n```\n\nHowever, there are several caveats:\n\n* Don't overuse `IJobParallelFor`. In general, over-parallelization is a bad thing. Only schedule your job in parallel if it is truly iterating a huge amount of times, and make sure to always profile when dealing with multithreaded code.\n* You must choose a sane `BatchSize` for the work inside your job. If you have very many small tasks that complete very quickly, a higher batch size will dispatch more indices to each thread at once, minimizing scheduler overhead. On the other hand, if you have complicated (or mixed-complexity) tasks, a smaller batch size will maximize the ability for threads to use work-stealing and thus might complete faster. The only way to know what batch size to use is to profile your code and see what's faster!\n* Scheduling just a single `IJobParallelFor` actually schedules `ThreadCount` jobs on the backend, decreasing the jobs pool. If you make a lot of these, the amount of jobs in play could quickly increase. For example, on a 16-core CPU, with default settings, spawning 8 `IJobParallelFor` would spawn 128 jobs on the backend. The scheduler can certainly handle it, but you'll probably want to keep an eye on `MaxExpectedCurrentJobs` if you want to keep the scheduler truly zero-allocation.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgenaray%2FZeroAllocJobScheduler","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgenaray%2FZeroAllocJobScheduler","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgenaray%2FZeroAllocJobScheduler/lists"}