-
I'm writing Rust code in a company and we use diesel to build our project.
The increment of the compiled product size may be about 1kb. I don't know the exact number, but it does bring larger binary size than common Rust code. In production environment, we must concern about that problem. I guess that diesel write too much May I ask some solutions about this? |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 1 reply
-
Thanks for opening this thread. I can understand your concerns that the use of generics can cause the size of compiled code to explode. That's generally true, at least if generics are used in a certain way. For diesel that's not that simple. I did a simple experiment here to demonstrate this. For this I wrote a simple small script to generate rust programs using diesel. It generates a file containing x times the function you provided above, where x can be passed via command line. Every function is called from main, so that we can be sure the compiler does not strip it out. Script to generate rust programs using diesel#!/usr/bin/env python3
import sys
out = sys.argv[1]
count = int(sys.argv[2])
main = open(out, "w")
main.write('''#[macro_use]
extern crate diesel;
use diesel::prelude::*;
table! {
users(id) {
id -> Integer,
names -> Text,
}
}
''');
for i in range(0, count):
main.write("fn update_" + str(i));
main.write('''(connection: &PgConnection) {
use self::users::dsl::*;
diesel::update(users.filter(id.eq(1)))
.set(names.eq("ccc"))
.get_result::<(i32, String)>(connection);
}
''');
main.write('''
fn main() {
let conn = PgConnection::establish("").unwrap();
''');
for i in range(0, count):
main.write(" update_" + str(i) + "(&conn);\n");
main.write("}");
main.close(); Now given your report the hypothesis is that if you have that function 1000 times there we should see a binary size increase of ~ 1kb * 1000 = 1Mb, which should be clearly measurable. Now I used the following configuration to build two example programs (one with 1000 function calls, one with a single function call):
This results in the following binary sizes:
That means we observe here a binary size increase of 0,1MB (or ~150 KB) instead of the predicted 1 MB, which is smaller by a factor of 10.
According to this numbers the code size generated by the test crate using diesel increased by ~120kb, while all other sizes remained exactly the same. Now this means using a lot of diesel queries does not result in a increased size of diesel itself. Now one could guess that that size increase is caused by code generated by diesel inside our test crate. So let's check what's a "normal" size increase for the same number of functions there by just using something other (Opening a file in this case, as this should have similar complexity but no generic code) Script to generate the non-diesel test code#!/usr/bin/env python3
import sys
out = sys.argv[1]
count = int(sys.argv[2])
main = open(out, "w")
main.write('''
use std::fs::File;
use std::io::prelude::*;
''');
for i in range(0, count):
main.write("#[inline(never)] fn update_" + str(i));
main.write('''() {
let mut f = File::open("/tmp/foo").unwrap();
}
''');
main.write('''
fn main() {
''');
for i in range(0, count):
main.write(" update_" + str(i) + "();\n");
main.write("}");
main.close(); Results via
For this example the binary size is increased by ~150kb instead of ~120kb. At least I would interpret that as "Increasing the number of functions doing some work by the factor of 1000 will increase the binary size by 100 - 200kb" or something like that. I hope that this demonstrates that this is not caused by generic code bloat like you guessed. Now let's try to explain why diesel does not increase the binary size via generic code bloat for each query: Most of the types in diesel are so called zero sized structs, which either do not contain any fields at all, or where each field is a zero sized struct by itself. That means Hopefully this explains the issue quite well. If there are any other question about this feel free to leave a message here, if you are able to produce code bloat via diesels generic methods I would be interested in a complete example showing this. |
Beta Was this translation helpful? Give feedback.
Thanks for opening this thread. I can understand your concerns that the use of generics can cause the size of compiled code to explode. That's generally true, at least if generics are used in a certain way. For diesel that's not that simple. I did a simple experiment here to demonstrate this. For this I wrote a simple small script to generate rust programs using diesel. It generates a file containing x times the function you provided above, where x can be passed via command line. Every function is called from main, so that we can be sure the compiler does not strip it out.
Script to generate rust programs using diesel