Я начинаю изучать C#, исходя из фона C ++. Я хотел узнать о модели памяти C# и сравнить ее с моделью C ++. При этом я нашел статью Модель памяти C# в теории и на практике . Пока что ничего удивительного в этом нет, но я попытался воспроизвести оптимизацию компилятора в статье, которая удаляет дополнительное чтение памяти и столкнулась с проблемой. Я использую моно-компилятор csharp и не могу воспроизвести оптимизацию. Вот код C#:
// test.cs
class MainApp {
static void Main() {
Foo foo = new Foo();
foo.bar();
}
}
class Foo {
private int _A = 0, _B = 1;
public bool bar() {
if (_B == -1) throw new Exception();
int a = _A;
int b = _B;
return a > b;
}
}
Затем я запускаю следующие команды компиляции:
mcs -optimize+ test.cs
mono --aot -O=all test.exe
Когда я проверяю вывод objdump -d test.exe.so
, я вижу следующее (соответствующее ) строки сборки:
0000000000000500 <Foo_bar>:
500: 48 83 ec 08 sub $0x8,%rsp
504: 48 89 3c 24 mov %rdi,(%rsp)
508: 48 8b c7 mov %rdi,%rax
50b: 48 63 40 14 movslq 0x14(%rax),%rax
50f: 83 f8 ff cmp $0xffffffff,%eax
512: 74 1b je 52f <Foo_bar+0x2f>
514: 48 8b 0c 24 mov (%rsp),%rcx
518: 48 63 41 10 movslq 0x10(%rcx),%rax
51c: 48 63 49 14 movslq 0x14(%rcx),%rcx
520: 3b c1 cmp %ecx,%eax
522: 40 0f 9f c0 setg %al
526: 48 0f b6 c0 movzbq %al,%rax
52a: 48 83 c4 08 add $0x8,%rsp
52e: c3 retq
... # exception stuff
Итак, инструкции 50b
, 518
и 51c
, кажется, указывают, что чтение все еще повторяется, даже если может быть оптимизирован. У меня вопрос: я делаю что-то не так, это упущенная возможность оптимизации, или здесь есть какая-то другая проблема (какая-то веская причина, почему эта оптимизация не состоялась)? Сейчас у меня нет доступа к Visual Studio, мне было бы интересно узнать, действительно ли он выполняет эту оптимизацию.
В статье утверждается, что я должен получить что-то вроде:
push eax
mov edx,dword ptr [ecx+8]
cmp edx,0FFFFFFFFh
je 00000016
mov eax,dword ptr [ecx+4]
cmp eax,edx
Я решил проверить, отличалась ли ситуация для C ++, и был немного удивлен тем, что нашел. Следующий код:
class Foo {
int _A{0};
int _B{1};
public:
__attribute__ ((noinline)) bool bar() volatile {
if (_B == -1) throw 0;
int a = _A;
int b = _B;
return a > b;
}
};
int main(int argc, char **argv) {
volatile Foo foo;
foo.bar();
}
Приводит к следующей (соответствующей) сборке:
00000000000007e4 <_ZNV3Foo3barEv>:
7e4: 8b 47 04 mov 0x4(%rdi),%eax
7e7: 83 f8 ff cmp $0xffffffff,%eax
7ea: 74 0b je 7f7 <_ZNV3Foo3barEv+0x13>
7ec: 8b 17 mov (%rdi),%edx
7ee: 8b 47 04 mov 0x4(%rdi),%eax
7f1: 39 c2 cmp %eax,%edx
7f3: 0f 9f c0 setg %al
7f6: c3 retq
... # exception stuff
Таким образом, оптимизация здесь также не происходит (хотя, если честно, без ((noinline))
этот код даже не отображается в объектном файле.) Вот то же самое из clang
:
0000000000400610 <_ZNV3Foo3barEv>:
400610: 50 push %rax
400611: 8b 47 04 mov 0x4(%rdi),%eax
400614: 83 f8 ff cmp $0xffffffff,%eax
400617: 74 0a je 400623 <_ZNV3Foo3barEv+0x13>
400619: 8b 07 mov (%rdi),%eax
40061b: 3b 47 04 cmp 0x4(%rdi),%eax
40061e: 0f 9f c0 setg %al
400621: 59 pop %rcx
400622: c3 retq
... # exception stuff
Так что еще есть дополнительное чтение, просто "встроенное" в cmp
инструкция.
релевантно --version
Mono C# compiler version 4.6.2.0
g++ (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0
clang version 6.0.0-1ubuntu2 (tags/RELEASE_600/final)