Бұл мақаланың ғылыми журналдағы нұсқасы мына жерде қолжетімді: Dapper vs EF Core: A Practical Performance Comparison in .NET
Бенчмарк релизі және бастапқы материалдары мына жерде қолжетімді: efcore-vs-dapper v1.0.0
.NET-қосымшада деректерге қол жеткізу технологиясын таңдау — тек жеке қалаудың мәселесі емес. Ол кідіріске, жадтың бөліну көлеміне, қоқыс жинағышқа түсетін жүктемеге, кодты сүйемелдеудің ыңғайлылығына, ал кейде жүктеме кезіндегі инфрақұрылым құнына да әсер етеді.
Көптеген корпоративтік .NET жүйелерінде таңдау екі танымал тәсілге келіп тіреледі:
- Entity Framework Core — LINQ-сұрауларды, өзгерістерді қадағалауды, көшірулерді және деректерді сақтаудың жоғары деңгейлі моделін ұсынатын толыққанды ORM; ол әзірлеуді жылдамдатады.
- Dapper — SQL-ге жақын жұмыс істеуге мүмкіндік беретін және нәтиже жолдарын аз қосымша абстракциямен объектілерге жылдам сәйкестендіретін жеңіл микро-ORM.
Әдетте мұны былай түйіндейді: «EF Core ыңғайлырақ, Dapper жылдамырақ».
Бірақ айырмашылық қаншалықты үлкен? Ол қай сценарийлерде шынымен маңызды?
Осы сұраққа жауап беру үшін біз EF Core мен Dapper-ді PostgreSQL, .NET және BenchmarkDotNet көмегімен бірнеше шынайы сценарийде салыстырдық: оқу, JOIN, жаңарту және параллель сұраулар.
Тест ортасы
Бенчмарк PostgreSQL 17.6 нұсқасында, .NET 10 және BenchmarkDotNet көмегімен орындалды. Деректер базасы бақыланатын ортада қайта құрылды, ал тест деректері кездейсоқ сандар генераторының бекітілген бастапқы мәнімен бірдей түрде жасалды.
Мақсат жеке синтетикалық микрооперацияны өлшеу емес, деректерге қол жеткізудің толық жолын шынайы жағдайда бағалау болды:
- сұрауды құрастыру;
- деректер базасына жүгіну және жауап алу;
- нәтиже жолдарын өңдеу;
- деректерді объектілерге сәйкестендіру;
- жад бөлу.
Тест сызбасы products, orders және order_items кестелері бар шағын интернет-дүкенді сипаттайды.

Сурет 1. products, orders және order_items кестелері бар деректер базасының сызбасы.
Жүктеме бастапқы кілт бойынша бір жолды оқуды, сүзгімен оқуды, өлшемі әртүрлі кестелерден беттеп таңдау жасауды, JOIN саны көп тапсырыс іріктеуін, қарапайым және күрделі жаңартуларды, сондай-ақ бір мезгілде орындалатын саны көп параллель оқуларды қамтыды.
Оқу сценарийлерінде EF Core өзгерістерді қадағаламайтын сұраулармен бапталды, осылайша артық шығындар алынып тасталды. EF Core-дың құрастырылған сұраулары да жеке нұсқа ретінде тексерілді.
Осы зерттеу аясында бастапқы кодты қарау кезінде біз Dapper-дің асинхронды сұрауларды орындау жолында nullable-мәнін қажетсіз түрлендіруді тауып, ол туралы хабарладық. Бұл бақылау іске асыру деңгейіне қатысты және бенчмарк нәтижесін бағалауға енгізілмеген.
Dapper GitHub Issue #2184: Unused value in QueryAsync
Неліктен жад бөлінуі маңызды
Кідіріс маңызды, бірақ .NET сияқты басқарылатын орындау орталарында ол жалғыз маңызды көрсеткіш емес.
Егер деректерге қол жеткізу қабаты әрбір сұрауға көбірек жад бөлсе, оны кейін қоқыс жинағыш босатуы керек. Тұрақты жоғары жүктемеде бұл қоқыс жинау жиілігін арттырып, ең қолайсыз жағдайдағы кідірісті нашарлатуы мүмкін.
Басқаша айтқанда, екі тәсілдің орташа орындау уақыты ұқсас көрінсе де, жадты азырақ бөлетін нұсқа жұмыс ортасында болжамдырақ болуы мүмкін.
Сондықтан бенчмарк операцияға кететін уақытты микросекундпен және басқарылатын ортада бөлінген жадты операцияға шаққандағы КБ не МБ мөлшерімен өлшеді.
Қарапайым оқу: Dapper-дің шағын, бірақ айқын артықшылығы
Бір тауарды кілт бойынша іздеу сценарийінде Dapper EF Core-дың негізгі нұсқасымен салыстырғанда аз уақыт жұмсады және жадты азырақ бөлді.
EF Core-дың құрастырылған сұраулары оқу жолын жылдамдатты, бірақ жад бөлу айырмашылығын толық жоймады.

