Мой недавний набег на мир ИИ с такими проектами, как этот, раскрыл аспект Dart, с которым у меня не было проблем до сих пор, а именно загрузку больших файлов, особенно файлов размером более 0x7ffff000 (2 147 479 552 байта). Комментарий в файле file_impl.dart гласит: -

On Linux, the `read` will
// transfer at most 0x7ffff000 bytes and return the number of bytes actually.
// transfered.

Это ограничение распространяется и на другие системные вызовы, такие как write и sendfile.

Все большие файлы языковых моделей, с которыми я сталкивался, больше этого, конкретный файл, используемый в проекте Alpaca, имеет размер 3,9 ГБ, размер квантованной модели Falcon 40B, которую я сейчас рассматриваю, имеет размер 24,4 ГБ. В общем, чем больше параметров в модели, тем она больше. Эти файлы должны быть загружены и проанализированы вашим приложением, прежде чем можно будет выполнять какие-либо логические выводы ИИ.

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

В этот момент я думал об использовании, возможно, потокового считывателя для этого, пример которого показан в документации Dart File API, когда я внезапно подумал: Зачем я это делаю? Я знаю по прошлому опыту, что загрузка больших файлов в системах Unix и Linux лучше всего использовать mmap для загрузки файла в виртуальное адресное пространство процессов, а не для чтения этого в том, что в Dart фактически было бы большим UInt8List.

Система файлового ввода-вывода Dart не поддерживает mmap’ирование, поэтому я искал другое решение, к счастью, этот проект в настоящее время предназначен только для Linux, и я быстро нашел pub-пакет stdlibc, предназначенный для Linux и macOS. Существует Windows-эквивалент mmap, использующий файлы с отображением памяти.

Итак, код, который я реализовал, просто стал: -

# Load model parts
 for (int i = 0; i < nParts!; ++i) {
      final partId = i;
      var fnamePart = fname;
      if (i > 0) {
        fnamePart += '.$i';
      }

      print(
          'llamaModelLoad:: loading model part ${i + 1}/$nParts from "$fnamePart"');
      int fileLength = stdlib.stat(fnamePart)!.st_size;
      if (fileLength <= 0) {
        print(
            'llamaModelLoad:: failed to stat model part ${i + 1}/$nParts from "$fnamePart"');
        return false;
      }
      final partFd = stdlib.open(fnamePart);
      if (partFd < 0) {
        print(
            'llamaModelLoad:: failed to open model part ${i + 1}/$nParts from "$fnamePart"');
        return false;
      }
      final pBufMapped = stdlib.mmap(
          length: fileLength,
          fd: partFd,
          prot: stdlib.PROT_READ,
          flags: stdlib.MAP_PRIVATE);
      if (pBufMapped!.data.lengthInBytes <= 0) {
        print(
            'llamaModelLoad:: failed to mmap model part ${i + 1}/$nParts from "$fnamePart"');
      }
      final bData = ByteData.view(pBufMapped.data);
      
  ......

Который просто регистрирует файл, чтобы получить длину, открывает его и mmap. Вы должны закрыть и munmap его, когда закончите, конечно. Это отлично сработало и было очень эффективным.

Теперь я думаю, что Dart действительно нуждается в отображении файлов, встроенном в среду выполнения, а не в том, чтобы полагаться на пакеты pub для этого. В SDK есть открытая проблема с просьбой об этом. Впервые это было поднято в августе 2022 года, чтобы добавить методы readMapped и readMappedSync в интерфейс RandomAccessFile. Полная история по этому вопросу, я просто добавил комментарий в конце, предполагая, что распространение больших файлов языковых моделей еще больше оправдывает это изменение. Будем надеяться, что это произойдет.

Забавно, не правда ли, когда вы сталкиваетесь с проблемой и решаете ее, вы часто получаете лучшее решение, чем то, о котором вы подумали сначала, напоминает мне одну старую пословицу из мира шахмат:

«Если вы видите хороший ход, ищите лучший».