Kotlin, как работает suspend под капотом
Как компилятор преобразует suspend код, чтобы корутины можно было приостанавливать и возобновлять?
Корутины в Kotlin представлены ключевым словом suspend. Интересно, что там происходит внутри? Как компилятор преобразует suspend блоки в код, поддерживающий приостановку и возобновление работы корутины?
Знание этого поможет понимать, почему suspend функция не возвращает управление, пока не завершится вся запущенная работа и как код может приостановить выполнение без блокировки потоков.
TL;DR; Компилятор Kotlin создает специальную машину состояний для каждой suspend функции, эта машина берет управление корутиной на себя!
Новенький в Android? Взгляни на эти полезные ресурсы по корутинам:
- Using coroutines in your Android app.
- Advanced Coroutines with Kotlin Flow and Live Data.
Для тех, кто предпочитает видео:
Корутины, краткое введение
Говоря по-простому, корутины это асинхронные операции в Android. Как описано в документации, мы можем использовать корутины для управления асинхронными задачами, которые иначе могут блокировать основной поток и приводить к зависанию UI приложения.
Также корутины удобно использовать для замены callback-кода на императивный код. Например, посмотрите на этот код с использованием колбеков:
// Simplified code that only considers the happy path fun loginUser(userId: String, password: String, userResult: Callback) < // Async callbacks userRemoteDataSource.logUserIn < user ->// Successful network request userLocalDataSource.logUserIn(user) < userDb ->// Result saved in DB userResult.success(userDb) > > >
Заменяем эти колбеки на последовательные вызовы функций с использованием корутин:
suspend fun loginUser(userId: String, password: String): UserDb
Для функций, которые вызываются в корутинах, мы добавили ключевое слово suspend. Так компилятор знает, что эти функции для корутин. С точки зрения разработчика, рассматривайте suspend функцию как обычную, выполнение которой может быть приостановлено и возобновлено в определенный момент.
В отличие от колбеков, корутины предлагают простой способ переключения между потоками и обработки исключений.
Но что в действительности делает компилятор внутри, когда мы отмечаем функцию как suspend?
Suspend под капотом
Давайте вернемся к suspend функции loginUser , посмотрите, другие функции которые она вызывает являются также suspend функциями:
suspend fun loginUser(userId: String, password: String): UserDb < val user = userRemoteDataSource.logUserIn(userId, password) val userDb = userLocalDataSource.logUserIn(user) return userDb >// UserRemoteDataSource.kt suspend fun logUserIn(userId: String, password: String): User // UserLocalDataSource.kt suspend fun logUserIn(userId: String): UserDb
Кратко говоря, компилятор Kotlin берет suspend функции и преобразовывает их в оптимизированную версию колбеков с использованием конечной машины состояний (о которой мы поговорим позже).
Интерфейс Continuation
Suspend функции взаимодействуют друг с другом с помощью Continuation объектов. Continuation объект — это простой generic интерфейс с дополнительными данными. Позже мы увидим, что сгенерированная машина состояний для suspend функции будет реализовывать этот интерфейс.
Сам интерфейс выглядит так:
interface Continuation < public val context: CoroutineContext public fun resumeWith(value: Result) >
- context это экземпляр CoroutineContext , который будет использоваться при возобновлении.
- resumeWith возобновляет выполнение корутины с Result, он может либо содержать результат вычисления, либо исключение.
Компилятор заменяет ключевое слово suspend на дополнительный аргумент completion (тип Continuation ) в функции, аргуемнт используется для передачи результата suspend функции в вызывающую корутину:
fun loginUser(userId: String, password: String, completion: Continuation)
Для упрощения, наш пример возвращает Unit вместо объекта User .
Байткод suspend функций фактически возвращает Any? так как это объединение (union) типов T | COROUTINE_SUSPENDED . Что позволяет функции возвращать результат синхронно, когда это возможно.
Если suspend функция не вызывает другие suspend функции, компилятор добавляет аргумент Continuation, но не будет с ним ничего делать, байткод функции будет выглядеть как обычная функция.
Кроме того, интерфейс Continuation можно увидеть в:
- При конвертации колбек-API в корутины с использованием suspendCoroutine или suspendCancellableCoroutine (предпочтительнее использовать в большинстве случаев). Вы напрямую взаимодействуете с экземпляром Continuation , чтобы возобновить корутину, приостановленную после выполнения блока кода из аргументов suspend функции.
- Вы можете запустить корутину при помощи startCoroutine extension функции в suspend методе. Она принимает Continuation как аргумент, который будет вызван, когда новая корутина завершится либо с результатом, либо с исключением.
Используем Dispatchers
Вы можете переключаться между разными диспетчерами для запуска вычислений на разных потоках. Как Kotlin знает, где возобновить suspend вычисления?
Есть подтип Continuation , он называется DispatchedContinuation, где его метод resume делает вызов Dispatcher доступного в контексте корутины CoroutineContext . Все диспетчеры ( Dispatchers ) будут вызывать метод dispatch , кроме типа Dispatchers.Unconfined , он переопределяет метод isDispatchNeeded (он вызывается перед вызовом dispatch ), который возвращает false в этом случае.
Сгенрированная машина состояний
Уточнение: Приведенный код не полностью соответствует байткоду сгенерированному компилятором. Это будет код на Kotlin, достаточно точный, для понимания того, что в действительности происходит внутри. Это представление сгенерировано корутинами версии 1.3.3 и может поменяться в следующих версиях библиотеки.
Компилятор Kotlin определяет, когда функция может остановится внутри. Каждая точка останова представляется как отдельное состояние в конечной машине состояний. Такие состояния компилятор помечает метками:
fun loginUser(userId: String, password: String, completion: Continuation) < // Label 0 ->first execution val user = userRemoteDataSource.logUserIn(userId, password) // Label 1 -> resumes from userRemoteDataSource val userDb = userLocalDataSource.logUserIn(user) // Label 2 -> resumes from userLocalDataSource completion.resume(userDb) >
Компилятор использует when для состояний:
fun loginUser(userId: String, password: String, completion: Continuation) < when(label) < 0 -> < // Label 0 ->first execution userRemoteDataSource.logUserIn(userId, password) > 1 -> < // Label 1 ->resumes from userRemoteDataSource userLocalDataSource.logUserIn(user) > 2 -> < // Label 2 ->resumes from userLocalDataSource completion.resume(userDb) > else -> throw IllegalStateException(/* . */) > >
Этот код неполный, так как различные состояния не могут обмениваться информацией. Компилятор использует для обмена тот же самый объект Continuation . Вот почему родительски тип в Continuation это Any? вместо ожидаемого возвращаемого типа User .
При этом компилятор создает приватный класс, который:
- хранит нужные данные
- вызывает функцию loginUser рекурсивно для возобновления вычисления
Ниже представлен примерный вид такого сгенерированного класса:
Комментарии в коде были добавлены вручную для объяснения действий
fun loginUser(userId: String?, password: String?, completion: Continuation) < class LoginUserStateMachine( // completion parameter is the callback to the function // that called loginUser completion: Continuation): CoroutineImpl(completion) < // Local variables of the suspend function var user: User? = null var userDb: UserDb? = null // Common objects for all CoroutineImpls var result: Any? = null var label: Int = 0 // this function calls the loginUser again to trigger the // state machine (label will be already in the next state) and // result will be the result of the previous state's computation override fun invokeSuspend(result: Any?) < this.result = result loginUser(null, null, this) >> /* . */ >
Поскольку invokeSuspend вызывает loginUser только с аргументом Continuation , остальные аргументы в функции loginUser будут нулевыми. На этом этапе компилятору нужно только добавить информацию как переходить из одного состояния в другое.
Компилятору нужно знать:
- Функция вызывается первый раз или
- Функция была возобновлена из предыдущего состояния Для этого проверяется тип аргумента Continuation в функции:
fun loginUser(userId: String?, password: String?, completion: Continuation) < /* . */ val continuation = completion as? LoginUserStateMachine ?: LoginUserStateMachine(completion) /* . */ >
Если функция вызывается первый раз, то создается новый экземпляр LoginUserStateMachine и аргумент completion передается в этот экземпляр, чтобы возобновить вычисление. Иначе продолжится выполнение машины состояний.
Давайте взглянем на код, который генерирует компилятор для смены состояний и обмена информацией между ними:
fun loginUser(userId: String?, password: String?, completion: Continuation) < /* . */ val continuation = completion as? LoginUserStateMachine ?: LoginUserStateMachine(completion) when(continuation.label) < 0 -> < // Checks for failures throwOnFailure(continuation.result) // Next time this continuation is called, it should go to state 1 continuation.label = 1 // The continuation object is passed to logUserIn to resume // this state machine's execution when it finishes userRemoteDataSource.logUserIn(userId. password. continuation) >1 -> < // Checks for failures throwOnFailure(continuation.result) // Gets the result of the previous state continuation.user = continuation.result as User // Next time this continuation is called, it should go to state 2 continuation.label = 2 // The continuation object is passed to logUserIn to resume // this state machine's execution when it finishes userLocalDataSource.logUserIn(continuation.user, continuation) >/* . leaving out the last state on purpose */ > >
Обратите внимание на различия между этим и предыдущим примером кода:
- Появилась переменная label из LoginUserStateMachine , которая передается в when .
- Каждый раз при обработке нового состояния проверяется есть ли ошибка.
- Перед вызовом следующей suspend функции ( logUserIn ), LoginUserStateMachine обновляет переменную label .
- Когда внутри машины состояний вызывается другая suspend функция, экземпляр Continuation (с типом LoginUserStateMachine ) передается как аргумент. Вложенная suspend функция также была преобразована компилятором со своей машиной состояний. Когда эта внутренняя машина состояний завершит свою работу, она возобновит выполнение “родительской” машины состояний.
Последнее состояние должно возобновить выполнение completion через вызов continuation.cont.resume (очевидно что входной аргумент completion , сохраняется в переменной continuation.cont экземпляра LoginUserStateMachine ):
fun loginUser(userId: String?, password: String?, completion: Continuation) < /* . */ val continuation = completion as? LoginUserStateMachine ?: LoginUserStateMachine(completion) when(continuation.label) < /* . */ 2 -> < // Checks for failures throwOnFailure(continuation.result) // Gets the result of the previous state continuation.userDb = continuation.result as UserDb // Resumes the execution of the function that called this one continuation.cont.resume(continuation.userDb) >else -> throw IllegalStateException(/* . */) > >
Компилятор Kotlin делает много работы “под капотом”. Из suspend функции:
suspend fun loginUser(userId: String, password: String): User
Генерируется большой кусок кода:
fun loginUser(userId: String?, password: String?, completion: Continuation) < class LoginUserStateMachine( // completion parameter is the callback to the function that called loginUser completion: Continuation): CoroutineImpl(completion) < // objects to store across the suspend function var user: User? = null var userDb: UserDb? = null // Common objects for all CoroutineImpl var result: Any? = null var label: Int = 0 // this function calls the loginUser again to trigger the // state machine (label will be already in the next state) and // result will be the result of the previous state's computation override fun invokeSuspend(result: Any?) < this.result = result loginUser(null, null, this) >> val continuation = completion as? LoginUserStateMachine ?: LoginUserStateMachine(completion) when(continuation.label) < 0 -> < // Checks for failures throwOnFailure(continuation.result) // Next time this continuation is called, it should go to state 1 continuation.label = 1 // The continuation object is passed to logUserIn to resume // this state machine's execution when it finishes userRemoteDataSource.logUserIn(userId. password. continuation) >1 -> < // Checks for failures throwOnFailure(continuation.result) // Gets the result of the previous state continuation.user = continuation.result as User // Next time this continuation is called, it should go to state 2 continuation.label = 2 // The continuation object is passed to logUserIn to resume // this state machine's execution when it finishes userLocalDataSource.logUserIn(continuation.user, continuation) >2 -> < // Checks for failures throwOnFailure(continuation.result) // Gets the result of the previous state continuation.userDb = continuation.result as UserDb // Resumes the execution of the function that called this one continuation.cont.resume(continuation.userDb) >else -> throw IllegalStateException(/* . */) > >
Компилятор Kotlin преобразовывает каждую suspend функцию в машину состояний, с использованием обратных вызовов.
Зная как компилятор работает “под капотом”, вы лучше понимаете:
- почему suspend функция не вернет результат пока не завершится вся работа, которая она начала;
- каким образом код приостанавливается не блокируя потоки (вся информация, о том что нужно выполнить при возобновлении работы, хранится в объекте Continuation ).
Урок 3. Корутины. Suspend функции
В этом уроке подробно разберем как создать suspend функции. Также рассмотрим, можно ли блокировать поток, как корутина может потеряться и зачем нужно слово suspend.
В прошлом уроке мы подробно рассмотрели взаимодействие Continuation и suspend функции со стороны Continuation. Теперь посмотрим на это взаимодействие со стороны suspend функций.
Как создать suspend функцию
Сейчас мы будем рассматривать, как создать suspend функцию из асинхронного кода. Если же у вас есть какой-то синхронный метод и его надо сделать suspend, то там просто используется билдер withContext. Об этом мы поговорим, когда начнем тему билдеров.
Давайте создадим suspend функцию download, которую мы использовали ранее в примерах.
suspend fun download(url: String): File
Ключевое слово здесь — suspend. Это даст Котлину понять, что это suspend функция и она будет приостанавливать корутину. В конце урока я дам более подробное объяснение, зачем нужно использовать слово suspend. Но перед этим нам нужно понять, как такая функция работает.
Предположим, у нас есть некий NetworkService, который асинхронно умеет загружать файлы. И мы хотим обернуть его в suspend функцию download:
suspend fun download(url: String): File < networkService.download(url, object: NetworkService.Callback < override fun onSuccess(result: File) < >>) >
В колбэк onSuccess придет загруженный файл.
Как вы помните из прошлого урока, suspend функция должна результаты своей работы передать в Continuation.invokeSuspend. Для этого используется метод Continuation.resume
suspend fun download(url: String): File < networkService.download(url, object: NetworkService.Callback < override fun onSuccess(result: File) < continuation.resume(result) >>) >
Метод resume содержит определенную логику, часть которой — это вызов метода invokeSuspend с передачей туда результата.
Осталось где-то взять continuation. Для этого мы используем функцию suspendCoroutine.
suspend fun download(url: String): File < return suspendCoroutine < continuation ->networkService.download(url, object: NetworkService.Callback < override fun onSuccess(result: File) < continuation.resume(result) >>) > >
Весь наш код уходит в блок suspendCoroutine. Этот блок дает нам доступ к continuation, в который мы передаем результат работы networkService.download.
Получается, что suspend функция выполнила асинхронную работу и результат передала в continuation.resume, а тот уже передаст его в continuation.invokeSuspend.
Возврат ошибки
Кроме успешного результата, continuation может принять данные об ошибке.
Пусть у NetworkService.Callback есть метод onFailure. Он будет вызван, если при загрузке произошла ошибка. В нем мы вызовем метод continuation.resumeWithException и передадим туда исключение.
suspend fun download(url: String): File < return suspendCoroutine < continuation ->networkService.download(url, object: NetworkService.Callback < override fun onSuccess(result: File) < continuation.resume(result) >override fun onFailure(error: Exception) < continuation.resumeWithException(error) >>) > >
В этом случае ошибка не пойдет в метод invokeSuspend, а будет обработана корутиной. Т.е. в этом случае continuation не продолжит свое выполнение и код, который в корутине находится после suspend функции не будет выполнен.
Простая suspend функция download готова. Ее можно вызывать в корутине, она не будет блокировать поток, но приостановит код.
Можно ли сразу вернуть результат
Я все время упоминаю, что suspend функция должна сначала выполнить свою работу, а потом вернуть результат. Но возможен и другой вариант в случае, если результат уже готов на момент вызова. В примере с функцией загрузки файла — это может быть использование кэша. suspend функция сначала проверяет кэш. Если файл уже есть в кэше, то функция сразу может вернуть его. Для этого ей надо просто сразу вызвать continuation.resume. А если файла в кэше нет, то действуем по старой схеме — стартуем асинхронную работу и по ее завершению вызываем continuation.resume.
Давайте посмотрим как в Kotlin добавить в suspend функцию такую возможность:
suspend fun download(url: String): File < return suspendCoroutine < continuation ->val file = getFileFromCache(url) if (file != null) < continuation.resume(file) >else < networkService.download(url, object : NetworkService.Callback < override fun onSuccess(result: File) < continuation.resume(result) >override fun onFailure(error: Exception) < continuation.resumeWithException(error) >>) > > >
Мы пытаемся получить файл из кэша. Если он там есть, то сразу передаем его в continuation, иначе запускаем асинхронную работу.
Блокирование потока
Когда мы создаем suspend функцию, мы должны позаботиться о том, чтобы она выполнялась асинхронно в другом потоке или вернула результат сразу. Если же мы напишем в suspend функции код, блокирующий поток, то слово suspend нам тут никак не поможет. Такая suspend функция просто заблокирует поток, в котором выполняется корутина.
Давайте рассмотрим пример некорректной suspend функции. Представим, что используемый в примере выше NetworkService теперь работает синхронно. Его метод download не уходит в фоновый поток и не просит колбэк, а загружает файл в текущем потоке и возвращает как результат вызова метода. Т.е. метод networkService.download блокирует поток, в котором он вызван. Если мы используем его напрямую в suspend функции, то получится неправильная suspend функция:
// wrong suspend function, don't do that! suspend fun download(url: String): File < return suspendCoroutine < continuation ->val file = networkService.download(url) continuation.resume(file) > >
Важно понимать, что ни метод suspendCoroutine, ни слово suspend в описании функции не cделают наш код асинхронным. Метод networkService.download будет выполнен в потоке корутины и заблокирует его. Поэтому от нас требуется обеспечить асинхронность кода в suspend функции, а в качестве колбэка использовать continuation.
О том, как в корутине выполнять синхронный код так, чтобы не блокировать поток корутины, мы поговорим позже, когда будем рассматривать билдеры.
Потерянная корутина
Что будет если в suspend функции не вызвать continuation.resume? Это очень важный момент! Если мы не вызовем ни один из методов continuation.resume*, то корутина просто не продолжит выполняться. Никогда. Потому что она ждет, что suspend функция продолжит ее через вызов Continuation. Поэтому тут будьте аккуратны, не теряйте вызов Continuation — передайте ему результат или ошибку, чтобы корутина могла продолжиться или завершиться.
Зачем нужно слово suspend?
Теперь мы знаем достаточно про suspend функции, чтобы поговорить о значении слова suspend. Само по себе это слово не добавляет к функции никакой магии. Оно не сделает так, чтобы синхронный код вдруг перестал блокировать поток. Оно вообще ничего не делает. Это просто маркер того, что данная функция умеет (и должна) работать с Continuation, чтобы приостановить выполнение корутины не блокируя поток.
Давайте взглянем на это с двух точек зрения: создание suspend функции и ее использование.
Создание
Чтобы suspend функция могла приостановить код не блокируя поток, ей нужен Continuation, выполнение которого она возобновит по завершению своей работы. Чтобы получить Continuation, используется функция suspendCoroutine. Возникает вопрос, почему любая другая функция без слова suspend не может этого сделать? Давайте допустим, что мы можем создать обычную (не suspend) функцию и вызвать в ней suspendCoroutine, который предоставит нам Continuation. Если мы вызовем такую функцию в корутине, то все отработает нормально, потому что у корутины есть Continuation. Его мы и получим. Но если мы вызовем ее вне корутины, то suspendCoroutine не сможет предоставить нам Continuation, потому что его просто нет.
Получается, что функция, в которой мы хотим получить доступ к Continuation, должна запускаться только в корутине. В обычном коде ее запускать нельзя. И вот именно для реализации этого ограничения и используется слово suspend. Мы не сможем запустить suspend функцию вне корутины, компилятор выдаст ошибку. Потому что компилятор знает, что suspend функции нужен будет Continuation, который есть только в корутине. Таким образом, когда мы создаем функцию и добавляем к ней слово suspend — мы можем быть уверены, что она будет запущена в корутине. А значит мы сможем добраться до Continuation.
Использование
Если мы в корутине собираемся использовать долго работающую функцию, которая помечена как suspend, то мы точно знаем, что эта функция приостановит код и не заблокирует при этом поток, в котором выполняется корутина. Если конечно, она написана корректно)
А обычная функция, которая не является suspend, не сможет такого сделать. Она либо заблокирует поток корутины, либо попросит дать ей колбэк.
Что происходит внутри suspendCoroutine?
Об этом я подробно расскажу в пятом уроке.
Присоединяйтесь к нам в Telegram:
— в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.
— в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Compose, Kotlin, RxJava, Dagger, Тестирование, Performance
— ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня
Suspend-функция
Давайте поговорим об одном из компонентов составляющих понятия Корутины Suspend-функции. Помните мы говорили о том что Корутины не блокируют поток, а приостанавливают работу кода. Ну так вот, такое свойство обеспечивает Suspend-функция. То есть, код который вписан в тело Suspend-функции при запуске не заблокирует поток, но этот код приостановит свою работу. Это означает что мы можем даже запустить Корутину в Main-потоке. Давайте приведем пример:
val url = "http://myurl.com/file.pdf" // long synchronous function getFile(url) println("File is downloaded")
Простой пример, в котором пытаемся загрузить файл по определенному URL. Здесь функция getFile() синхронная, то есть блокирует поток. Но чтобы запускать функцию из основного потока нам нужно будет сделать ее асинхронной и прокинуть в нее лямбду-коллбек с функцией println(), который будет выполнен после загрузки файла:
getFile(url)
Из выше сказанного следует что, при запуске массивного кода(с тяжелыми вычислениями) в основном потоке нам придется либо блокировать основной поток либо писать колбэк(и плодить колбэкхэлл)
Но благодаря Корутинам и саспенд-функции у нас возможность написать код в которой тяжелая функция не блокирует поток но и код который стоит после этой функции выполнялся после завершения тяжелой функции без коллбэков. Давайте посмотрим как это сделать в переписанном примере:
CoroutineScope(Job()).launch < val url = "http://myurl.com/file.pdf" // long synchronous function suspendedGetFile(url) println("File is downloaded") >
Тут launch — это Builders, который создает Корутину. В ее теле появилась Suspend-функция suspendedGetFile(url). Помните? Builder, Suspend-функция все это части Корутины. Эту конструкцию мы можем назвать Корутиной. Ну так вот, эта Корутина не блокирует поток и ее можно запустить в основном потоке. Suspend-функция suspendedGetFile() запустит загрузку файла в рабочем потоке, а функция println() отработает после завершения функции suspendedGetFile().
+ Suspend-функция возвращает свой ответ асинхронно поэтому ее нельзя вызвать из обычной функции
+ Suspend-функция может выполнять код в разных потоках. Нужно помнить про возможный рассинхрон, так как нет гарантии что исполнение и возврат ответа будет на одном потоке
Пусть это и выглядит как фантастика, но помните что Котлин-код далее будет трансформирован в Java-классы. И в процессе этих преобразований наша Корутина будет трансформирована и будет использован механизм Continuation. Все наши компоненты Корутины «под капотом» реализуют колбэк, который выполнит println() после загрузки suspendedGetFile().
IBM провела симуляцию нейрокомпьютера, сопоставимого с мозгом человека
На конференции Supercomputing 2012 компания IBM представила результаты работы нейросимулятора Compass на суперкомпьютере Sequoia (второе место в мировом рейтинге суперкомпьютеров). Впервые был достигнут масштаб, соответствующий человеческому мозгу — 530 миллиардов нейронов и 137 триллионов синапсов. Симуляция происходила в 1542 раза медленнее реального времени. В ней были задействованы все 1 572 864 ядер и полтора петабайта памяти.

Compass — система симуляции новой компьютерной архитектуры TrueNorth, которую в IBM называют «старшим братом Watson». В отличие от привычных нам компьютеров, основанных на архитектуре фон Неймана, TrueNorth построен по образу и подобию мозга, на базе разработанных IBM «нейросемантических ядер» — чипов, содержащих кремниевые нейроны и синапсы. Каждый чип содержит 256 нейронов, 1024 аксона и 256х1024 синапса. Площадь чипа, произведённого по 45-нанометровой технологии — 4.5 мм 2 . В рекордной симуляции была смоделирована работа 2084 миллиардов таких ядер.
Эта работа была проделана в рамках программы DARPA SyNAPSE, конечная цель которой — создать нейрокомпьютер, сравнимый с мозгом высших млекопитающих как по быстродействию и когнитивным способностям, так и по компактности и энергоёмкости. То есть фактически речь идёт о создании мозга для «терминаторов». Вот некоторые его характеристики:
- 10 миллиардов нейронов
- 100 триллионов синапсов
- энергопотребление в пределах 1 кВт
- занимаемый объём — меньше 2 дм 3
- интерфейсы для сенсорного восприятия и моторных функций
IBM работает над созданием «когнитивного компьютера» для DARPA уже несколько лет. Так, в 2009 году были представлены результаты нейорсимуляции в масштабах мозга кошки. Пока рано говорить о реалистичной имитации человеческого мозга, ведь кроме достаточного количества нейронов необходимо ещё знать схему их соединения и, самое главное, программу, которая управляет работой мозга. В этом направлении тоже есть прогресс. Одна из симуляций в рамках DARPA SyNAPSE была основана на схеме соединений нейронных комплексов в мозгу макаки, полученной в рамках проекта CoCoMac.
- TrueNorth
- SyNAPSE
- суперкомпьютер
- моделирование мозга
- нейробиология
- нейронные сети