Сурет 2. Dapper, EF Core және EF Core-дың құрастырылған сұраулары үшін SelectOneProduct орындау уақыты мен жад бөлінуі.
Бұл маңызды нәтиже, өйткені қарапайым оқу операциялары серверлік қызметтерде жиі кездеседі. Дегенмен мұндағы айырмашылық тым үлкен емес. Көптеген іскерлік қосымшаларда осы нәтиженің өзі ғана EF Core-ды Dapper-ге ауыстыруға жеткілікті себеп бола қоймайды.
Сұрау күрделенген сайын қызығырақ нәтижелер шығады.
Жаппай оқу: деректер көлемі артқан сайын айырма азаяды
Көп тауар іріктелгенде, Dapper мен EF Core арасындағы салыстырмалы айырма жалпы орындау уақыты өскен сайын кішірейді.
Dapper бенчмаркте әлі де жылдамырақ болды, бірақ күрделірек сценарийлермен салыстырғанда артықшылығы онша айқын емес еді.

Сурет 3. Таңдалған жазбалар саны артқан кездегі SelectNProducts орындау уақытының өсуі.
Бұл қарапайым жаппай іріктеуде операция құнының негізгі бөлігін деректер базасының жұмысы мен деректерді беру құрауы мүмкін екенін көрсетеді. ORM-нің қосымша шығындары сақталады, бірақ жалпы уақыттағы үлесі азаяды.
JOIN саны көп сұраулар: Dapper алға шығады
Ең үлкен айырмашылық JOIN саны көп сұрауларда байқалды.
Бенчмарк тапсырыстарды олардың тауар позицияларымен бірге іріктеді. Соның салдарынан жадта құрылуы тиіс объектілер графының көлемі артты.
Дәл осы жерде EF Core көбірек жұмыс атқарады: LINQ-ті түрлендіреді, мәндерді объектілерге айналдырады, байланыстарды өңдейді және ішкі күйді басқарады. Өзгерістерді қадағалау өшірілген күннің өзінде, тікелей SQL нәтижесін объектілерге сәйкестендірумен салыстырғанда мұнда абстракция деңгейі жоғары.
Ал Dapper нақты жазылған SQL-ді орындайды және нәтиже жолдарын қарапайым объектілерге азырақ ішкі инфрақұрылым арқылы сәйкестендіреді.

Сурет 4. ItemsPerOrder мәндері әртүрлі болғанда Dapper, EF Core және EF Core-дың құрастырылған сұраулары үшін SelectNOrders орындау уақыты.
JOIN саны көп сипаттамалы сценарийде Dapper сұрауды шамамен 51,2 % жылдамырақ орындады.
Бұл EF Core нашар деген сөз емес. Ірі объектілер графы мен JOIN саны көп оқу — абстракциялардың қосымша шығындары анық байқалатын жүктеме түрі.
Сұрау күрделілігі және жылдамдату
Деректер көлемі мен сұрау күрделілігінің байланысын айқынырақ көрсету үшін біз жылдамдатудың жылу картасын құрдық.
Жылдамдату коэффициенті мынадай түрде есептелді:
EF Core уақыты / Dapper уақыты
Демек, 2,0 мәні осы сценарийде EF Core Dapper-ден шамамен екі есе ұзақ орындалғанын білдіреді.

Сурет 5. Деректер көлемі және ItemsPerOrder мәндері әртүрлі болғандағы жылдамдату коэффициенті.
Жылу картасы заңдылықты анық көрсетеді: сұрау күрделенген сайын Dapper-дің артықшылығы көбіне өседі, әсіресе көбірек жолды біріктіріп, объектілерге айналдыру қажет болғанда.
Параллель оқу: жад бөлінуі маңыздырақ бола түседі
Бенчмарк бір мезгілде орындалатын сұраулар саны жоғары және параллелизм деңгейі 128 болатын оқу сценарийін де қамтыды.
Бұл жағдайда EF Core мен Dapper-дің орындау уақыты дерлік бірдей болды. Dapper-дің уақыт бойынша артықшылығы небәрі шамамен 0,5 % еді.
Алайда жад бөлу басқа көрініс берді.
Dapper ресурстар үшін сұраулар бәсекелескен кезде жадты азырақ бөлді. Бұл бір уақытта көптеген сұрауды өңдейтін нақты қызметтер үшін маңызды болуы мүмкін.

