Размерные VS ссылочные типы

Итак, думаю есть смысл обсудить, что и где лучше применять, а точнее ссылочные или размерные типы. Мнения в этом вопросе расходятся: например, @ibond считает, что размерные типы негативно влияют на производительность, а вот авторы нескольких статей на Хабре(но уж точно не наш Илья :smile: ) совершенно противоположного мнения. Причём каждая позиция имеет под собой весьма серьёзные основания. Хотел бы выслушать мнения.

Глеб, лень искать все это у ibonda и на Хабре… Если уж вы подробно ознакомились с их позициями, то попробуйте здесь кратко изложить все их аргументы и в каком контексте они были сказаны. Ну, или хотя бы точные ссылки на сами посты и статьи. А то пока и обсуждать-то нечего :slight_smile:

Но предполагаю, что они все правы по-своему, только каждый – применительно к своей конкретной ситуации.

Хорошая идея.

Итак, преимущества ссылочных типов:

  1. Возможность выделить достаточно большое количество памяти и высвободить её в любой момент.

  2. Гибкость динамических объектов.

Недостатки:

  1. Объекты ссылочного типа располагаются в куче, а эта область памяти подчиняется сборщику мусора, службе, которая может периодически перемещать объект, на это тратятся ресурсы.

  2. Ссылочные объекты имеют достаточно сложную структуру, что опять таки негативно сказывается на производительности.

  3. Выделение памяти в куче происходит ощутимо дольше, чем в стеке.

Размерные типы:

Преимущества:

  1. Моментальное выделение памяти в стеке.

  2. Отсутствие затрат на перемещение объекта при сборке мусора.

Недостатки:

  1. Невысокая гибкость. Невозможность управляемого высвобождения памяти.

  2. Ограничения на размер.

Размерные типы размещаются в памяти ближе к процессору, поэтому в целом они быстрее. Но если, к примеру, передавать их в функцию - будет копироваться вся переменная, и если они весит много а копируется часто - это будет негативно влиять на производительность. Но, кстати, если передавать их как var-параметры - в функцию будет копироваться только указатель на них. Но доступ к var-параметрам немного медленнее, потому что надо разыменовывать указатель каждый раз.


Ну, это конечно можно к ним отнести, но не в том дело. В принципе, памяти в куче больше. В теории, можно наверное как то через WinAPI выделять память в куче, а ссылочность тут не при чём.

Вы про что?

Оно вроде только если места не хватает перемещает всё, да и ресурсов для этого много не надо.

Ну, не совсем. Единственное это то что каждая переменная ссылочного типа - это указатель, получение значения по этому указателю - наверное единственное что влияет на производительность.

Выделение, вроде, нет, только доступ к памяти, и только потому что куча располагается дальше от процессора чем стек.


Сомневаюсь, что вы сможете без рекурсии добится заполнения стека. А так его объём можно приравнивать к бесконечности.

Стоп. Это замедление ведь очень мало.

Что ещё хуже.

Да, но речь идёт о непрерывном объекте. Заполнить его кусками информации сложно, но вот одним - легко. Мне где-то попадались цифра примерно 75 мБ под объект.

Ну наверное жешь. Но вы ведь хотите полную картину, вот я для полноты написал.

Ну это массив же, или какой то объект с внутренними массивами? А в паскале нет массивов размещающихся в стеке.

Скажу тогда, зачем всё это вообще нужно мне. Я реализуются нейросеть на Паскале, да такую, которую обычно на серверных GPU запускают. Очевидно, что надо оптимизировать по максимуму. Смысл в том, что нейросеть содержит веса, на которые определённым образом умножаются данные. Например, четырёхмерном массив Single с размерами [3, 3, 512, 512] (остроконечный формат с лева на право). Так вот, эти веса инициализируются один раз в начале программы и никак не меняются. Может, есть смысл их засунуть в стек?

Да, наверное, но в паскале вы это никак (кроме как вручную, по одному элементу объявлять) не сделаете.

Надо делать на C++, и потом делать обёртку в паскале. Через external подключить функцию создающую массив длинной 33512*512 и возвращающую ссылку на первый элемент.

Хотя, это похоже займёт в памяти >9MB, а стек обычно в районе 1MB. По крайней мере на сколько я понял покопавшись в интернете. Как точно узнать объём - нашёл несколько очень низкоуровневых решений на C++ и следующие решение, которое работает только на вин 8 и 10:

