Здравствуйте,
В каких случая лучше использовать `in`?
Напр. есть класс:
public static class CipherUtility
{
public static string Encrypt<T>(in string value, in string password, in string salt)
{
..
return str;
}
public static string Decrypt<T>(this string text, string password, string salt)
{
..
return str;
}
}
Если использовать `in` — экономится память при передачи строк в методы и трата CPU на лишнее копирование,
особенно если входящий параметр — очень большая строка.
Но с другой стороны, без `in` вызов метода `Decrypt` в длинной цепочке вызовов будет выглядеть более читаемым/эстетичным:
"some_str".SomeOtherMethod().Decrypt<object>("pass","salt").SomeOtherMethod2()...
И нет опасности, что в каком-нибудь другом потоке value/password/salt поменяются пока будет выполнятся метод `Encrypt`
Підтримати Україну у боротьбі з країною-терористом.
Нет, в обоих случаях передаётся ссылка на данные строки, потому что строка — ссылочный тип. Только в первом случае ссылка передаётся по ссылке, во втором — ссылка передаётся по значению.
Здравствуйте, #John, Вы писали: J>И нет опасности, что в каком-нибудь другом потоке value/password/salt поменяются пока будет выполнятся метод `Encrypt`
Уровень опасности совершенно одинаковый. Без жосткого использования unsafe или рефлексии поменять значение строки в дотнете нельзя.
Так что если вы получаете в метод Encrypt строковый параметр, то значение этой строки можно ожидать неизменным. Даже если вы его сохраните в поле объекта, и будете обращаться к нему раз в полгода — всё равно, строка останется той же самой.
А вот если злоумышленно написанный код лезет внутрь строкового буфера и меняет его, то метод encrypt сломается независимо от наличия декоратора in в аргументе.
Потому что единственный способ от этого защититься — принудительно сделать глубокую копию строки (например, через StringBuilder), которая невидима для внешнего кода.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
что это дает: в первом случае передается три указателя в метод, а во втором — только один.
особенно критично для hot-path кода. value-tuple's замечательная весчь (как оно сделано) — модифицировать сущ. код легко, не надо описывать бесчисленные структуры параметров, названия им придумывать. счастье.
Здравствуйте, Qbit86, Вы писали:
Q>Здравствуйте, Igorxz, Вы писали:
I>>что это дает: в первом случае передается три указателя в метод, а во втором — только один.
Q>Но value tuple — это мутабельная структура, так ведь? Передавать такие с модификатором in не рекомендуется.
это все общие слова. типа как лозунги — "лучше быть здоровым и богатым, чем наоборот" — кто бы с этим спорил, токо это ни о чем.
I>>особенно критично для hot-path кода.
Q>В hot-path я бы такое поостерёгся. У меня микробенчмарк показал падение производительности при передаче кортежа с модификатором in.
не может этого быть, ибо, известно что напр. в x64 архитектуре параметры в метод передаются так: два первых через регистры, остальные через стек.
для члена-функции первый всегда скрытый экземпляр.
если 5-10 параметров против 1-2. не может оно падать в производительности.
Здравствуйте, Igorxz, Вы писали:
I>не может этого быть, ибо, известно что напр. в x64 архитектуре параметры в метод передаются так: два первых через регистры, остальные через стек.
Не 2, а 4 — rcx, rdx, r8, r9. И еще 4 аргумента с плавающей запятой передаются через xmm0-xmm3. Microsoft x64 calling convention
Здравствуйте, Qbit86, Вы писали:
Q>Не слова, а реальность, данная нам в ощущениях. И измерениях.
ощущения бывают разные, в разных местах. путать их с какими-то а-ля концепциями-понятиями не стоит. ты о чем рассказываешь: об ощущениях своих или об осмысленной позиции? разберись.
пришлось мерить самому... вот что намерилось у меня. думаю тенденция понятна, а её причина была объяснена выше.
Здравствуйте, Igorxz, Вы писали:
I>пришлось мерить самому... вот что намерилось у меня.
Так а почему намерялось на десяти строках? Выше ты предлагал топикстартеру передавать кортежи из трёх строк, «особенно критично для hot-path кода».
I>что это дает: в первом случае передается три указателя в метод, а во втором — только один.
Но добавляется лишний уровень косвенности в адресации.
Здравствуйте, Igorxz, Вы писали:
Q>>В hot-path я бы такое поостерёгся. У меня микробенчмарк показал падение производительности при передаче кортежа с модификатором in. I>не может этого быть, ибо, известно что напр. в x64 архитектуре параметры в метод передаются так: два первых через регистры, остальные через стек. I>для члена-функции первый всегда скрытый экземпляр. I>если 5-10 параметров против 1-2. не может оно падать в производительности.
Когда мы передаем в функцию переменные через Tuple, то .net сначала запихивает эти переменные из регистров/другой памяти в объект tuple (если еще не создан, то создает такой объект), а потом передает ссылку на этот объект(qword ptr [rbp+48h]) через регистр в функцию (данном примере через `rcx`).
Когда мы передаем просто переменные в функцию, то .net передает эти переменные как через регистры, так и через стек.
Но что странно, что в начале функции .net сначала выгружает данные из этих регистров в стек, потом делает какую-то проверку(какую??) и уже потом проводит операцию сложения. Для tuples нет такого оверхеда по выгрузке данных из регистров в стек, но есть оверхед перед вызовом функции: создание объекта.
class Program
{
#region//00007FFF6E6B1AA0 push rbp
//00007FFF6E6B1AA1 push rdi
//00007FFF6E6B1AA2 push rsi
//00007FFF6E6B1AA3 sub rsp,30h
//00007FFF6E6B1AA7 mov rbp,rsp
//00007FFF6E6B1AAA xor eax,eax
//00007FFF6E6B1AAC mov qword ptr[rbp + 28h], rax
//00007FFF6E6B1AB0 mov dword ptr[rbp + 50h], ecx
//00007FFF6E6B1AB3 cmp dword ptr[7FFF6E5949C8h],0
//00007FFF6E6B1ABA je 00007FFF6E6B1AC1
//00007FFF6E6B1ABC call 00007FFFCE2E39D0
//00007FFF6E6B1AC1 mov eax,dword ptr[rbp + 50h]
//00007FFF6E6B1AC4 mov dword ptr[rbp + 24h], eax
//00007FFF6E6B1AC7 mov eax,dword ptr[rbp + 50h]
//00007FFF6E6B1ACA inc eax
//00007FFF6E6B1ACC mov dword ptr[rbp + 50h], eax
//00007FFF6E6B1ACF mov eax,dword ptr[rbp + 24h]
//00007FFF6E6B1AD2 lea rsp,[rbp+30h]
//00007FFF6E6B1AD6 pop rsi
//00007FFF6E6B1AD7 pop rdi
//00007FFF6E6B1AD8 pop rbp
//00007FFF6E6B1AD9 ret#endregion
static int ecx(int x) =>x++;
#region//00007FFF6E6B1B60 push rbp
//00007FFF6E6B1B61 push rdi
//00007FFF6E6B1B62 push rsi
//00007FFF6E6B1B63 sub rsp,30h
//00007FFF6E6B1B67 mov rbp,rsp
//00007FFF6E6B1B6A xor eax,eax
//00007FFF6E6B1B6C mov qword ptr[rbp + 28h], rax
//00007FFF6E6B1B70 mov dword ptr[rbp + 50h], ecx
//00007FFF6E6B1B73 mov dword ptr[rbp + 58h], edx
//00007FFF6E6B1B76 cmp dword ptr[7FFF6E5949C8h],0
//00007FFF6E6B1B7D je 00007FFF6E6B1B84
//00007FFF6E6B1B7F call 00007FFFCE2E39D0
//00007FFF6E6B1B84 mov eax,dword ptr[rbp + 50h]
//00007FFF6E6B1B87 add eax,dword ptr[rbp + 58h]
//00007FFF6E6B1B8A lea rsp,[rbp+30h]
//00007FFF6E6B1B8E pop rsi
//00007FFF6E6B1B8F pop rdi
//00007FFF6E6B1B90 pop rbp
//00007FFF6E6B1B91 ret#endregion
static int ecx_edx(int x, int y) => x + y;
static int ecx_edx_rd8(int x, int y, int z) => x + y + z;
static int ecx_edx_rd8_rd9(int x, int y, int z, int a) => x + y + z + a;
static int ecx_edx_rd8_rd9_stack(int x, int y, int z, int a, int b) => x + y + z + a + b;
#region//00007FFF6E691D80 push rbp
//00007FFF6E691D81 push rdi
//00007FFF6E691D82 push rsi
//00007FFF6E691D83 sub rsp,30h
//00007FFF6E691D87 mov rbp,rsp
//00007FFF6E691D8A xor eax,eax
//00007FFF6E691D8C mov qword ptr[rbp + 28h], rax
//00007FFF6E691D90 mov qword ptr[rbp + 50h], rcx
//00007FFF6E691D94 cmp dword ptr[7FFF6E5749C8h],0
//00007FFF6E691D9B je 00007FFF6E691DA2
//00007FFF6E691D9D call 00007FFFCE2E39D0
//00007FFF6E691DA2 mov rax,qword ptr[rbp + 50h]
//00007FFF6E691DA6 mov eax,dword ptr[rax]
//00007FFF6E691DA8 mov rdx,qword ptr[rbp + 50h]
//00007FFF6E691DAC add eax,dword ptr[rdx + 4]
//00007FFF6E691DAF lea rsp,[rbp+30h]
//00007FFF6E691DB3 pop rsi
//00007FFF6E691DB4 pop rdi
//00007FFF6E691DB5 pop rbp
//00007FFF6E691DB6 ret#endregion
static int tuple_over_heap_rcx(in (int x, int y)t) => t.x + t.y;
static void Main()
{
// 00007FFF6E6A14C6 mov ecx,5 var val = ecx(5);
// 00007FFF6E6914D9 mov ecx,2
// 00007FFF6E6914DE mov edx,4
val = ecx_edx(2,4);
// 00007FFF6E6B14F1 mov ecx,1
// 00007FFF6E6B14F6 mov edx,2
// 00007FFF6E6B14FB mov r8d,3
val = ecx_edx_rd8(1, 2, 3);
// 00007FFF6E6A1922 mov ecx,9
// 00007FFF6E6A1927 mov edx,8
// 00007FFF6E6A192C mov r8d,7
// 00007FFF6E6A1932 mov r9d,6
ecx_edx_rd8_rd9(9,8,7,6);
// 00007FFF6E681944 mov dword ptr [rsp+20h],5
// 00007FFF6E68194C mov ecx,1
// 00007FFF6E681951 mov edx,2
// 00007FFF6E681956 mov r8d,3
// 00007FFF6E68195C mov r9d,4
ecx_edx_rd8_rd9_stack(1, 2, 3, 4, 5);
// 00007FFF6E6B1974 xor ecx,ecx
// 00007FFF6E691976 mov qword ptr [rbp+48h],rcx
// 00007FFF6E69197A lea rcx,[rbp+48h]
// 00007FFF6E69197E mov edx,2
// 00007FFF6E691983 mov r8d,3
// 00007FFF6E691989 call 00007FFF6E6914A0
// 00007FFF6E69198E mov rcx,qword ptr[rbp + 48h]
// 00007FFF6E691992 mov qword ptr[rbp + 78h], rcx
// 00007FFF6E691996 lea rcx,[rbp+78h]
tuple_over_heap_rcx((2,3));
}
}
Підтримати Україну у боротьбі з країною-терористом.
Здравствуйте, #John, Вы писали:
J>Когда мы передаем просто переменные в функцию, то .net передает эти переменные как через регистры, так и через стек. J>Но что странно, что в начале функции .net сначала выгружает данные из этих регистров в стек, потом делает какую-то проверку(какую??) и уже потом проводит операцию сложения.
Ты уверен, что не в Debug смотришь? Не похоже на релизный выхлоп.
К тому же для простых структур (до 4 полей) есть соответствующая оптимизация (lowering) и в твоем примере
tuple_over_heap_rcx((2,3));
метод будет заинлайнен, вычислен, а потом вообще будет выкинут Пруф
Для
public static class Program
{
public static void Main()
{
tuple_over_heap_rcx((2, 3));
}
public static int tuple_over_heap_rcx(in (int x, int y) t) => t.x + t.y;
public static int ecx_edx(int x, int y) => x + y;
public static int ecx_edx_rd8(int x, int y, int z) => x + y + z;
public static int ecx_edx_rd8_rd9(int x, int y, int z, int a) => x + y + z + a;
public static int ecx_edx_rd8_rd9_stack(int x, int y, int z, int a, int b) => x + y + z + a + b;
}
Здравствуйте, Qbit86, Вы писали:
Q>Так а почему намерялось на десяти строках? Выше ты предлагал топикстартеру передавать кортежи из трёх строк, «особенно критично для hot-path кода».
потому что это подтверждает то, о чем я говорил. а говорил я следующее: передача 1-2 параметра не может быть медленнее, чем передача 5-10 параметров.
если это вдруг не так, уж не знаю по какой причине (компилятор/оптимизатор кривой, ещё чего), то что-то не так в _твоем_ датском королевстве.
Здравствуйте, rameel, Вы писали:
R>Ты в тесте на сконструированный тупл ссылку передаешь, что читерство, мог бы сразу константу подставлять
в тесте я подтверждаю именно то, что утверждал: передача 1-2 параметра быстрее чем 5-10.
сконструированние туплов зависит от контекста/ситуации — где-то нужно, где-то нет.
(насчет "читерство" — советую с мало знакомыми людьми быть вежливым и проявлять сдержанность)
Здравствуйте, Igorxz, Вы писали:
Q>>Так а почему намерялось на десяти строках? Выше ты предлагал топикстартеру передавать кортежи из трёх строк, «особенно критично для hot-path кода». I>потому что это подтверждает то, о чем я говорил. а говорил я следующее...
А говорил ты следующее: «что это дает: в первом случае передается три указателя в метод, а во втором — только один. особенно критично для hot-path кода»
I>передача 1-2 параметра не может быть медленнее, чем передача 5-10 параметров. I>если это вдруг не так, уж не знаю по какой причине
Чтоб передать этот один параметр — кортеж — тебе его надо ещё сконструировать. На руках-то у тебя отдельные value, password и salt. Поэтому помимо измерения времени вызова нужно ещё учитывать время подготовки перед этим вызовом.
I>то что-то не так в _твоем_ датском королевстве.
«Реальность — это то, что продолжает существовать, даже когда ты перестаёшь в неё верить.»
Здравствуйте, Qbit86, Вы писали:
Q>Чтоб передать этот один параметр — кортеж — тебе его надо ещё сконструировать. На руках-то у тебя отдельные value, password и salt. Поэтому помимо измерения времени вызова нужно ещё учитывать время подготовки перед этим вызовом. Q>«Реальность — это то, что продолжает существовать, даже когда ты перестаёшь в неё верить.»
ты либо молодой/зеленый, либо прекрасно понял то, о чем я говорю, но прикидываешься.
в первом случае ты понять ничего не сможешь, во втором не захочешь.
в обоих случаях дискутировать бессмысленно...)))