Сурет 6. DOP = 128 кезіндегі параллель іріктеуде жадтың бөлінуі.
Бұл негізгі қорытындылардың бірі: өнімділік тек орташа кідіріспен шектелмейді. Жадтың қарқынды бөлінуі қоқыс жинауға, жауап уақытының шашырауына және жүйенің жұмыс ортасындағы тұрақтылығына әсер етуі мүмкін.
Нәтижелердің қорытындысы
Бенчмаркте орындау уақыты мен жадтың бөлінуін бірге ескеретін біріктірілген өнімділік индексі қолданылды. Ол тек жылдамырақ емес, жадты үнемдірек пайдаланатын тәсілдерге де басымдық берді.
| Сценарий | Dapper-дің уақыт бойынша артықшылығы | Dapper индексі | Құрастырылған сұраулары бар EF Core индексі |
|---|---|---|---|
| Қарапайым SelectOne | 8,4 % | 1,5 | 1,3 |
| Сүзгісі бар SelectOne | 16,4 % | 1,66 | 1,43 |
| SelectN, 1000 жазба | 6,7 % | 1,16 | 1,03 |
| JOIN бар SelectN | 51,2 % | 2,28 | 0,97 |
| Қарапайым UpdateOne | 31,6 % | 1,97 | 1,28 |
| Күрделі UpdateOne | 48,3 % | 2,5 | 1,3 |
| Параллель сұрау, DOP = 128 | 0,5 % | 1,12 | 1,0 |
Dapper-дің ең үлкен артықшылықтары JOIN саны көп іріктеуде, күрделі жаңартуларда және жадтың бөлінуіне сезімтал параллель жүктемелерде байқалды.
Бұл архитектура үшін нені білдіреді
Қорытынды «әрқашан Dapper қолданыңыз» деген емес.
Дұрысы мынадай:
Жүктеме сипатына сай деректерге қол жеткізу тәсілін таңдаңыз.
Әзірлеу жылдамдығы, кодты сүйемелдеудің ыңғайлылығы, көшірулер, бай пәндік модель және өзгерістерді қадағалау әрбір қосымша жад бөлінуін азайтудан маңыздырақ болса, EF Core — жақсы таңдау.
Қосымшада кідіріске сезімтал учаскелер, ірі оқу модельдері, JOIN саны көп сұраулар, жадқа жоғары қысым немесе пішінін қатаң бақылау қажет SQL болса, Dapper — жақсы таңдау.
Іс жүзінде көптеген жүйеде екі тәсілді де қолдануға болады.
Мысалы:
- Кодты сүйемелдеу маңыздырақ болатын әкімшілік CRUD-операциялар, ішкі құралдар және іскерлік үдерістер үшін EF Core пайдалану.
- Сұрау пішінін толық бақылау қажет, жылдамдыққа сындарлы оқу жолдары, есеп беру сұраулары, жоғары жүктемелі соңғы нүктелер және күрделі SQL үшін Dapper пайдалану.
Мұндай аралас тәсіл бүкіл жүйеге бір технологияны күштеп енгізуден жиі шынайырақ.
EF Core-дың құрастырылған сұраулары көмектеседі, бірақ ішінара ғана
EF Core-дың құрастырылған сұраулары кейбір оқу сценарийлерінде қосымша шығындарды азайтты, бірақ жад бөлу профилін түбегейлі өзгертпеді.
Себебі сұрауды құрастыру EF Core шығындарының тек бір бөлігі ғана.
Басқа шығындар сақталады:
- объектілерді құру;
- байланыстарды өңдеу;
- өзгерістерді қадағалауды баптау;
- күйді басқару;
- абстракциялардың қосымша шығындары.
Құрастырылған сұраулар пайдалы, бірақ EF Core-ды Dapper-ге айналдыратын сиқырлы ауыстырып-қосқыш емес.
Dapper жылдамырақ, бірақ тегін емес
Dapper көбірек бақылау береді, алайда бұл бақылау қосымша жауапкершілікпен бірге келеді.
Dapper қолданғанда әзірлеушілер SQL-дің дұрыстығын, параметрлеуді, деректерді объектілермен дәл сәйкестендіруді, сызба өзгерістерін, сұрау логикасының қайталануын, транзакция шекараларын және SQL бен қосымша модельдерінің сәйкестігін өздері басқаруы керек.
EF Core осы міндеттердің көбін фреймворк арқылы атқарады. Dapper қолданылғанда, олар команда жауапкершілігінде қалады.
Сондықтан архитектуралық ымыра айқын:
Dapper орындау кезіндегі қосымша шығындарды азайта алады, ал EF Core әзірлеуге кететін еңбекті азайта алады.
Дұрыс таңдау жүйеңіз үшін осы шығындардың қайсысы маңызды екеніне байланысты.
Қорытынды
Біздің бенчмаркте Dapper басқарылатын ортада бөлінетін жад көлемін тұрақты түрде азайтып, орташа кідірісті жиі төмендетті. Ең үлкен пайда күрделі объектілер графын алу және жаңарту сценарийлерінде байқалды.
EF Core-дың құрастырылған сұраулары кейбір оқу жолдарын жақсартты, бірақ сыналған конфигурацияда жад бөлінуін айтарлықтай азайтпады.
Өнімділікке сезімтал .NET қызметтерінде, әсіресе жоғары жүктемеде, деректерге қол жеткізу технологиясын әдетке не жеке қалауға емес, нақты жүктеме сипаттамаларына сүйеніп таңдау керек.
Егер басты шектеу әзірлеу жылдамдығы болса, EF Core әдетте әдепкі таңдау ретінде орынды.
Егер басты шектеулер жадқа түсетін қысым, кідірістің тұрақтылығы немесе күрделі SQL-дің өнімділігі болса, Dapper-ге байыппен қараған жөн.
Ең тәжірибелік архитектура көбіне EF Core мен Dapper-ді бір-біріне қарсы қоюдан тұрмайды.
Ол абстракция пайдалы жерде EF Core-ды, ал бақылау маңызды жерде Dapper-ді қолданады.