procedure GetCurrentThreadStackLimits(lowLimit: ^cardinal; highLimit: ^cardinal);
external 'kernel32.dll';

begin
  var l,h:cardinal;
  GetCurrentThreadStackLimits(@l,@h);
  writeln((l,h));
end.

У меня вин7, поэтому проверить работоспособность не смог.

Вообще, ускорения надо добиваться настраивая горы GPU-шек, а махинациями с тем, где всё размещается, вы скорее всего не больше 1% разницы сделаете.

Ещё OpenCL можно использовать, если GPU нет подходящих.

Нет, ну зачем извращаться, System.Threading.Tasks.Parallel сделаете всё то что может OpenCL, разве нет?

1 лайк

Не знаю, просто видел примеры проектов с использованием OpenCL в том числе и для нейронных сетей. Поставил один такой у себя (не нейросеть), он показывает что использует обычный встроенный графический процессор в Intel.

А можно ли как-то управлять GPU из под .NET? Чтобы не плодить кучу файлов? Если обычный GPU будет работать, то я, возможно, задействуют его.

Только имейте ввиду, что сейчас процесс занимает более 2-х часов(не просто так я сказал про сервер). Посчитайте 1% от 3 часов. Но зато это первая реализация под Windows, причём написана она не на хвалёном C++ и Python, а на C# и PascalABC.NET :slight_smile:

Нет уж, лучше вручную на Паскале, чем на C++. Паскаль, по моему, удобнее.

Самому интересно. Тот проект с OpenCL о котором писал уже это Ready. С таким названием сложно искать, вот ссылка https://github.com/GollyGang/ready Но он на С++. Просто можете взять в release последнюю версию и посмотреть, есть ли смысл вообще связываться с OpenCL на Вашем железе. А о применении к нейронным сетям на NET, OpenCL уже можете потом сами поискать, там вроде даже какие-то статьи на Хабр выскакивали.

Вроде у OpenCL есть обёртка под .Net, что то типа OpenTK

Вы наверное меня не поняли, вам придётся объявить 2 записи с 512 элементами. И это в лучшем случае. Потому что в паскале не выйдет сделать массив который суётся в стек.

Но всё же, как я и сказал, вряд ли всё это поместится в стек. И разница в скорости доступа будет небольшая всё равно.

Меньше 2 мин, если от 3 часов брать. Но, кроме всего, надо сначала провести тесты скорости, 1% это я отфига взял.

Вот к примеру:

