{"id":43843,"date":"2024-06-24T15:53:48","date_gmt":"2024-06-24T19:53:48","guid":{"rendered":"https:\/\/mjtsai.com\/blog\/?p=43843"},"modified":"2024-07-01T14:37:19","modified_gmt":"2024-07-01T18:37:19","slug":"swiftdata-vs-realm-performance-comparison","status":"publish","type":"post","link":"https:\/\/mjtsai.com\/blog\/2024\/06\/24\/swiftdata-vs-realm-performance-comparison\/","title":{"rendered":"SwiftData vs. Realm Performance Comparison"},"content":{"rendered":"<p><a href=\"https:\/\/www.emergetools.com\/blog\/posts\/swiftdata-vs-realm-performance-comparison\">Jacob Bartlett<\/a>:<\/p>\n<blockquote cite=\"https:\/\/www.emergetools.com\/blog\/posts\/swiftdata-vs-realm-performance-comparison\"><p>The Realm DB engine was written from the ground-up in C++ to minimise this overhead. [&#8230;] Therefore, it&rsquo;s not unreasonable to describe SwiftData as a wrapper over a wrapper over a wrapper.<\/p><p>[&#8230;]<\/p><p>These show that the SwiftData objects took around <em>10x longer<\/em> to instantiate.<\/p><p>[&#8230;]<\/p><p>Realm topped out at writing 2,000,000 simple <code>User<\/code> objects before hitting an out-of-memory exception and crashing. SwiftData was only able to manage a paltry 1,000,000 objects. Realm was about <strong>3-6x faster<\/strong> beyond write volumes exceededing 1,000 objects.<\/p><p>[&#8230;]<\/p><p>For our dead-simple <code>User<\/code> objects (a few fields and no relationships), we queried for all users with the first name &ldquo;Jane&rdquo;. Realm was much faster, its zero-copy architecture shining when reading data directly into memory. For simple SwiftData objects, read performance started off okay and degraded sharply with over 100k objects in the database.<\/p><p>With our more complex <code>Student<\/code> model, we searched for all Physics students who got the top grade. We observed the opposite effect: SwiftData was usually more than <strong>10x faster<\/strong> than Realm.<\/p><\/blockquote>\n<p>Interestingly, SwiftData sometimes has the edge with smaller datasets, both in terms of RAM use and speed.<\/p>\n\n<p>Previously:<\/p>\n<ul>\n<li><a href=\"https:\/\/mjtsai.com\/blog\/2023\/06\/12\/swiftdata\/\">SwiftData<\/a><\/li>\n<li><a href=\"https:\/\/mjtsai.com\/blog\/2022\/08\/26\/lighter-swift\/\">Lighter.swift<\/a><\/li>\n<li><a href=\"https:\/\/mjtsai.com\/blog\/2016\/07\/28\/grdb-swift-sqlite-orm\/\">GRDB Swift SQLite ORM<\/a><\/li>\n<li><a href=\"https:\/\/mjtsai.com\/blog\/2015\/01\/14\/switching-from-core-data-to-realm\/\">Switching From Core Data to Realm<\/a><\/li>\n<li><a href=\"https:\/\/mjtsai.com\/blog\/2014\/07\/16\/introducing-realm\/\">Introducing Realm<\/a><\/li>\n<\/ul>\n\n<p id=\"swiftdata-vs-realm-performance-comparison-update-2024-06-26\">Update (2024-06-26): <a href=\"https:\/\/wadetregaskis.com\/when-all-you-have-is-a-core-data-everything-looks-like\/\">Wade Tregaskis<\/a>:<\/p>\n<blockquote cite=\"https:\/\/wadetregaskis.com\/when-all-you-have-is-a-core-data-everything-looks-like\/\">\n<p>I think we (Shark engineers) tried to be open-minded and kind. We were sceptical, but you never know until you actually look. We could see some potential for a more general query capability, for example. But of course the first and most obvious hurdle was: how well does Core Data handle sizeable numbers of records? Oh yes, was the response, it&rsquo;s great even with tens of thousands of records.<\/p>\n<p>[&#8230;]<\/p>\n<p>We asked how it did with tens of <em>millions<\/em> of records, and that was pretty much the end of the conversation.<\/p>\n<p>[&#8230;]<\/p>\n<p>I guess by modern standards SQLite is considered efficient and fast, but &#x2013; hah &#x2013; <em>back in my day<\/em> SQLite was what you used when you didn&rsquo;t have time to write your own, <em>efficient and fast<\/em> persistent data management system.<\/p>\n<\/blockquote>\n\n<p><a href=\"https:\/\/mastodon.social\/@helge\/112674993446754639\">Helge He&szlig;<\/a>:<\/p>\n<blockquote cite=\"https:\/\/mastodon.social\/@helge\/112674993446754639\">\n<p>The Realm vs SwiftData thing encouraged me to try the same project w\/ Lighter. It isn&rsquo;t exactly the same as Lighter is no ORM, but it will give some hints on what a little lower level can yield. For now, the 10k plain items test:<\/p>\n<p>SwiftData:<br \/>\n&#x1F4BD; User instantiation: 0.0676<br \/>\n&#x1F4BD; Create users: 1.9151<\/p>\n<p>Realm:<br \/>\n&#x1F4BD; User instantiation: 0.0229<br \/>\n&#x1F4BD; Create users: 0.1220<\/p>\n<p>Lighter:<br \/>\n&#x1F4BD; User instantiation: 0.0049<br \/>\n&#x1F4BD; Create users: 0.0820<\/p>\n<p>[&#8230;]<\/p>\n<p>Something disappointing in SwiftData is that it doesn&rsquo;t make use of the static nature of the macro(s). The macro can&rsquo;t see the full schema like Lighter does, but it could still statically generate a ton, e.g. a static snapshot struct for the backing data. Or predefined indices for quickly binding the snapshot to the SQLite API (or really any).<\/p>\n<p>Instead we get custom backends.<\/p>\n<\/blockquote>\n\n<p><a href=\"https:\/\/mastodon.social\/@helge\/112677838826656397\">Helge He&szlig;<\/a>:<\/p>\n<blockquote cite=\"https:\/\/mastodon.social\/@helge\/112677838826656397\"><p>So I&rsquo;ve essentially ported the whole perf test over to Lighter, which was interesting because it also demonstrates some key differences in the approaches. E.g. to update the math grades of the bad students, the sample essentially loads all students and their grades into memory, then updates the grades one-by-one and saves them back to the store.<\/p><p>In plain SQL that would be just a single line <code>UPDATE<\/code> statement, no loading at all.<\/p><p>The SwiftData implementation in the test also seems to be not quite optimal. E.g. to update the items, already fetched items get inserted into a new <code>ModelContext<\/code> and then saved (a lot of new <code>MC<\/code>s are created, which seems completely counter the idea, though sometimes necessary to keep SwiftData RAM at bounds). Presumably just saving the context used to fetch the items would be quite a bit more efficient.<\/p><\/blockquote>\n\n<p id=\"swiftdata-vs-realm-performance-comparison-update-2024-07-01\">Update (2024-07-01): <a href=\"https:\/\/aplus.rs\/2024\/core-data-performance-test\/\">Aleksandar Vaci&#x107;<\/a> (<a href=\"https:\/\/mastodon.social\/@aleck\/112700616153893037\">Mastodon<\/a>):<\/p>\n<blockquote cite=\"https:\/\/aplus.rs\/2024\/core-data-performance-test\/\">\n<p>Thus by using somewhat reasonable but still sizable chunks (100k records) of the original data set and employing Core Data best practices, we lowered peak memory usage 10&#xD7; and shortened total time spent about 2.5&#xD7; which is no small feat.<\/p>\n<\/blockquote>","protected":false},"excerpt":{"rendered":"<p>Jacob Bartlett: The Realm DB engine was written from the ground-up in C++ to minimise this overhead. [&#8230;] Therefore, it&rsquo;s not unreasonable to describe SwiftData as a wrapper over a wrapper over a wrapper.[&#8230;]These show that the SwiftData objects took around 10x longer to instantiate.[&#8230;]Realm topped out at writing 2,000,000 simple User objects before hitting [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"apple_news_api_created_at":"2024-06-24T19:53:51Z","apple_news_api_id":"b00c956c-01f5-42e1-89e8-8921bb48b1e5","apple_news_api_modified_at":"2024-07-01T18:37:23Z","apple_news_api_revision":"AAAAAAAAAAAAAAAAAAAAAw==","apple_news_api_share_url":"https:\/\/apple.news\/AsAyVbAH1QuGJ6Ikhu0ix5Q","apple_news_coverimage":0,"apple_news_coverimage_caption":"","apple_news_is_hidden":false,"apple_news_is_paid":false,"apple_news_is_preview":false,"apple_news_is_sponsored":false,"apple_news_maturity_rating":"","apple_news_metadata":"\"\"","apple_news_pullquote":"","apple_news_pullquote_position":"","apple_news_slug":"","apple_news_sections":"\"\"","apple_news_suppress_video_url":false,"apple_news_use_image_component":false,"footnotes":""},"categories":[4],"tags":[800,109,143,31,2321,2456,138,71,942,2617,425,2404],"class_list":["post-43843","post","type-post","status-publish","format-standard","hentry","category-programming-category","tag-concurrency","tag-coredata","tag-database","tag-ios","tag-ios-17","tag-iphone-15-pro","tag-optimization","tag-programming","tag-realm","tag-shark","tag-sqlite","tag-swiftdata"],"apple_news_notices":[],"_links":{"self":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts\/43843","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/comments?post=43843"}],"version-history":[{"count":5,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts\/43843\/revisions"}],"predecessor-version":[{"id":43915,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts\/43843\/revisions\/43915"}],"wp:attachment":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/media?parent=43843"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/categories?post=43843"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/tags?post=43843"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}