비동기가 과연 효율적일까?

2022. 3. 31. 17:32카테고리 없음

Stack Overflow에서 비동기에 관해 쉽게 설명해주는 글을 보아서 번역을 해보았다.

 

Async는 speed에 관한 것이 아니라 초당 요청에 관한 개념과 연관되어있다.

Aysnc는 오직 한 가지의 일만 수행한다.

만약 Task가 대기되어진다면, 그 Task는 CPU Bound 작업(CPU 사용시간 > I/O 대기 시간)이 아니라면

(즉 I/O Bound 작업이라면)

그 결과적으로 해당 Thread는 가동되지 않고 놀게 되고,(Thread becomes Idle) 다른 작업을 할 수 있도록 pool로 돌아간다.

 

바로 여기서 Async의 핵심은 리소스를 효율적으로 쓰는 것이다.

Thread가 묶여서 있을 수 있는 상황에서 일부 I/O 작업이 완료되기를 기다리는 대신 다른 작업을 수행할 수 있다.

 

1. Async != Faster

사실 Async는 더 느리다. 비동기 작업과 관련된 오버 헤드가 있다.

Context Switch, Heap에서 셔플되는 데이터 등 추가 처리 시간이 필요하기 때문이다.

우리가 마이크로초 단위에서 이야기해도, Async는 항상 Sync Process로 동일한 작업을 하는 것보다 느리다.

 

 

원문

Yes, you are missing the fact that async is not about speed, and is only slightly related to the concept of requests per second.

Async does one thing and only one thing. If a task is being awaited, and that task does not involve CPU-bound work, and as a result, the thread becomes idle, then, that thread potentially could be released to return to the pool to do other work.

That's it. Async in a nutshell. The point of async is to utilize resources more efficiently. In situations where you might have had threads tied up, just sitting there tapping their toes, waiting for some I/O operation to complete, they can instead be tasked with other work. This results in two very important ideas you should internalize:

  1. Async != faster. In fact, async is slower. There's overhead involved in an asynchronous operation: context switching, data being shuffled on and off the heap, etc. That adds up to additional processing time. Even if we're only talking microseconds in some cases, async will always be slower than an equivalent sync process. Period. Full stop.
  2. Async only buys you anything if your server is at load. It's only at times when your server is stressed that async will give it some much needed breathing room, whereas sync might bring it to its knees. It's all about scale. If your server is only fielding a minuscule amount of requests, you very likely will never see a difference over sync, and like I said, you may end up using more resources, ironically, because of the overhead involved.

That doesn't mean you shouldn't use async. Even if your app isn't popular today, it doesn't mean it won't be later, and rejiggering all your code at that point to support async will be a nightmare. The performance cost of async is usually negligible, and if you do end up needing it, it'll be a life-saver.

UPDATE

In the regard of keeping the performance cost of async negligible, there's a few helpful tips, that aren't obvious or really spelled out that well in most discussions of async in C#.

 

  • Use ConfigureAwait(false) as much as you possibly can.Pretty much every asynchronous method call should be followed by this except for a few specific exceptions. ConfigureAwait(false) tells the runtime that you don't need the synchronization context preserved during the async operation. By default when you await an async operation an object is created to preserve thread locals between thread switches. This takes up a large part of the processing time involved in handling an async operation, and in many cases is completely unnecessary. The only places it really matters is in things like action methods, UI threads, etc - places where there's information tied to the thread that needs to be preserved. You only need to preserve this context once, so as long as your action method, for example, awaits an async operation with the synchronization context intact, that operation itself can perform other async operations where the synchronization context is not preserved. Because of this, you should confine uses of await to a minimum in things like action methods, and instead try to group multiple async operations into a single async method that that action method can call. This will reduce the overhead involved in using async. It's worth noting that this is only a concern for actions in ASP.NET MVC. ASP.NET Core utilizes a dependency injection model instead of statics, so there are no thread locals to be concerned about. In others you can use ConfigureAwait(false) in an ASP.NET Core action, but not in ASP.NET MVC. In fact, if you try, you'll get a runtime error.
  • await DoSomethingAsync().ConfigureAwait(false);
  • As much as possible, you should reduce the amount of locals that need to be preserved. Variables that you initialize before calling await are added to the heap and the popped back off once the task has completed. The more you've declared, the more that goes onto the heap. In particular large object graphs can wreck havoc here, because that's a ton of information to move on and off the heap. Sometimes this is unavoidable, but it's something to be mindful of.
  • When possible, elide the async/await keywords. Consider the following for example:Here, DoSomethingElseAsync returns a Task that is awaited and unwrapped. Then, a new Task is created to return from DoSometingAsync. However, if instead, you wrote the method as:The Task returned by DoSomethingElseAsync is returned directly by DoSomethingAsync. This reduces a significant amount of overhead.
 
 

 

참고

https://stackoverflow.com/questions/48015096/not-much-difference-between-asp-net-core-sync-and-async-controller-actions

 

Not much difference between ASP.NET Core sync and async controller actions

I wrote a couple of action methods in a controller to test the difference between sync and async controller actions in ASP.NET core: [Route("api/syncvasync")] public class SyncVAsyncController :

stackoverflow.com