type
  r1 = record
    value1: single;
    value2: single;
    value3: single;
    value4: single;
    value5: single;
    value6: single;
    value7: single;
    value8: single;
    value9: single;
    value10: single;
    value11: single;
    value12: single;
    value13: single;
    value14: single;
    value15: single;
    value16: single;
    value17: single;
    value18: single;
    value19: single;
    value20: single;
    value21: single;
    value22: single;
    value23: single;
    value24: single;
    value25: single;
    value26: single;
    value27: single;
    value28: single;
    value29: single;
    value30: single;
    value31: single;
    value32: single;
    value33: single;
    value34: single;
    value35: single;
    value36: single;
    value37: single;
    value38: single;
    value39: single;
    value40: single;
    value41: single;
    value42: single;
    value43: single;
    value44: single;
    value45: single;
    value46: single;
    value47: single;
    value48: single;
    value49: single;
    value50: single;
    value51: single;
    value52: single;
    value53: single;
    value54: single;
    value55: single;
    value56: single;
    value57: single;
    value58: single;
    value59: single;
    value60: single;
    value61: single;
    value62: single;
    value63: single;
    value64: single;
    value65: single;
    value66: single;
    value67: single;
    value68: single;
    value69: single;
    value70: single;
    value71: single;
    value72: single;
    value73: single;
    value74: single;
    value75: single;
    value76: single;
    value77: single;
    value78: single;
    value79: single;
    value80: single;
    value81: single;
    value82: single;
    value83: single;
    value84: single;
    value85: single;
    value86: single;
    value87: single;
    value88: single;
    value89: single;
    value90: single;
    value91: single;
    value92: single;
    value93: single;
    value94: single;
    value95: single;
    value96: single;
    value97: single;
    value98: single;
    value99: single;
    value100: single;
    value101: single;
    value102: single;
    value103: single;
    value104: single;
    value105: single;
    value106: single;
    value107: single;
    value108: single;
    value109: single;
    value110: single;
    value111: single;
    value112: single;
    value113: single;
    value114: single;
    value115: single;
    value116: single;
    value117: single;
    value118: single;
    value119: single;
    value120: single;
    value121: single;
    value122: single;
    value123: single;
    value124: single;
    value125: single;
    value126: single;
    value127: single;
    value128: single;
    value129: single;
    value130: single;
    value131: single;
    value132: single;
    value133: single;
    value134: single;
    value135: single;
    value136: single;
    value137: single;
    value138: single;
    value139: single;
    value140: single;
    value141: single;
    value142: single;
    value143: single;
    value144: single;
    value145: single;
    value146: single;
    value147: single;
    value148: single;
    value149: single;
    value150: single;
    value151: single;
    value152: single;
    value153: single;
    value154: single;
    value155: single;
    value156: single;
    value157: single;
    value158: single;
    value159: single;
    value160: single;
    value161: single;
    value162: single;
    value163: single;
    value164: single;
    value165: single;
    value166: single;
    value167: single;
    value168: single;
    value169: single;
    value170: single;
    value171: single;
    value172: single;
    value173: single;
    value174: single;
    value175: single;
    value176: single;
    value177: single;
    value178: single;
    value179: single;
    value180: single;
    value181: single;
    value182: single;
    value183: single;
    value184: single;
    value185: single;
    value186: single;
    value187: single;
    value188: single;
    value189: single;
    value190: single;
    value191: single;
    value192: single;
    value193: single;
    value194: single;
    value195: single;
    value196: single;
    value197: single;
    value198: single;
    value199: single;
    value200: single;
    value201: single;
    value202: single;
    value203: single;
    value204: single;
    value205: single;
    value206: single;
    value207: single;
    value208: single;
    value209: single;
    value210: single;
    value211: single;
    value212: single;
    value213: single;
    value214: single;
    value215: single;
    value216: single;
    value217: single;
    value218: single;
    value219: single;
    value220: single;
    value221: single;
    value222: single;
    value223: single;
    value224: single;
    value225: single;
    value226: single;
    value227: single;
    value228: single;
    value229: single;
    value230: single;
    value231: single;
    value232: single;
    value233: single;
    value234: single;
    value235: single;
    value236: single;
    value237: single;
    value238: single;
    value239: single;
    value240: single;
    value241: single;
    value242: single;
    value243: single;
    value244: single;
    value245: single;
    value246: single;
    value247: single;
    value248: single;
    value249: single;
    value250: single;
    value251: single;
    value252: single;
    value253: single;
    value254: single;
    value255: single;
    value256: single;
    value257: single;
    value258: single;
    value259: single;
    value260: single;
    value261: single;
    value262: single;
    value263: single;
    value264: single;
    value265: single;
    value266: single;
    value267: single;
    value268: single;
    value269: single;
    value270: single;
    value271: single;
    value272: single;
    value273: single;
    value274: single;
    value275: single;
    value276: single;
    value277: single;
    value278: single;
    value279: single;
    value280: single;
    value281: single;
    value282: single;
    value283: single;
    value284: single;
    value285: single;
    value286: single;
    value287: single;
    value288: single;
    value289: single;
    value290: single;
    value291: single;
    value292: single;
    value293: single;
    value294: single;
    value295: single;
    value296: single;
    value297: single;
    value298: single;
    value299: single;
    value300: single;
    value301: single;
    value302: single;
    value303: single;
    value304: single;
    value305: single;
    value306: single;
    value307: single;
    value308: single;
    value309: single;
    value310: single;
    value311: single;
    value312: single;
    value313: single;
    value314: single;
    value315: single;
    value316: single;
    value317: single;
    value318: single;
    value319: single;
    value320: single;
    value321: single;
    value322: single;
    value323: single;
    value324: single;
    value325: single;
    value326: single;
    value327: single;
    value328: single;
    value329: single;
    value330: single;
    value331: single;
    value332: single;
    value333: single;
    value334: single;
    value335: single;
    value336: single;
    value337: single;
    value338: single;
    value339: single;
    value340: single;
    value341: single;
    value342: single;
    value343: single;
    value344: single;
    value345: single;
    value346: single;
    value347: single;
    value348: single;
    value349: single;
    value350: single;
    value351: single;
    value352: single;
    value353: single;
    value354: single;
    value355: single;
    value356: single;
    value357: single;
    value358: single;
    value359: single;
    value360: single;
    value361: single;
    value362: single;
    value363: single;
    value364: single;
    value365: single;
    value366: single;
    value367: single;
    value368: single;
    value369: single;
    value370: single;
    value371: single;
    value372: single;
    value373: single;
    value374: single;
    value375: single;
    value376: single;
    value377: single;
    value378: single;
    value379: single;
    value380: single;
    value381: single;
    value382: single;
    value383: single;
    value384: single;
    value385: single;
    value386: single;
    value387: single;
    value388: single;
    value389: single;
    value390: single;
    value391: single;
    value392: single;
    value393: single;
    value394: single;
    value395: single;
    value396: single;
    value397: single;
    value398: single;
    value399: single;
    value400: single;
    value401: single;
    value402: single;
    value403: single;
    value404: single;
    value405: single;
    value406: single;
    value407: single;
    value408: single;
    value409: single;
    value410: single;
    value411: single;
    value412: single;
    value413: single;
    value414: single;
    value415: single;
    value416: single;
    value417: single;
    value418: single;
    value419: single;
    value420: single;
    value421: single;
    value422: single;
    value423: single;
    value424: single;
    value425: single;
    value426: single;
    value427: single;
    value428: single;
    value429: single;
    value430: single;
    value431: single;
    value432: single;
    value433: single;
    value434: single;
    value435: single;
    value436: single;
    value437: single;
    value438: single;
    value439: single;
    value440: single;
    value441: single;
    value442: single;
    value443: single;
    value444: single;
    value445: single;
    value446: single;
    value447: single;
    value448: single;
    value449: single;
    value450: single;
    value451: single;
    value452: single;
    value453: single;
    value454: single;
    value455: single;
    value456: single;
    value457: single;
    value458: single;
    value459: single;
    value460: single;
    value461: single;
    value462: single;
    value463: single;
    value464: single;
    value465: single;
    value466: single;
    value467: single;
    value468: single;
    value469: single;
    value470: single;
    value471: single;
    value472: single;
    value473: single;
    value474: single;
    value475: single;
    value476: single;
    value477: single;
    value478: single;
    value479: single;
    value480: single;
    value481: single;
    value482: single;
    value483: single;
    value484: single;
    value485: single;
    value486: single;
    value487: single;
    value488: single;
    value489: single;
    value490: single;
    value491: single;
    value492: single;
    value493: single;
    value494: single;
    value495: single;
    value496: single;
    value497: single;
    value498: single;
    value499: single;
    value500: single;
    value501: single;
    value502: single;
    value503: single;
    value504: single;
    value505: single;
    value506: single;
    value507: single;
    value508: single;
    value509: single;
    value510: single;
    value511: single;
    value512: single;
  end;

begin
  var valueT: r1;
  var referenceT := new single[512];
  
  var lc := 1000000000;
  
  var sw := new System.Diagnostics.Stopwatch;
  sw.Start;
  
  loop lc do
  begin
    valueT.value113 := valueT.value384;
  end;
  
  sw.Stop;
  writeln(sw.Elapsed);
  sw.Restart;
  
  loop lc do
  begin
    referenceT[112] := referenceT[383];
  end;
  
  sw.Stop;
  writeln(sw.Elapsed);
  readln;
  
end.

У меня, в режиме без отладочной информации, выводит следующее:

image

То есть массив, хоть он и должен ещё проверять выход индекса за границы - он всё равно немного быстрее. Хотя это немного странно, value-тип должен быть быстрее, хоть на сколько то.

А, понятно. Я в IL коде посмотрел - там чтоб получить поле записи - создаёт ссылку на неё, а потом ещё ищет элемент с заданным именем в этой ссылке:

      IL_0039: ldloca.s     r1
      IL_003b: ldloca.s     r1
      IL_003d: ldfld        float32 Program1.r1::value384
      IL_0042: stfld        float32 Program1.r1::value113

А у массивов есть свои операторы:

      IL_0090: ldloc.1      // numArray
      IL_0091: ldc.i4.s     112 // 0x70
      IL_0093: ldloc.1      // numArray
      IL_0094: ldc.i4       383 // 0x0000017f
      IL_0099: ldelem.r4    
      IL_009a: stelem.r4    

Тут ldelem.r4 и stelem.r4 это операторы получения и перезаписи элемента по индексу.

И не лень ведь было :smile:

Значит, единственное, что можно сделать - работать с массивами по указателям, как в C++.